/* Zutils - Utilities dealing with compressed files Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__MSVCRT__) || defined(__OS2__) #include #endif #include "arg_parser.h" #include "zutils.h" #if CHAR_BIT != 8 #error "Environments where CHAR_BIT != 8 are not supported." #endif namespace { #ifdef O_BINARY const int o_binary = O_BINARY; #else const int o_binary = 0; #endif enum Mode { m_none, m_zcat, m_zgrep, m_ztest }; void show_help() { std::printf( "Zutils is a collection of utilities able to deal with any combination of\n" "compressed and non-compressed files transparently. If any given file,\n" "including standard input, is compressed, its uncompressed content is used.\n" "\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nUsage: %s [options] [files]\n", invocation_name ); std::printf( "\nTry '%s --help' for more specific help.\n", invocation_name ); std::printf( "\nOperations:\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" " --zcat zcat operation\n" " --zgrep zgrep operation\n" " --ztest ztest operation\n" ); show_help_addr(); } int simple_extension_index( const std::string & name ) { for( int i = 0; i < num_formats; ++i ) { const std::string ext( simple_extensions[i] ); if( name.size() > ext.size() && name.compare( name.size() - ext.size(), ext.size(), ext ) == 0 ) return i; } return -1; } int open_instream( std::string & input_filename, const Mode program_mode, const bool search ) { int infd = open( input_filename.c_str(), O_RDONLY | o_binary ); if( infd < 0 ) { if( search && ( program_mode == m_zcat || program_mode == m_zgrep ) && simple_extension_index( input_filename ) < 0 ) { for( int i = 0; i < num_formats; ++i ) { const std::string name( input_filename + simple_extensions[format_order[i]] ); infd = open( name.c_str(), O_RDONLY | o_binary ); if( infd >= 0 ) { input_filename = name; break; } } } if( infd < 0 ) show_error2( "Can't open input file", input_filename.c_str() ); } return infd; } #include "zcat.cc" #include "zgrep.cc" #include "ztest.cc" } // end namespace int main( const int argc, const char * const argv[] ) { enum { format_opt = 256, help_opt, verbose_opt, zcat_opt, zgrep_opt, ztest_opt }; const Arg_parser::Option * options = 0; int infd = -1; int format_type = -1; Mode program_mode = m_none; bool recursive = false; std::string input_filename; std::list< std::string > filenames; Cat_options cat_options; std::vector< const char * > grep_args; // args to grep, maybe empty std::vector< const char * > ztest_args; // args to ztest, maybe empty invocation_name = argv[0]; const Arg_parser::Option m_zcat_options[] = { { 'A', "show-all", Arg_parser::no }, // cat { 'b', "number-nonblank", Arg_parser::no }, // cat { 'c', "stdout", Arg_parser::no }, // gzip { 'd', "decompress", Arg_parser::no }, // gzip { 'e', 0, Arg_parser::no }, // cat { 'E', "show-ends", Arg_parser::no }, // cat { 'f', "force", Arg_parser::no }, // gzip { 'h', "help", Arg_parser::no }, { 'l', "list", Arg_parser::no }, // gzip { 'L', "license", Arg_parser::no }, // gzip { 'n', "number", Arg_parser::no }, // cat { 'q', "quiet", Arg_parser::no }, { 'r', "recursive", Arg_parser::no }, { 's', "squeeze-blank", Arg_parser::no }, // cat { 't', 0, Arg_parser::no }, // cat { 'T', "show-tabs", Arg_parser::no }, // cat { 'v', "show-nonprinting", Arg_parser::no }, // cat { 'V', "version", Arg_parser::no }, { format_opt, "format", Arg_parser::yes }, { verbose_opt, "verbose", Arg_parser::no }, { zcat_opt, "zcat", Arg_parser::no }, { 0 , 0, Arg_parser::no } }; const Arg_parser::Option m_zgrep_options[] = { { 'a', "text", Arg_parser::no }, // grep GNU { 'A', "after-context", Arg_parser::yes }, // grep GNU { 'b', "byte-offset", Arg_parser::no }, // grep GNU { 'B', "before-context", Arg_parser::yes }, // grep GNU { 'c', "count", Arg_parser::no }, // grep { 'C', "context", Arg_parser::yes }, // grep GNU { 'e', "regexp", Arg_parser::yes }, // grep { 'E', "extended-regexp", Arg_parser::no }, // grep { 'f', "file ", Arg_parser::yes }, // grep { 'F', "fixed-strings", Arg_parser::no }, // grep { 'h', "no-filename", Arg_parser::no }, // grep GNU { 'H', "with-filename", Arg_parser::no }, // grep GNU { 'i', "ignore-case", Arg_parser::no }, // grep { 'I', 0, Arg_parser::no }, // grep GNU { 'l', "files-with-matches", Arg_parser::no }, // grep { 'L', "files-without-match", Arg_parser::no }, // grep GNU { 'm', "max-count", Arg_parser::yes }, // grep GNU { 'n', "line-number", Arg_parser::no }, // grep { 'o', "only-matching", Arg_parser::no }, // grep { 'q', "quiet", Arg_parser::no }, { 'r', "recursive", Arg_parser::no }, { 's', "no-messages", Arg_parser::no }, // grep { 'v', "invert-match", Arg_parser::no }, // grep { 'V', "version", Arg_parser::no }, { 'w', "word-regexp", Arg_parser::no }, // grep GNU { 'x', "line-regexp", Arg_parser::no }, // grep { format_opt, "format", Arg_parser::yes }, { help_opt, "help", Arg_parser::no }, { verbose_opt, "verbose", Arg_parser::no }, { zgrep_opt, "zgrep", Arg_parser::no }, { 0 , 0, Arg_parser::no } }; const Arg_parser::Option m_ztest_options[] = { { 'h', "help", Arg_parser::no }, { 'q', "quiet", Arg_parser::no }, { 'r', "recursive", Arg_parser::no }, { 'v', "verbose", Arg_parser::no }, { 'V', "version", Arg_parser::no }, { format_opt, "format", Arg_parser::yes }, { ztest_opt, "ztest", Arg_parser::no }, { 0 , 0, Arg_parser::no } }; { // parse operation const Arg_parser::Option operations[] = { { 'h', "help", Arg_parser::no }, { 'V', "version", Arg_parser::no }, { zcat_opt, "zcat", Arg_parser::no }, { zgrep_opt, "zgrep", Arg_parser::no }, { ztest_opt, "ztest", Arg_parser::no }, { 0 , 0, Arg_parser::no } }; const Arg_parser parser( argv[1], ( argc > 2 ) ? argv[2] : 0, operations ); if( parser.error().size() ) // bad operation { show_error( parser.error().c_str(), 0, true ); return 1; } if( parser.arguments() > 0 ) { switch( parser.code( 0 ) ) { case 0 : break; case 'h': show_help(); return 0; case 'V': show_version(); return 0; case zcat_opt : program_mode = m_zcat; options = m_zcat_options; util_name = "zcat"; break; case zgrep_opt : program_mode = m_zgrep; options = m_zgrep_options; util_name = "zgrep"; break; case ztest_opt : program_mode = m_ztest; options = m_ztest_options; util_name = "ztest"; break; default : internal_error( "uncaught option" ); } } #if defined(__MSVCRT__) || defined(__OS2__) setmode( STDIN_FILENO, O_BINARY ); setmode( STDOUT_FILENO, O_BINARY ); #endif if( program_mode == m_none ) { show_error( "You must specify the operation to be performed.", 0, true ); return 1; } } // end parse operation const Arg_parser parser( argc, argv, options ); if( parser.error().size() ) // bad option { show_error( parser.error().c_str(), 0, true ); return ( program_mode == m_zcat || program_mode == m_ztest ) ? 1 : 2; } int argind = 0; int grep_show_name = -1; bool grep_list = false; bool grep_pattern_found = false; for( ; argind < parser.arguments(); ++argind ) { const int code = parser.code( argind ); const char * arg = parser.argument( argind ).c_str(); if( !code ) { if( program_mode == m_zgrep && !grep_pattern_found ) { grep_args.push_back( arg ); grep_pattern_found = true; continue; } else break; // no more options } if( code == format_opt ) { format_type = get_format_type( arg ); continue; } switch( program_mode ) { case m_none: internal_error( "invalid operation" ); break; case m_zcat: switch( code ) { case 'A': cat_options.show_ends = true; cat_options.show_nonprinting = true; cat_options.show_tabs = true; break; case 'b': cat_options.number_lines = 1; break; case 'c': break; case 'd': break; case 'e': cat_options.show_nonprinting = true; // fall through case 'E': cat_options.show_ends = true; break; case 'f': break; case 'h': show_zcat_help(); return 0; case 'l': break; case 'L': break; case 'n': if( cat_options.number_lines == 0 ) { cat_options.number_lines = 2; } break; case 'q': verbosity = -1; break; case 'r': recursive = true; break; case 's': cat_options.squeeze_blank = true; break; case 't': cat_options.show_nonprinting = true; // fall through case 'T': cat_options.show_tabs = true; break; case 'v': cat_options.show_nonprinting = true; break; case 'V': show_version( "Zcat" ); return 0; case verbose_opt : if( verbosity < 4 ) ++verbosity; break; case zcat_opt : break; default : internal_error( "uncaught option" ); } break; case m_zgrep: switch( code ) { case 'a': grep_args.push_back( "-a" ); break; case 'A': grep_args.push_back( "-A" ); grep_args.push_back( arg ); break; case 'b': grep_args.push_back( "-b" ); break; case 'B': grep_args.push_back( "-B" ); grep_args.push_back( arg ); break; case 'c': grep_args.push_back( "-c" ); break; case 'C': grep_args.push_back( "-C" ); grep_args.push_back( arg ); break; case 'e': grep_args.push_back( "-e" ); grep_args.push_back( arg ); grep_pattern_found = true; break; case 'E': grep_args.push_back( "-E" ); break; case 'f': grep_args.push_back( "-f" ); grep_args.push_back( arg ); grep_pattern_found = true; break; case 'F': grep_args.push_back( "-F" ); break; case 'h': grep_show_name = false; break; case 'H': grep_show_name = true; break; case 'i': grep_args.push_back( "-i" ); break; case 'I': grep_args.push_back( "-I" ); break; case 'l': grep_args.push_back( "-l" ); grep_list = true; break; case 'L': grep_args.push_back( "-L" ); grep_list = true; break; case 'm': grep_args.push_back( "-m" ); grep_args.push_back( arg ); break; case 'n': grep_args.push_back( "-n" ); break; case 'o': grep_args.push_back( "-o" ); break; case 'q': grep_args.push_back( "-q" ); verbosity = -1; break; case 'r': recursive = true; break; case 's': grep_args.push_back( "-s" ); verbosity = -1; break; case 'v': grep_args.push_back( "-v" ); break; case 'V': show_version( "Zgrep" ); return 0; case 'w': grep_args.push_back( "-w" ); break; case 'x': grep_args.push_back( "-x" ); break; case help_opt : show_zgrep_help(); return 0; case verbose_opt : if( verbosity < 4 ) ++verbosity; break; case zgrep_opt : break; default : internal_error( "uncaught option" ); } break; case m_ztest: switch( code ) { case 'h': show_ztest_help(); return 0; case 'q': verbosity = -1; ztest_args.push_back( "-q" ); break; case 'r': recursive = true; break; case 'v': if( verbosity < 4 ) ++verbosity; ztest_args.push_back( "-v" ); break; case 'V': show_version( "Ztest" ); return 0; case ztest_opt : break; default : internal_error( "uncaught option" ); } break; } } // end process options if( program_mode == m_zgrep && !grep_pattern_found ) { show_error( "Pattern not found." ); return 2; } for( ; argind < parser.arguments(); ++argind ) filenames.push_back( parser.argument( argind ) ); if( filenames.empty() ) filenames.push_back("-"); if( grep_show_name < 0 ) grep_show_name = ( filenames.size() != 1 || recursive ); int retval = ( ( program_mode == m_zgrep ) ? 1 : 0 ); while( !filenames.empty() ) { input_filename = filenames.front(); filenames.pop_front(); if( !input_filename.size() || input_filename == "-" ) { input_filename.clear(); infd = STDIN_FILENO; } else { if( recursive ) { struct stat st; if( !stat( input_filename.c_str(), &st ) && S_ISDIR( st.st_mode ) ) { DIR * const dirp = opendir( input_filename.c_str() ); if( !dirp ) { show_error2( "Can't open directory", input_filename.c_str() ); if( retval < 1 ) retval = 1; continue; } std::list< std::string > tmp_list; while( true ) { const struct dirent * const entryp = readdir( dirp ); if( !entryp ) { closedir( dirp ); break; } std::string tmp_name( entryp->d_name ); if( tmp_name != "." && tmp_name != ".." ) tmp_list.push_back( input_filename + "/" + tmp_name ); } filenames.splice( filenames.begin(), tmp_list ); continue; } } infd = open_instream( input_filename, program_mode, format_type < 0 ); if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } } int tmp = 0; switch( program_mode ) { case m_none: break; case m_zcat: tmp = cat( infd, format_type, input_filename, cat_options ); break; case m_zgrep: if( infd == STDIN_FILENO ) tmp = zgrep_stdin( infd, format_type, grep_args ); else tmp = zgrep_file( infd, format_type, input_filename, grep_args, grep_list, grep_show_name ); break; case m_ztest: if( infd == STDIN_FILENO ) tmp = ztest_stdin( infd, format_type, ztest_args ); else tmp = ztest_file( infd, format_type, input_filename, ztest_args ); break; } if( program_mode == m_zgrep ) { if( tmp == 0 || ( tmp == 2 && retval == 1 ) ) retval = tmp; } else if( tmp > retval ) retval = tmp; if( input_filename.size() ) { close( infd ); infd = -1; } } if( std::fclose( stdout ) != 0 ) { show_error( "Can't close stdout", errno ); switch( program_mode ) { case m_none: break; case m_zcat: retval = 1; break; case m_zgrep: if( retval != 0 || verbosity >= 0 ) retval = 2; break; case m_ztest: if( retval == 0 ) retval = 1; break; } } return retval; }