home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / wdiff-0.5-src.tgz / tar.out / fsf / wdiff / wdiff.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  34KB  |  1,334 lines

  1. /* wdiff -- front end to diff for comparing on a word per word basis.
  2.    Copyright (C) 1992 Free Software Foundation, Inc.
  3.    Francois Pinard <pinard@iro.umontreal.ca>.
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 2, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #if HAVE_CONFIG_H
  20. # include <config.h>
  21. #endif
  22.  
  23. /* Exit codes values.  */
  24. #define EXIT_NO_DIFFERENCES 0    /* no differences found */
  25. #define EXIT_ANY_DIFFERENCE 1    /* some differences found */
  26. #define EXIT_OTHER_REASON 2    /* any other reason for exit */
  27.  
  28. /* It is mandatory that some `diff' program is selected for use.  The
  29.    following definition may also include the complete path.  */
  30. #ifndef DIFF_PROGRAM
  31. # define DIFF_PROGRAM "diff"
  32. #endif
  33.  
  34. /* One may also, optionnaly, define a default PAGER_PROGRAM.  This might
  35.    be done from the Makefile.  If PAGER_PROGRAM is undefined and the
  36.    PAGER environment variable is not set, none will be used.  */
  37.  
  38. /* Define the separator lines when output is inhibited.  */
  39. #define SEPARATOR_LINE \
  40.   "======================================================================"
  41.  
  42. /* Library declarations.  */
  43.  
  44. #if STDC_HEADERS
  45. # include <stdlib.h>
  46. #endif
  47.  
  48. #include <ctype.h>
  49. #include <stdio.h>
  50.  
  51. #if STDC_HEADERS
  52. # include <string.h>
  53. #else
  54. # if HAVE_STRING_H
  55. #  include <string.h>
  56. # else
  57. #  include <strings.h>
  58. #  define strrchr rindex
  59. # endif
  60. #endif
  61.  
  62. char *strstr ();
  63.  
  64. #if HAVE_TPUTS
  65. # if HAVE_TERMCAP_H
  66. #  include <termcap.h>
  67. # else
  68. const char *tgetstr ();
  69. # endif
  70. #endif
  71.  
  72. #include <sys/types.h>
  73. #include <setjmp.h>
  74. #include <signal.h>
  75. #ifndef RETSIGTYPE
  76. # define RETSIGTYPE void
  77. #endif
  78.  
  79. #include <sys/stat.h>
  80.  
  81. #if HAVE_UNISTD_H
  82. # include <unistd.h>
  83. #endif
  84.  
  85. #include <errno.h>
  86. #ifndef errno
  87. extern int errno;
  88. #endif
  89.  
  90. #include "getopt.h"
  91.  
  92. #ifdef PROTOTYPES
  93. # define _(Args) Args
  94. #else
  95. # define _(Args) ()
  96. #endif
  97.  
  98. char *getenv ();
  99. FILE *readpipe _((const char *, ...));
  100. FILE *writepipe _((const char *, ...));
  101. char *tmpnam ();
  102. void error ();
  103.  
  104. /* Declarations.  */
  105.  
  106. /* Option variables.  */
  107.  
  108. struct option const longopts[] =
  109. {
  110.   {"copyright"   , 0, NULL, 'C'},
  111.   {"version"     , 0, NULL, 'V'},
  112.   {"no-deleted"  , 0, NULL, '1'},
  113.   {"no-inserted" , 0, NULL, '2'},
  114.   {"no-common"   , 0, NULL, '3'},
  115.   {"help"        , 0, NULL, 'h'},
  116.   {"ignore-case" , 0, NULL, 'i'},
  117.   {"printer"     , 0, NULL, 'p'},
  118.   {"statistics"  , 0, NULL, 's'},
  119.   {"terminal"    , 0, NULL, 't'},
  120.   {"start-delete", 1, NULL, 'w'},
  121.   {"end-delete"  , 1, NULL, 'x'},
  122.   {"start-insert", 1, NULL, 'y'},
  123.   {"end-insert"  , 1, NULL, 'z'},
  124.   {NULL          , 0, NULL, 0}
  125. };
  126.  
  127. static char const copyright[] =
  128.   "Copyright (C) 1992 Free Software Foundation, Inc.";
  129.  
  130. const char *program_name;    /* name of executing program */
  131.  
  132. int inhibit_left;        /* inhibit display of left side words */
  133. int inhibit_right;        /* inhibit display of left side words */
  134. int inhibit_common;        /* inhibit display of common words */
  135. int ignore_case;        /* ignore case in comparisons */
  136. int show_statistics;        /* if printing summary statistics */
  137. int no_wrapping;        /* end/restart strings at end of lines */
  138. int autopager;            /* if calling the pager automatically */
  139. int overstrike;            /* if using printer overstrikes */
  140. int overstrike_for_less;    /* if output aimed to the "less" program */
  141. const char *user_delete_start;    /* user specified string for start of delete */
  142. const char *user_delete_end;    /* user specified string for end of delete */
  143. const char *user_insert_start;    /* user specified string for start of insert */
  144. const char *user_insert_end;    /* user specified string for end of insert */
  145.  
  146. int find_termcap;        /* initialize the termcap strings */
  147. const char *term_delete_start;    /* termcap string for start of delete */
  148. const char *term_delete_end;    /* termcap string for end of delete */
  149. const char *term_insert_start;    /* termcap string for start of insert */
  150. const char *term_insert_end;    /* termcap string for end of insert */
  151.  
  152. /* Other variables.  */
  153.  
  154. enum copy_mode
  155. {
  156.   COPY_NORMAL,            /* copy text unemphasized */
  157.   COPY_DELETED,            /* copy text underlined */
  158.   COPY_INSERTED            /* copy text bolded */
  159. }
  160. copy_mode;
  161.  
  162. jmp_buf signal_label;        /* where to jump when signal received */
  163. int interrupted;        /* set when some signal has been received */
  164.  
  165. /* Guarantee some value for L_tmpnam.  */
  166. #ifndef L_tmpnam
  167. # include "pathmax.h"
  168. # define L_tmpnam PATH_MAX
  169. #endif
  170.  
  171. typedef struct side SIDE;    /* all variables for one side */
  172. struct side
  173. {
  174.   const char *filename;        /* original input file name */
  175.   FILE *file;            /* original input file */
  176.   int position;            /* number of words read so far */
  177.   int character;        /* one character look ahead */
  178.   char temp_name[L_tmpnam];    /* temporary file name */
  179.   FILE *temp_file;        /* temporary file */
  180. };
  181. SIDE side_array[2];        /* area for holding side descriptions */
  182. SIDE *left_side = &side_array[0];
  183. SIDE *right_side = &side_array[1];
  184.  
  185. FILE *input_file;        /* stream being produced by diff */
  186. int character;            /* for reading input_file */
  187. char directive;            /* diff directive character */
  188. int argument[4];        /* four diff directive arguments */
  189.  
  190. FILE *output_file;        /* file to which we write output */
  191. const char *termcap_init_string; /* how to initialize the termcap mode */
  192. const char *termcap_end_string; /* how to complete the termcap mode */
  193.  
  194. int count_total_left;        /* count of total words in left file */
  195. int count_total_right;        /* count of total words in right file */
  196. int count_isolated_left;    /* count of deleted words in left file */
  197. int count_isolated_right;    /* count of added words in right file */
  198. int count_changed_left;        /* count of changed words in left file */
  199. int count_changed_right;    /* count of changed words in right file */
  200.  
  201. /* Signal processing.  */
  202.  
  203. /*-----------------.
  204. | Signal handler.  |
  205. `-----------------*/
  206.  
  207. static RETSIGTYPE
  208. signal_handler (int number)
  209. {
  210.   interrupted = 1;
  211.   signal (number, signal_handler);
  212. }
  213.  
  214. /*----------------------------.
  215. | Prepare to handle signals.  |
  216. `----------------------------*/
  217.  
  218. static void
  219. setup_signals (void)
  220. {
  221.   interrupted = 0;
  222.  
  223.   /* Intercept willingful requests for stopping.  */
  224.  
  225.   signal (SIGINT, signal_handler);
  226.   signal (SIGPIPE, signal_handler);
  227.   signal (SIGTERM, signal_handler);
  228. }
  229.  
  230.  
  231. /* Terminal initialization.  */
  232.  
  233. static void
  234. initialize_strings (void)
  235. {
  236. #if HAVE_TPUTS
  237.   if (find_termcap)
  238.     {
  239.       const char *name;        /* terminal capability name */
  240.       char term_buffer[2048];    /* terminal description */
  241.       static char *buffer;    /* buffer for capabilities */
  242.       char *filler;        /* cursor into allocated strings */
  243.       int success;        /* tgetent results */
  244.  
  245.       name = getenv ("TERM");
  246.       if (name == NULL)
  247.     error (1, 0, "Specify a terminal type with `setenv TERM <yourtype>'.");
  248.       success = tgetent (term_buffer, name);
  249.       if (success < 0)
  250.     error (1, 0, "Could not access the termcap data base.");
  251.       if (success == 0)
  252.     error (1, 0, "Terminal type `%s' is not defined.", name);
  253.       buffer = (char *) malloc (strlen (term_buffer));
  254.       filler = buffer;
  255.  
  256.       termcap_init_string = tgetstr ("ti", &filler);
  257.       termcap_end_string = tgetstr ("te", &filler);
  258.       term_delete_start = tgetstr ("us", &filler);
  259.       term_delete_end = tgetstr ("ue", &filler);
  260.       term_insert_start = tgetstr ("so", &filler);
  261.       term_insert_end = tgetstr ("se", &filler);
  262.     }
  263. #endif /* HAVE_TPUTS */
  264.  
  265.   /* Ensure some default strings.  */
  266.  
  267.   if (!overstrike)
  268.     {
  269.       if (!term_delete_start && !user_delete_start)
  270.     user_delete_start = "[-";
  271.       if (!term_delete_end && !user_delete_end)
  272.     user_delete_end = "-]";
  273.       if (!term_insert_start && !user_insert_start)
  274.     user_insert_start = "{+";
  275.       if (!term_insert_end && !user_insert_end)
  276.     user_insert_end = "+}";
  277.     }
  278. }
  279.  
  280.  
  281. /* Character input and output.  */
  282.  
  283. #if HAVE_TPUTS
  284.  
  285. /*-----------------------------------------.
  286. | Write one character for tputs function.  |
  287. `-----------------------------------------*/
  288.  
  289. static int
  290. putc_for_tputs (int chr)
  291. {
  292.   return putc (chr, output_file);
  293. }
  294.  
  295. #endif /* HAVE_TPUTS */
  296.  
  297. /*---------------------------.
  298. | Indicate start of delete.  |
  299. `---------------------------*/
  300.  
  301. static void
  302. start_of_delete (void)
  303. {
  304.  
  305.   /* Avoid any emphasis if it would be useless.  */
  306.  
  307.   if (inhibit_common && (inhibit_right || inhibit_left))
  308.     return;
  309.  
  310.   copy_mode = COPY_DELETED;
  311. #if HAVE_TPUTS
  312.   if (term_delete_start)
  313.     tputs (term_delete_start, 0, putc_for_tputs);
  314. #endif
  315.   if (user_delete_start)
  316.     fprintf (output_file, "%s", user_delete_start);
  317. }
  318.  
  319. /*-------------------------.
  320. | Indicate end of delete.  |
  321. `-------------------------*/
  322.  
  323. static void
  324. end_of_delete (void)
  325. {
  326.  
  327.   /* Avoid any emphasis if it would be useless.  */
  328.  
  329.   if (inhibit_common && (inhibit_right || inhibit_left))
  330.     return;
  331.  
  332.   if (user_delete_end)
  333.     fprintf (output_file, "%s", user_delete_end);
  334. #if HAVE_TPUTS
  335.   if (term_delete_end)
  336.     tputs (term_delete_end, 0, putc_for_tputs);
  337. #endif
  338.   copy_mode = COPY_NORMAL;
  339. }
  340.  
  341. /*---------------------------.
  342. | Indicate start of insert.  |
  343. `---------------------------*/
  344.  
  345. static void
  346. start_of_insert (void)
  347. {
  348.  
  349.   /* Avoid any emphasis if it would be useless.  */
  350.  
  351.   if (inhibit_common && (inhibit_right || inhibit_left))
  352.     return;
  353.  
  354.   copy_mode = COPY_INSERTED;
  355. #if HAVE_TPUTS
  356.   if (term_insert_start)
  357.     tputs (term_insert_start, 0, putc_for_tputs);
  358. #endif
  359.   if (user_insert_start)
  360.     fprintf (output_file, "%s", user_insert_start);
  361. }
  362.  
  363. /*-------------------------.
  364. | Indicate end of insert.  |
  365. `-------------------------*/
  366.  
  367. static void
  368. end_of_insert (void)
  369. {
  370.  
  371.   /* Avoid any emphasis if it would be useless.  */
  372.  
  373.   if (inhibit_common && (inhibit_right || inhibit_left))
  374.     return;
  375.  
  376.   if (user_insert_end)
  377.     fprintf (output_file, "%s", user_insert_end);
  378. #if HAVE_TPUTS
  379.   if (term_insert_end)
  380.     tputs (term_insert_end, 0, putc_for_tputs);
  381. #endif
  382.   copy_mode = COPY_NORMAL;
  383. }
  384.  
  385. /*--------------------------------.
  386. | Skip over white space on SIDE.  |
  387. `--------------------------------*/
  388.  
  389. static void
  390. skip_whitespace (SIDE *side)
  391. {
  392.   if (interrupted)
  393.     longjmp (signal_label, 1);
  394.  
  395.   while (isspace (side->character))
  396.     side->character = getc (side->file);
  397. }
  398.  
  399. /*------------------------------------.
  400. | Skip over non white space on SIDE.  |
  401. `------------------------------------*/
  402.  
  403. static void
  404. skip_word (SIDE *side)
  405. {
  406.   if (interrupted)
  407.     longjmp (signal_label, 1);
  408.  
  409.   while (side->character != EOF && !isspace (side->character))
  410.     side->character = getc (side->file);
  411.   side->position++;
  412. }
  413.  
  414. /*-------------------------------------.
  415. | Copy white space from SIDE to FILE.  |
  416. `-------------------------------------*/
  417.  
  418. static void
  419. copy_whitespace (SIDE *side, FILE *file)
  420. {
  421.   if (interrupted)
  422.     longjmp (signal_label, 1);
  423.  
  424.   while (isspace (side->character))
  425.     {
  426.  
  427.       /* While changing lines, ensure we stop any special display prior
  428.      to, and restore the special display after.  When copy_mode is
  429.      anything else than COPY_NORMAL, file is always output_file.  We
  430.      care underlining whitespace or overstriking it with itself,
  431.      because "less" understands these things as emphasis requests.  */
  432.  
  433.       switch (copy_mode)
  434.     {
  435.     case COPY_NORMAL:
  436.       putc (side->character, file);
  437.       break;
  438.  
  439.     case COPY_DELETED:
  440.       if (side->character == '\n')
  441.         {
  442.           if (no_wrapping && user_delete_end)
  443.         fprintf (output_file, "%s", user_delete_end);
  444. #if HAVE_TPUTS
  445.           if (term_delete_end)
  446.         tputs (term_delete_end, 0, putc_for_tputs);
  447. #endif
  448.           putc ('\n', output_file);
  449. #if HAVE_TPUTS
  450.           if (term_delete_start)
  451.         tputs (term_delete_start, 0, putc_for_tputs);
  452. #endif
  453.           if (no_wrapping && user_delete_start)
  454.         fprintf (output_file, "%s", user_delete_start);
  455.         }
  456.       else if (overstrike_for_less)
  457.         {
  458.           putc ('_', output_file);
  459.           putc ('\b', output_file);
  460.           putc (side->character, output_file);
  461.         }
  462.       else
  463.         putc (side->character, output_file);
  464.       break;
  465.  
  466.     case COPY_INSERTED:
  467.       if (side->character == '\n')
  468.         {
  469.           if (no_wrapping && user_insert_end)
  470.         fprintf (output_file, "%s", user_insert_end);
  471. #if HAVE_TPUTS
  472.           if (term_insert_end)
  473.         tputs (term_insert_end, 0, putc_for_tputs);
  474. #endif
  475.           putc ('\n', output_file);
  476. #if HAVE_TPUTS
  477.           if (term_insert_start)
  478.         tputs (term_insert_start, 0, putc_for_tputs);
  479. #endif
  480.           if (no_wrapping && user_insert_start)
  481.         fprintf (output_file, "%s", user_insert_start);
  482.         }
  483.       else if (overstrike_for_less)
  484.         {
  485.           putc (side->character, output_file);
  486.           putc ('\b', output_file);
  487.           putc (side->character, output_file);
  488.         }
  489.       else
  490.         putc (side->character, output_file);
  491.       break;
  492.     }
  493.  
  494.       /* Advance to next character.  */
  495.  
  496.       side->character = getc (side->file);
  497.     }
  498. }
  499.  
  500. /*-----------------------------------------.
  501. | Copy non white space from SIDE to FILE.  |
  502. `-----------------------------------------*/
  503.  
  504. static void
  505. copy_word (SIDE *side, FILE *file)
  506. {
  507.   if (interrupted)
  508.     longjmp (signal_label, 1);
  509.  
  510.   while (side->character != EOF && !isspace (side->character))
  511.     {
  512.  
  513.       /* In printer mode, act according to copy_mode.  If copy_mode is not
  514.      COPY_NORMAL, we know that file is necessarily output_file.  */
  515.  
  516.       if (overstrike)
  517.     switch (copy_mode)
  518.       {
  519.       case COPY_NORMAL:
  520.         putc (side->character, file);
  521.         break;
  522.  
  523.       case COPY_DELETED:
  524.         putc ('_', output_file);
  525.  
  526.         /* Avoid underlining an underscore.  */
  527.  
  528.         if (side->character != '_')
  529.           {
  530.         putc ('\b', output_file);
  531.         putc (side->character, output_file);
  532.           }
  533.         break;
  534.       
  535.       case COPY_INSERTED:
  536.         putc (side->character, output_file);
  537.         putc ('\b', output_file);
  538.         putc (side->character, output_file);
  539.         break;
  540.       }
  541.       else
  542.     putc (side->character, file);
  543.  
  544.       side->character = getc (side->file);
  545.     }
  546.   side->position++;
  547. }
  548.  
  549. /*-------------------------------------------------------------------------.
  550. | For a given SIDE, turn original input file in another one, in which each |
  551. | word is on one line.                               |
  552. `-------------------------------------------------------------------------*/
  553.  
  554. static void
  555. split_file_into_words (SIDE *side)
  556. {
  557.   struct stat stat_buffer;    /* for checking if file is directory */
  558.  
  559.   /* Open files.  */
  560.  
  561.   if (side->filename == NULL)
  562.     {
  563.  
  564.       /* Select a file name, use it for opening a temporary file and
  565.      unlink it right away.  Then, copy the whole standard input on
  566.      this temporary local file.  Once done, prepare it for reading.
  567.      We do not need the file name itself anymore.  */
  568.  
  569.       tmpnam (side->temp_name);
  570.       side->file = fopen (side->temp_name, "w+");
  571.       if (side->file == NULL)
  572.     error (EXIT_OTHER_REASON, errno, side->temp_name);
  573.       if (unlink (side->temp_name) != 0)
  574.     error (EXIT_OTHER_REASON, errno, side->temp_name);
  575.       while (side->character = getchar (), side->character != EOF)
  576.     putc (side->character, side->file);
  577.       rewind (side->file);
  578.     }
  579.   else
  580.     {
  581.  
  582.       /* Check and diagnose if the file name is a directory.  Or else,
  583.      prepare the file for reading.  */
  584.      
  585.       if (stat (side->filename, &stat_buffer) != 0)
  586.     error (EXIT_OTHER_REASON, errno, side->filename);
  587.       if ((stat_buffer.st_mode & S_IFMT) == S_IFDIR)
  588.     error (EXIT_OTHER_REASON, 0, "Directories not supported");
  589.       side->file = fopen (side->filename, "r");
  590.       if (side->file == NULL)
  591.     error (EXIT_OTHER_REASON, errno, side->filename);
  592.     }
  593.   side->character = getc (side->file);
  594.   side->position = 0;
  595.  
  596.   tmpnam (side->temp_name);
  597.   side->temp_file = fopen (side->temp_name, "w");
  598.   if (side->temp_file == NULL)
  599.     error (EXIT_OTHER_REASON, errno, side->temp_name);
  600.  
  601.   /* Complete splitting input file into words on output.  */
  602.  
  603.   while (side->character != EOF)
  604.     {
  605.       if (interrupted)
  606.     longjmp (signal_label, 1);
  607.  
  608.       skip_whitespace (side);
  609.       if (side->character == EOF)
  610.     break;
  611.       copy_word (side, side->temp_file);
  612.       putc ('\n', side->temp_file);
  613.     }
  614.   fclose (side->temp_file);
  615. }
  616.  
  617. /*-------------------------------------------------------------------.
  618. | Decode one directive line from INPUT_FILE.  The format should be:  |
  619. |                                      |
  620. |      ARG0 [ , ARG1 ] LETTER ARG2 [ , ARG3 ] \n             |
  621. |                                      |
  622. | By default, ARG1 is assumed to have the value of ARG0, and ARG3 is |
  623. | assumed to have the value of ARG2.  Return 0 if any error found.   |
  624. `-------------------------------------------------------------------*/
  625.  
  626. static int
  627. decode_directive_line (void)
  628. {
  629.   int value;            /* last scanned value */
  630.   int state;            /* ordinal of number being read */
  631.   int error;            /* if error seen */
  632.  
  633.   error = 0;
  634.   state = 0;
  635.   value = 0;
  636.   while (!error && state < 4)
  637.     {
  638.  
  639.       /* Read the next number.  ARG0 and ARG2 are mandatory.  */
  640.  
  641.       if (isdigit (character))
  642.     {
  643.       value = 0;
  644.       while (isdigit (character))
  645.         {
  646.           value = 10 * value + character - '0';
  647.           character = getc (input_file);
  648.         }
  649.     }
  650.       else if (state != 1 && state != 3)
  651.     error = 1;
  652.  
  653.       /* Assign the proper value.  */
  654.  
  655.       argument[state] = value;
  656.  
  657.       /* Skip the following character.  */
  658.  
  659.       switch (state)
  660.     {
  661.     case 0:
  662.     case 2:
  663.       if (character == ',')
  664.         character = getc (input_file);
  665.       break;
  666.       
  667.     case 1:
  668.       if (character == 'a' || character == 'd' || character == 'c')
  669.         {
  670.           directive = character;
  671.           character = getc (input_file);
  672.         }
  673.       else
  674.         error = 1;
  675.       break;
  676.  
  677.     case 3:
  678.       if (character != '\n')
  679.         error = 1;
  680.       break;
  681.     }
  682.       state++;
  683.     }
  684.  
  685.   /* Complete reading of the line and return success value.  */
  686.  
  687.   while (character != EOF && character != '\n')
  688.     character = getc (input_file);
  689.   if (character == '\n')
  690.     character = getc (input_file);
  691.  
  692.   return !error;
  693. }  
  694.  
  695. /*----------------------------------------------.
  696. | Skip SIDE until some word ORDINAL, included.  |
  697. `----------------------------------------------*/
  698.  
  699. static void
  700. skip_until_ordinal (SIDE *side, int ordinal)
  701. {
  702.   while (side->position < ordinal)
  703.     {
  704.       skip_whitespace (side);
  705.       skip_word (side);
  706.     }
  707. }
  708.  
  709. /*----------------------------------------------.
  710. | Copy SIDE until some word ORDINAL, included.  |
  711. `----------------------------------------------*/
  712.  
  713. static void
  714. copy_until_ordinal (SIDE *side, int ordinal)
  715. {
  716.   while (side->position < ordinal)
  717.     {
  718.       copy_whitespace (side, output_file);
  719.       copy_word (side, output_file);
  720.     }
  721. }
  722.  
  723. /*-----------------------------------------------------.
  724. | Study diff output and use it to drive reformatting.  |
  725. `-----------------------------------------------------*/
  726.  
  727. static void
  728. reformat_diff_output (void)
  729. {
  730.   int resync_left;        /* word position for left resynchronisation */
  731.   int resync_right;        /* word position for rigth resynchronisation */
  732.  
  733.   /* Rewind input files.  */
  734.  
  735.   rewind (left_side->file);
  736.   left_side->character = getc (left_side->file);
  737.   left_side->position = 0;
  738.  
  739.   rewind(right_side->file);
  740.   right_side->character = getc (right_side->file);
  741.   right_side->position = 0;
  742.  
  743.   /* Process diff output.  */
  744.  
  745.   while (1)
  746.     {
  747.       if (interrupted)
  748.     longjmp (signal_label, 1);
  749.  
  750.       /* Skip any line irrelevant to this program.  */
  751.  
  752.       while (character != EOF && !isdigit (character))
  753.     {
  754.       while (character != EOF && character != '\n')
  755.         character = getc (input_file);
  756.       if (character == '\n')
  757.         character = getc (input_file);
  758.     }
  759.  
  760.       /* Get out the loop if end of file.  */
  761.  
  762.       if (character == EOF)
  763.     break;
  764.  
  765.       /* Read, decode and process one directive line.  */
  766.  
  767.       if (decode_directive_line ())
  768.     {
  769.  
  770.       /* Accumulate statistics about isolated or changed word counts.
  771.          Decide the required position on both files to resynchronize
  772.          them, just before obeying the directive.  Then, reposition
  773.          both files first, showing any needed common code along the
  774.          road.  Be careful to copy common code from the left side if
  775.          only deleted code is to be shown.  */
  776.  
  777.       switch (directive)
  778.         {
  779.         case 'a':
  780.           count_isolated_right += argument[3] - argument[2] + 1;
  781.           resync_left = argument[0];
  782.           resync_right = argument[2] - 1;
  783.           break;
  784.  
  785.         case 'd':
  786.           count_isolated_left += argument[1] - argument[0] + 1;
  787.           resync_left = argument[0] - 1;
  788.           resync_right = argument[2];
  789.           break;
  790.  
  791.         case 'c':
  792.           count_changed_left += argument[1] - argument[0] + 1;
  793.           count_changed_right += argument[3] - argument[2] + 1;
  794.           resync_left = argument[0] - 1;
  795.           resync_right = argument[2] - 1;
  796.           break;
  797.  
  798.         default:
  799.           abort ();
  800.         }
  801.  
  802.       if (!inhibit_left)
  803.         if (!inhibit_common && inhibit_right)
  804.           copy_until_ordinal (left_side, resync_left);
  805.         else
  806.           skip_until_ordinal (left_side, resync_left);
  807.  
  808.       if (!inhibit_right)
  809.         if (inhibit_common)
  810.           skip_until_ordinal (right_side, resync_right);
  811.         else
  812.           copy_until_ordinal (right_side, resync_right);
  813.  
  814.       if (!inhibit_common && inhibit_left && inhibit_right)
  815.         copy_until_ordinal (right_side, resync_right);
  816.  
  817.       /* Use separator lines to disambiguate the output.  */
  818.  
  819.       if (inhibit_left && inhibit_right)
  820.         {
  821.           if (!inhibit_common)
  822.         fprintf (output_file, "\n%s\n", SEPARATOR_LINE);
  823.         }
  824.       else if (inhibit_common)
  825.         fprintf (output_file, "\n%s\n", SEPARATOR_LINE);
  826.  
  827.       /* Show any deleted code.  */
  828.  
  829.       if ((directive == 'd' || directive == 'c') && !inhibit_left)
  830.         {
  831.           copy_whitespace (left_side, output_file);
  832.           start_of_delete ();
  833.           copy_word (left_side, output_file);
  834.           copy_until_ordinal (left_side, argument[1]);
  835.           end_of_delete ();
  836.         }
  837.  
  838.       /* Show any inserted code, or ensure skipping over it in case the
  839.          right file is used merely to show common words.  */
  840.  
  841.       if (directive == 'a' || directive == 'c')
  842.         if (inhibit_right)
  843.           {
  844.         if (!inhibit_common && inhibit_left)
  845.           skip_until_ordinal (right_side, argument[3]);
  846.           }
  847.         else
  848.           {
  849.         copy_whitespace (right_side, output_file);
  850.         start_of_insert ();
  851.         copy_word (right_side, output_file);
  852.         copy_until_ordinal (right_side, argument[3]);
  853.         end_of_insert ();
  854.           }
  855.     }
  856.     }
  857.  
  858.   /* Copy remainder of input.  Copy from left side if the user wanted to see
  859.      only the common code and deleted words.  */
  860.  
  861.   if (inhibit_common)
  862.     {
  863.       if (!inhibit_left || !inhibit_right)
  864.     fprintf (output_file, "\n%s\n", SEPARATOR_LINE);
  865.     }
  866.   else if (!inhibit_left && inhibit_right)
  867.     {
  868.       copy_until_ordinal (left_side, count_total_left);
  869.       copy_whitespace (left_side, output_file);
  870.     }
  871.   else
  872.     {
  873.       copy_until_ordinal (right_side, count_total_right);
  874.       copy_whitespace (right_side, output_file);
  875.     }
  876.  
  877.   /* Close input files.  */
  878.  
  879.   fclose (left_side->file);
  880.   fclose (right_side->file);
  881. }
  882.  
  883.  
  884. /* Launch and complete various programs.  */
  885.  
  886. /*-------------------------.
  887. | Lauch the diff program.  |
  888. `-------------------------*/
  889.  
  890. static void
  891. launch_input_program (void)
  892. {
  893.   /* Launch the diff program.  */
  894.  
  895.   if (ignore_case)
  896.     input_file = readpipe (DIFF_PROGRAM, "-c", left_side->temp_name,
  897.                right_side->temp_name, NULL);
  898.   else
  899.     input_file = readpipe (DIFF_PROGRAM, left_side->temp_name,
  900.                right_side->temp_name, NULL);
  901.   if (!input_file)
  902.     error (EXIT_OTHER_REASON, errno, DIFF_PROGRAM);
  903.   character = getc (input_file);
  904. }
  905.  
  906. /*----------------------------.
  907. | Complete the diff program.  |
  908. `----------------------------*/
  909.  
  910. static void
  911. complete_input_program (void)
  912. {
  913.   fclose (input_file);
  914.   wait (NULL);
  915. }
  916.  
  917. /*---------------------------------.
  918. | Launch the output pager if any.  |
  919. `---------------------------------*/
  920.  
  921. static void
  922. launch_output_program (void)
  923. {
  924.   char *program;        /* name of the pager */
  925.   char *basename;        /* basename of the pager */
  926.  
  927.   /* Check if a output program should be called, and which one.  Avoid
  928.      all paging if only statistics are needed.  */
  929.  
  930.   if (autopager && isatty (fileno (stdout))
  931.       && !(inhibit_left && inhibit_right && inhibit_common))
  932.     {
  933.       program = getenv ("PAGER");
  934. #ifdef PAGER_PROGRAM
  935.       if (program == NULL)
  936.     program = PAGER_PROGRAM;
  937. #endif
  938.     }
  939.   else
  940.     program = NULL;
  941.  
  942.   /* Use stdout as default output.  */
  943.  
  944.   output_file = stdout;
  945.  
  946.   /* Ensure the termcap initialization string is sent to stdout right
  947.      away, never to the pager.  */
  948.  
  949. #if HAVE_TPUTS
  950.   if (termcap_init_string)
  951.     {
  952.       tputs (termcap_init_string, 0, putc_for_tputs);
  953.       fflush (stdout);
  954.     }
  955. #endif
  956.  
  957.   /* If we should use a pager, launch it.  */
  958.  
  959.   if (program && *program)
  960.     {
  961.       output_file = writepipe (program, NULL);
  962.       if (!output_file)
  963.     error (EXIT_OTHER_REASON, errno, program);
  964.  
  965.       /* If we are paging to less, use printer mode, not display mode.  */
  966.  
  967.       if (basename = strrchr ("less", '/'), basename)
  968.     basename++;
  969.       else
  970.     basename = program;
  971.  
  972.       if (strstr (basename, "less"))
  973.     {
  974.       find_termcap = 0;
  975.       overstrike = 1;
  976.       overstrike_for_less = 1;
  977.     }
  978.     }
  979. }
  980.  
  981. /*-----------------------------.
  982. | Complete the pager program.  |
  983. `-----------------------------*/
  984.  
  985. static void
  986. complete_output_program (void)
  987. {
  988.  
  989.   /* Complete any pending emphasis mode.  This would be necessary only if
  990.      some signal interrupts the normal operation of the program.  */
  991.  
  992.   switch (copy_mode)
  993.     {
  994.     case COPY_DELETED:
  995.       end_of_delete ();
  996.       break;
  997.  
  998.     case COPY_INSERTED:
  999.       end_of_insert ();
  1000.       break;
  1001.     
  1002.     case COPY_NORMAL:
  1003.       break;
  1004.  
  1005.     default:
  1006.       abort ();
  1007.     }
  1008.  
  1009.   /* Let the user play at will inside the pager, until s/he exits, before
  1010.      proceeding any further.  */
  1011.  
  1012.   if (output_file && output_file != stdout)
  1013.     {
  1014.       fclose (output_file);
  1015.       wait (NULL);
  1016.     }
  1017.  
  1018.   /* Ensure the termcap termination string is sent to stdout, never to
  1019.      the pager.  Moreover, the pager has terminated already.  */
  1020.  
  1021. #if HAVE_TPUTS
  1022.   if (termcap_end_string)
  1023.     {
  1024.       output_file = stdout;
  1025.       tputs (termcap_end_string, 0, putc_for_tputs);
  1026.     }
  1027. #endif
  1028. }
  1029.  
  1030. /*-------------------------------.
  1031. | Print accumulated statistics.     |
  1032. `-------------------------------*/
  1033.  
  1034. static void
  1035. print_statistics (void)
  1036. {
  1037.   int count_common_left;    /* words unchanged in left file */
  1038.   int count_common_right;    /* words unchanged in right file */
  1039.  
  1040.   count_common_left
  1041.     = count_total_left - count_isolated_left - count_changed_left;
  1042.   count_common_right
  1043.     = count_total_right - count_isolated_right - count_changed_right;
  1044.  
  1045.   printf ("%s: %d words", left_side->filename, count_total_left);
  1046.   if (count_total_left > 0)
  1047.     {
  1048.       printf ("  %d %d%% common", count_common_left,
  1049.           count_common_left * 100 / count_total_left);
  1050.       printf ("  %d %d%% deleted", count_isolated_left,
  1051.           count_isolated_left * 100 / count_total_left);
  1052.       printf ("  %d %d%% changed", count_changed_left,
  1053.           count_changed_left * 100 / count_total_left);
  1054.     }
  1055.   printf ("\n");
  1056.  
  1057.   printf ("%s: %d words", right_side->filename, count_total_right);
  1058.   if (count_total_right > 0)
  1059.     {
  1060.       printf ("  %d %d%% common", count_common_right,
  1061.           count_common_right * 100 / count_total_right);
  1062.       printf ("  %d %d%% inserted", count_isolated_right,
  1063.           count_isolated_right * 100 / count_total_right);
  1064.       printf ("  %d %d%% changed", count_changed_right,
  1065.           count_changed_right * 100 / count_total_right);
  1066.     }
  1067.   printf ("\n");
  1068. }
  1069.  
  1070.  
  1071. /* Main control.  */
  1072.  
  1073. /*-----------------------------------.
  1074. | Prints a more detailed Copyright.  |
  1075. `-----------------------------------*/
  1076.  
  1077. static void
  1078. print_copyright (void)
  1079. {
  1080.   fprintf (stderr, "\
  1081. This program is free software; you can redistribute it and/or modify\n\
  1082. it under the terms of the GNU General Public License as published by\n\
  1083. the Free Software Foundation; either version 2, or (at your option)\n\
  1084. any later version.\n\
  1085. \n\
  1086. This program is distributed in the hope that it will be useful,\n\
  1087. but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
  1088. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
  1089. GNU General Public License for more details.\n\
  1090. \n\
  1091. You should have received a copy of the GNU General Public License\n\
  1092. along with this program; if not, write to the Free Software\n\
  1093. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
  1094. \n");
  1095. }
  1096.  
  1097. /*-----------------------------.
  1098. | Tell how to use, then exit.  |
  1099. `-----------------------------*/
  1100.  
  1101. static void
  1102. usage (int status)
  1103. {
  1104.   if (status != 0)
  1105.     fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
  1106.   else
  1107.     {
  1108.       printf ("\
  1109. Usage: %s [OPTION]... FILE1 FILE2\n", program_name);
  1110.       printf ("\
  1111. Mandatory arguments to long options are mandatory for short options too.\n\
  1112. \n\
  1113.   -C, --copyright            print Copyright then exit\n\
  1114.   -V, --version              print program version then exit\n\
  1115.   -1, --no-deleted           inhibit output of deleted words\n\
  1116.   -2, --no-inserted          inhibit output of inserted words\n\
  1117.   -3, --no-common            inhibit output of common words\n\
  1118.   -a, --auto-pager           automatically calls a pager\n\
  1119.   -h, --help                 print this help\n\
  1120.   -i, --ignore-case          fold character case while comparing\n\
  1121.   -l, --less-mode            variation of printer mode for \"less\"\n\
  1122.   -n, --avoid-wraps          do not extend fields through newlines\n\
  1123.   -p, --printer              overstrike as for printers\n\
  1124.   -s, --statistics           say how many words deleted, inserted etc.\n\
  1125.   -t, --terminal             use termcap as for terminal displays\n\
  1126.   -w, --start-delete=STRING  string to mark beginning of delete region\n\
  1127.   -x, --end-delete=STRING    string to mark end of delete region\n\
  1128.   -y, --start-insert=STRING  string to mark beginning of insert region\n\
  1129.   -z, --end-insert=STRING    string to mark end of insert region\n");
  1130.     }
  1131.   exit (status);
  1132. }
  1133.  
  1134. /*---------------.
  1135. | Main program.     |
  1136. `---------------*/
  1137.  
  1138. int
  1139. main (int argc, char *const argv[])
  1140. {
  1141.   int option_char;        /* option character */
  1142.  
  1143.   /* Decode arguments.  */
  1144.  
  1145.   program_name = argv[0];
  1146.  
  1147.   inhibit_left = 0;
  1148.   inhibit_right = 0;
  1149.   inhibit_common = 0;
  1150.  
  1151.   ignore_case = 0;
  1152.   show_statistics = 0;
  1153.   no_wrapping = 0;
  1154.   autopager = 0;
  1155.   overstrike = 0;
  1156.   overstrike_for_less = 0;
  1157.   user_delete_start = NULL;
  1158.   user_delete_end = NULL;
  1159.   user_insert_start = NULL;
  1160.   user_insert_end = NULL;
  1161.  
  1162.   find_termcap = -1;        /* undecided yet */
  1163.   term_delete_start = NULL;
  1164.   term_delete_end = NULL;
  1165.   term_insert_start = NULL;
  1166.   term_insert_end = NULL;
  1167.   copy_mode = COPY_NORMAL;
  1168.  
  1169.   count_total_left = 0;
  1170.   count_total_right = 0;
  1171.   count_isolated_left = 0;
  1172.   count_isolated_right = 0;
  1173.   count_changed_left = 0;
  1174.   count_changed_right = 0;
  1175.  
  1176.   while (option_char = getopt_long (argc, argv, "123CVahidlnpstw:x:y:z:",
  1177.                     longopts, NULL),
  1178.      option_char != EOF)
  1179.     switch (option_char)
  1180.       {
  1181.       case '1':
  1182.     inhibit_left = 1;
  1183.     break;
  1184.  
  1185.       case '2':
  1186.     inhibit_right = 1;
  1187.     break;
  1188.  
  1189.       case '3':
  1190.     inhibit_common = 1;
  1191.     break;
  1192.  
  1193.       case 'C':
  1194.     print_copyright ();
  1195.     exit (0);
  1196.  
  1197.       case 'v':
  1198.     printf ("GNU %s %s\n%s\n", PRODUCT, VERSION, copyright);
  1199.     exit (0);
  1200.  
  1201.       case 'a':
  1202.     autopager = 1;
  1203.     break;
  1204.  
  1205.       case 'h':
  1206.     usage (0);
  1207.  
  1208.       case 'i':
  1209.     ignore_case = 1;
  1210.     break;
  1211.  
  1212.       case 'l':
  1213.     if (find_termcap < 0)
  1214.       find_termcap = 0;
  1215.     overstrike = 1;
  1216.     overstrike_for_less = 1;
  1217.     break;
  1218.  
  1219.       case 'n':
  1220.     no_wrapping = 1;
  1221.     break;
  1222.  
  1223.       case 'p':
  1224.     overstrike = 1;
  1225.     break;
  1226.  
  1227.       case 's':
  1228.     show_statistics = 1;
  1229.     break;
  1230.  
  1231.       case 't':
  1232. #if HAVE_TPUTS
  1233.     if (find_termcap < 0)
  1234.       find_termcap = 1;
  1235.     break;
  1236. #else
  1237.     error (EXIT_OTHER_REASON, 0, "Cannot use -t, termcap not available.");
  1238. #endif
  1239.  
  1240.       case 'w':
  1241.     user_delete_start = optarg;
  1242.     break;
  1243.  
  1244.       case 'x':
  1245.     user_delete_end = optarg;
  1246.     break;
  1247.  
  1248.       case 'y':
  1249.     user_insert_start = optarg;
  1250.     break;
  1251.  
  1252.       case 'z':
  1253.     user_insert_end = optarg;
  1254.     break;
  1255.  
  1256.       default:
  1257.     usage (EXIT_OTHER_REASON);
  1258.       }
  1259.  
  1260.   if (optind + 2 != argc)
  1261.     usage (EXIT_OTHER_REASON);
  1262.  
  1263.   /* If find_termcap still undecided, consider it unset.  However, set if
  1264.      autopager is set while stdout is directed to a terminal, but this
  1265.      decision will be reversed later if the pager happens to be "less".  */
  1266.  
  1267.   if (find_termcap < 0)
  1268.     find_termcap = autopager && isatty (fileno (stdout));
  1269.  
  1270.   /* Setup file names and signals, then do it all.  */
  1271.  
  1272.   if (strcmp (argv[optind], "") == 0 || strcmp (argv[optind], "-") == 0)
  1273.     left_side->filename = NULL;
  1274.   else
  1275.     left_side->filename = argv[optind];
  1276.   optind++;
  1277.   *left_side->temp_name = '\0';
  1278.  
  1279.   if (strcmp (argv[optind], "") == 0 || strcmp (argv[optind], "-") == 0)
  1280.     right_side->filename = NULL;
  1281.   else
  1282.     right_side->filename = argv[optind];
  1283.   optind++;
  1284.   *right_side->temp_name = '\0';
  1285.  
  1286.   if (left_side->filename == NULL && right_side->filename == NULL)
  1287.     error (EXIT_OTHER_REASON, 0, "Both files cannot be standard input.");
  1288.  
  1289.   setup_signals ();
  1290.   input_file = NULL;
  1291.   output_file = NULL;
  1292.   termcap_init_string = NULL;
  1293.   termcap_end_string = NULL;
  1294.  
  1295.   if (!setjmp (signal_label))
  1296.     {
  1297.       split_file_into_words (left_side);
  1298.       count_total_left = left_side->position;
  1299.       split_file_into_words (right_side);
  1300.       count_total_right = right_side->position;
  1301.       launch_input_program ();
  1302.       launch_output_program ();
  1303.       initialize_strings ();
  1304.       reformat_diff_output ();
  1305.       fclose (input_file);
  1306.     }
  1307.  
  1308.   /* Clean up.  Beware that input_file and output_file might not exist,
  1309.      if a signal occurred early in the program.  */
  1310.  
  1311.   if (input_file)
  1312.     complete_input_program ();
  1313.  
  1314.   if (*left_side->temp_name)
  1315.     unlink (left_side->temp_name);
  1316.   if (*right_side->temp_name)
  1317.     unlink (right_side->temp_name);
  1318.  
  1319.   if (output_file)
  1320.     complete_output_program ();
  1321.  
  1322.   if (interrupted)
  1323.     exit (EXIT_OTHER_REASON);
  1324.  
  1325.   if (show_statistics)
  1326.     print_statistics ();
  1327.  
  1328.   if (count_isolated_left || count_isolated_right
  1329.       || count_changed_left || count_changed_right)
  1330.     exit (EXIT_ANY_DIFFERENCE);
  1331.  
  1332.   exit (EXIT_NO_DIFFERENCES);
  1333. }
  1334.