"Fossies" - the Fresh Open Source Software Archive

Member "zutils-1.10/zdiff.cc" (5 Jan 2021, 17242 Bytes) of package /linux/privat/zutils-1.10.tar.lz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "zdiff.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes reports: 1.10-rc1_vs_1.10 or 1.9_vs_1.10.

    1 /* Zdiff - decompress and compare two files line by line
    2    Copyright (C) 2010-2021 Antonio Diaz Diaz.
    3 
    4    This program is free software: you can redistribute it and/or modify
    5    it under the terms of the GNU General Public License as published by
    6    the Free Software Foundation, either version 2 of the License, or
    7    (at your option) any later version.
    8 
    9    This program is distributed in the hope that it will be useful,
   10    but WITHOUT ANY WARRANTY; without even the implied warranty of
   11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12    GNU General Public License for more details.
   13 
   14    You should have received a copy of the GNU General Public License
   15    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   16 */
   17 
   18 #define _FILE_OFFSET_BITS 64
   19 
   20 #include <algorithm>
   21 #include <cctype>
   22 #include <cerrno>
   23 #include <climits>
   24 #include <csignal>
   25 #include <cstdio>
   26 #include <cstdlib>
   27 #include <cstring>
   28 #include <string>
   29 #include <vector>
   30 #include <fcntl.h>
   31 #include <stdint.h>
   32 #include <unistd.h>
   33 #include <sys/stat.h>
   34 #if defined(__MSVCRT__) || defined(__OS2__)
   35 #include <io.h>
   36 #endif
   37 
   38 #include "arg_parser.h"
   39 #include "rc.h"
   40 #include "zutils.h"
   41 
   42 // 'verbosity' is always 0 in zdiff; no --verbose or --quiet available.
   43 
   44 namespace {
   45 
   46 std::string fifonames[2];   // names of the two fifos passed to diff
   47 
   48 #include "zcmpdiff.cc"
   49 
   50 void show_help()
   51   {
   52   std::printf( "zdiff compares two files and, if they differ, writes to standard output the\n"
   53                "differences line by line. A hyphen '-' used as a file argument means standard\n"
   54                "input. If any file given is compressed, its decompressed content is used.\n"
   55                "zdiff is a front end to the program diff and has the limitation that messages\n"
   56                "from diff refer to temporary file names instead of those specified.\n"
   57                "\nThe formats supported are bzip2, gzip, lzip, and xz.\n"
   58                "\nUsage: zdiff [options] file1 [file2]\n"
   59                "\nzdiff compares file1 to file2. The standard input is used only if file1 or\n"
   60                "file2 refers to standard input. If file2 is omitted zdiff tries the\n"
   61                "following:\n"
   62                "\n  - If file1 is compressed, compares its decompressed contents with\n"
   63                "  the corresponding uncompressed file (the name of file1 with the\n"
   64                "  extension removed).\n"
   65                "\n  - If file1 is uncompressed, compares it with the decompressed\n"
   66                "  contents of file1.[lz|bz2|gz|xz] (the first one that is found).\n"
   67                "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n"
   68                "Some options only work if the diff program used supports them.\n"
   69                "\nOptions:\n"
   70                "  -h, --help                        display this help and exit\n"
   71                "  -V, --version                     output version information and exit\n"
   72                "  -a, --text                        treat all files as text\n"
   73                "  -b, --ignore-space-change         ignore changes in the amount of white space\n"
   74                "  -B, --ignore-blank-lines          ignore changes whose lines are all blank\n"
   75                "  -c                                use the context output format\n"
   76                "  -C, --context=<n>                 same as -c but use <n> lines of context\n"
   77                "  -d, --minimal                     try hard to find a smaller set of changes\n"
   78                "  -E, --ignore-tab-expansion        ignore changes due to tab expansion\n"
   79                "  -i, --ignore-case                 ignore case differences in file contents\n"
   80                "  -M, --format=<list>               process only the formats in <list>\n"
   81                "  -N, --no-rcfile                   don't read runtime configuration file\n"
   82                "  -O, --force-format=[<f1>][,<f2>]  force the formats given (bz2, gz, lz, xz)\n"
   83                "  -p, --show-c-function             show which C function each change is in\n"
   84                "  -q, --brief                       output only whether files differ\n"
   85                "  -s, --report-identical-files      report when two files are identical\n"
   86                "  -t, --expand-tabs                 expand tabs to spaces in output\n"
   87                "  -T, --initial-tab                 make tabs line up by prepending a tab\n"
   88                "  -u                                use the unified output format\n"
   89                "  -U, --unified=<n>                 same as -u but use <n> lines of context\n"
   90                "  -w, --ignore-all-space            ignore all white space\n"
   91                "  -W, --width=<n>                   output at most <n> print columns\n"
   92                "  -y, --side-by-side                output in two columns\n"
   93                "      --bz2=<command>               set compressor and options for bzip2 format\n"
   94                "      --gz=<command>                set compressor and options for gzip format\n"
   95                "      --lz=<command>                set compressor and options for lzip format\n"
   96                "      --xz=<command>                set compressor and options for xz format\n" );
   97   show_help_addr();
   98   }
   99 
  100 
  101 const char * my_basename( const char * filename )
  102   {
  103   const char * c = filename;
  104   while( *c ) { if( *c == '/' ) { filename = c + 1; } ++c; }
  105   return filename;
  106   }
  107 
  108 
  109 extern "C" void remove_fifos()
  110   {
  111   if( fifonames[0].size() )
  112     { std::remove( fifonames[0].c_str() ); fifonames[0].clear(); }
  113   if( fifonames[1].size() )
  114     { std::remove( fifonames[1].c_str() ); fifonames[1].clear(); }
  115   }
  116 
  117 
  118 /* Set fifonames[i] to "${TMPDIR}/<coded_pid>[_-]<basename(filenames[i])>"
  119    and create FIFOs. The pid is coded in little endian order.
  120 */
  121 bool set_fifonames( const std::string filenames[2] )
  122   {
  123   enum { num_codes = 36 };
  124   const char * const codes = "0123456789abcdefghijklmnopqrstuvwxyz";
  125   const char * p = std::getenv( "TMPDIR" );
  126 
  127   if( p ) { fifonames[0] = p; fifonames[0] += '/'; }
  128   else fifonames[0] = "/tmp/";
  129   int n = getpid();
  130   do fifonames[0] += codes[n % num_codes]; while( n /= num_codes );
  131   const unsigned pos = fifonames[0].size();
  132   fifonames[0] += '_';
  133   fifonames[1] = fifonames[0];
  134   fifonames[0] += my_basename( filenames[0].c_str() );
  135   fifonames[1] += my_basename( filenames[1].c_str() );
  136   if( fifonames[1] == fifonames[0] ) fifonames[1][pos] = '-';
  137 
  138   for( int i = 0; i < 2; ++i )
  139     if( mkfifo( fifonames[i].c_str(), S_IRUSR | S_IWUSR ) != 0 )
  140       {
  141       if( errno == EEXIST )
  142         {
  143         std::remove( fifonames[i].c_str() );
  144         if( mkfifo( fifonames[i].c_str(), S_IRUSR | S_IWUSR ) == 0 )
  145           continue;
  146         }
  147       show_file_error( fifonames[i].c_str(), "Can't create FIFO", errno );
  148       return false;
  149       }
  150   return true;
  151   }
  152 
  153 
  154 bool set_data_feeder( const std::string & filename,
  155                       const std::string & fifoname, const int infd,
  156                       Children & children, int format_index )
  157   {
  158   uint8_t magic_data[magic_buf_size];
  159   int magic_size = 0;
  160   if( format_index < 0 )
  161     format_index = test_format( infd, magic_data, &magic_size );
  162   children.compressor_name = get_compressor_name( format_index );
  163 
  164   if( children.compressor_name )    // compressed
  165     {
  166     int fda[2];             // pipe from feeder to compressor
  167     if( pipe( fda ) < 0 )
  168       { show_error( "Can't create pipe", errno ); return false; }
  169     const pid_t pid = fork();
  170     if( pid == 0 )          // child 1 (compressor feeder)
  171       {
  172       if( close( fda[0] ) != 0 ||
  173           !feed_data( filename, infd, fda[1], magic_data, magic_size ) )
  174         _exit( 2 );
  175       if( close( fda[1] ) != 0 )
  176         { show_close_error(); _exit( 2 ); }
  177       _exit( 0 );
  178       }
  179     if( pid < 0 )           // parent
  180       { show_fork_error( "data feeder" ); return false; }
  181 
  182     const pid_t pid2 = fork();
  183     if( pid2 == 0 )         // child 2 (compressor)
  184       {
  185       const int outfd = open( fifoname.c_str(), O_WRONLY | O_BINARY );
  186       if( outfd < 0 )
  187         {
  188         if( verbosity >= 0 )
  189           std::fprintf( stderr, "%s: Can't open FIFO '%s' for writing: %s\n",
  190                         program_name, fifoname.c_str(), std::strerror( errno ) );
  191         _exit( 2 );
  192         }
  193       if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
  194           dup2( outfd, STDOUT_FILENO ) >= 0 &&
  195           close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
  196           close( outfd ) == 0 )
  197         {
  198         const std::vector< std::string > & compressor_args =
  199           get_compressor_args( format_index );
  200         const int size = compressor_args.size();
  201         const char ** const argv = new const char *[size+3];
  202         argv[0] = children.compressor_name;
  203         for( int i = 0; i < size; ++i )
  204           argv[i+1] = compressor_args[i].c_str();
  205         argv[size+1] = ( verbosity >= 0 ) ? "-d" : "-dq";
  206         argv[size+2] = 0;
  207         execvp( argv[0], (char **)argv );
  208         }
  209       show_exec_error( children.compressor_name );
  210       _exit( 2 );
  211       }
  212     if( pid2 < 0 )          // parent
  213       { show_fork_error( children.compressor_name ); return false; }
  214 
  215     close( fda[0] ); close( fda[1] );
  216     children.pid[0] = pid;
  217     children.pid[1] = pid2;
  218     }
  219   else                  // uncompressed
  220     {
  221     const pid_t pid = fork();
  222     if( pid == 0 )          // child (feeder)
  223       {
  224       const int outfd = open( fifoname.c_str(), O_WRONLY | O_BINARY );
  225       if( outfd < 0 )
  226         {
  227         if( verbosity >= 0 )
  228           std::fprintf( stderr, "%s: Can't open FIFO '%s' for writing: %s\n",
  229                         program_name, fifoname.c_str(), std::strerror( errno ) );
  230         _exit( 2 );
  231         }
  232       if( !feed_data( filename, infd, outfd, magic_data, magic_size ) )
  233         _exit( 2 );
  234       if( close( outfd ) != 0 )
  235         { show_close_error(); _exit( 2 ); }
  236       _exit( 0 );
  237       }
  238     if( pid < 0 )           // parent
  239       { show_fork_error( "data feeder" ); return false; }
  240     children.pid[0] = pid;
  241     children.pid[1] = 0;
  242     }
  243   return true;
  244   }
  245 
  246 
  247 extern "C" void signal_handler( int sig )
  248   {
  249   remove_fifos();
  250   std::signal( sig, SIG_DFL );
  251   std::raise( sig );
  252   }
  253 
  254 
  255 void set_signals()
  256   {
  257   std::signal( SIGHUP, signal_handler );
  258   std::signal( SIGINT, signal_handler );
  259   std::signal( SIGTERM, signal_handler );
  260   }
  261 
  262 } // end namespace
  263 
  264 
  265 int main( const int argc, const char * const argv[] )
  266   {
  267   enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt };
  268   std::vector< const char * > diff_args;    // args to diff, maybe empty
  269   int format_types[2] = { -1, -1 };
  270   program_name = "zdiff";
  271   invocation_name = ( argc > 0 ) ? argv[0] : program_name;
  272 
  273   const Arg_parser::Option options[] =
  274     {
  275     { 'a', "text",                   Arg_parser::no  },
  276     { 'b', "ignore-space-change",    Arg_parser::no  },
  277     { 'B', "ignore-blank-lines",     Arg_parser::no  },
  278     { 'c',  0,                       Arg_parser::no  },
  279     { 'C', "context",                Arg_parser::yes },
  280     { 'd', "minimal",                Arg_parser::no  },
  281     { 'E', "ignore-tab-expansion",   Arg_parser::no  },
  282     { 'h', "help",                   Arg_parser::no  },
  283     { 'i', "ignore-case",            Arg_parser::no  },
  284     { 'M', "format",                 Arg_parser::yes },
  285     { 'N', "no-rcfile",              Arg_parser::no  },
  286     { 'O', "force-format",           Arg_parser::yes },
  287     { 'p', "show-c-function",        Arg_parser::no  },
  288     { 'q', "brief",                  Arg_parser::no  },
  289     { 's', "report-identical-files", Arg_parser::no  },
  290     { 't', "expand-tabs",            Arg_parser::no  },
  291     { 'T', "initial-tab",            Arg_parser::no  },
  292     { 'u',  0,                       Arg_parser::no  },
  293     { 'U', "unified",                Arg_parser::yes },
  294     { 'V', "version",                Arg_parser::no  },
  295     { 'w', "ignore-all-space",       Arg_parser::no  },
  296     { 'W', "width",                  Arg_parser::yes },
  297     { 'y', "side-by-side",           Arg_parser::no  },
  298     { bz2_opt,    "bz2",             Arg_parser::yes },
  299     { gz_opt,     "gz",              Arg_parser::yes },
  300     { lz_opt,     "lz",              Arg_parser::yes },
  301     { xz_opt,     "xz",              Arg_parser::yes },
  302     {  0 ,  0,                       Arg_parser::no  } };
  303 
  304   const Arg_parser parser( argc, argv, options );
  305   if( parser.error().size() )               // bad option
  306     { show_error( parser.error().c_str(), 0, true ); return 2; }
  307 
  308   maybe_process_config_file( parser );
  309 
  310   int argind = 0;
  311   for( ; argind < parser.arguments(); ++argind )
  312     {
  313     const int code = parser.code( argind );
  314     if( !code ) break;                  // no more options
  315     const std::string & arg = parser.argument( argind );
  316     switch( code )
  317       {
  318       case 'a': diff_args.push_back( "-a" ); break;
  319       case 'b': diff_args.push_back( "-b" ); break;
  320       case 'B': diff_args.push_back( "-B" ); break;
  321       case 'c': diff_args.push_back( "-c" ); break;
  322       case 'C': diff_args.push_back( "-C" );
  323                 diff_args.push_back( arg.c_str() ); break;
  324       case 'd': diff_args.push_back( "-d" ); break;
  325       case 'E': diff_args.push_back( "-E" ); break;
  326       case 'h': show_help(); return 0;
  327       case 'i': diff_args.push_back( "-i" ); break;
  328       case 'M': parse_format_list( arg ); break;
  329       case 'N': break;
  330       case 'O': parse_format_types2( arg, format_types ); break;
  331       case 'p': diff_args.push_back( "-p" ); break;
  332       case 'q': diff_args.push_back( "-q" ); break;
  333       case 's': diff_args.push_back( "-s" ); break;
  334       case 't': diff_args.push_back( "-t" ); break;
  335       case 'T': diff_args.push_back( "-T" ); break;
  336       case 'u': diff_args.push_back( "-u" ); break;
  337       case 'U': diff_args.push_back( "-U" );
  338                 diff_args.push_back( arg.c_str() ); break;
  339       case 'V': show_version(); return 0;
  340       case 'w': diff_args.push_back( "-w" ); break;
  341       case 'W': diff_args.push_back( "-W" );
  342                 diff_args.push_back( arg.c_str() ); break;
  343       case 'y': diff_args.push_back( "-y" ); break;
  344       case bz2_opt: parse_compressor( arg, fmt_bz2 ); break;
  345       case gz_opt: parse_compressor( arg, fmt_gz ); break;
  346       case lz_opt: parse_compressor( arg, fmt_lz ); break;
  347       case xz_opt: parse_compressor( arg, fmt_xz ); break;
  348       default : internal_error( "uncaught option." );
  349       }
  350     } // end process options
  351 
  352 #if defined(__MSVCRT__) || defined(__OS2__)
  353   setmode( STDIN_FILENO, O_BINARY );
  354   setmode( STDOUT_FILENO, O_BINARY );
  355 #endif
  356 
  357   if( argind >= parser.arguments() )
  358     { show_error( "No files given.", 0, true ); return 2; }
  359   if( argind + 2 < parser.arguments() )
  360     { show_error( "Too many files.", 0, true ); return 2; }
  361 
  362   const int files = parser.arguments() - argind;
  363   std::string filenames[2];     // file names of the two input files
  364   filenames[0] = parser.argument( argind );
  365   if( files == 2 ) filenames[1] = parser.argument( argind + 1 );
  366 
  367   int infd[2];              // file descriptors of the two files
  368   infd[0] = ( filenames[0] == "-" ) ?
  369     STDIN_FILENO : open_instream( filenames[0] );
  370   if( infd[0] < 0 ) return 2;
  371 
  372   if( files == 2 )
  373     {
  374     if( check_identical( filenames[0].c_str(), filenames[1].c_str() ) )
  375       return 0;
  376     infd[1] = ( filenames[1] == "-" ) ?
  377       STDIN_FILENO : open_instream( filenames[1] );
  378     if( infd[1] < 0 ) return 2;
  379     }
  380   else
  381     {
  382     if( filenames[0] == "-" )
  383       { show_error( "Missing operand after '-'.", 0, true ); return 2; }
  384     if( format_types[0] >= 0 || format_types[1] >= 0 )
  385       { show_error( "Two files must be given when format is specified.", 0, true );
  386         return 2; }
  387     filenames[1] = filenames[0];
  388     infd[1] = open_other_instream( filenames[1] );
  389     if( infd[1] < 0 )
  390       {
  391       if( verbosity >= 0 )
  392         std::fprintf( stderr, "%s: Can't find file to compare with '%s'.\n",
  393                       program_name, filenames[0].c_str() );
  394       show_error( 0, 0, true ); return 2;
  395       }
  396     }
  397 
  398   std::atexit( remove_fifos );
  399   set_signals();
  400   if( !set_fifonames( filenames ) ) return 2;
  401 
  402   Children children[2];
  403   if( !set_data_feeder( filenames[0], fifonames[0], infd[0], children[0],
  404                         format_types[0] ) ||
  405       !set_data_feeder( filenames[1], fifonames[1], infd[1], children[1],
  406                         format_types[1] ) )
  407     return 2;
  408 
  409   const pid_t diff_pid = fork();
  410   if( diff_pid == 0 )           // child (diff)
  411     {
  412     const char ** const argv = new const char *[diff_args.size()+5];
  413     argv[0] = DIFF;
  414     for( unsigned i = 0; i < diff_args.size(); ++i )
  415       argv[i+1] = diff_args[i];
  416     argv[diff_args.size()+1] = "--";
  417     argv[diff_args.size()+2] = fifonames[0].c_str();
  418     argv[diff_args.size()+3] = fifonames[1].c_str();
  419     argv[diff_args.size()+4] = 0;
  420     execvp( argv[0], (char **)argv );
  421     show_exec_error( DIFF );
  422     _exit( 2 );
  423     }
  424   if( diff_pid < 0 )            // parent
  425     { show_fork_error( DIFF ); return 2; }
  426 
  427   int retval = wait_for_child( diff_pid, DIFF );
  428 
  429   for( int i = 0; i < 2; ++i )
  430     if( !good_status( children[i], retval == 0 ) ) retval = 2;
  431 
  432   for( int i = 0; i < 2; ++i )
  433     if( filenames[i] != "-" && close( infd[i] ) != 0 )
  434       {
  435       show_file_error( filenames[i].c_str(), "Error closing input file", errno );
  436       retval = 2;
  437       }
  438 
  439   return retval;
  440   }