home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / rcs57pc3.zip / diff / sdiff.c < prev    next >
C/C++ Source or Header  |  1998-09-06  |  25KB  |  1,186 lines

  1. /* SDIFF -- interactive merge front end to diff
  2.    Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU SDIFF was written by Thomas Lord.  */
  21.  
  22. #include "system.h"
  23. #include <stdio.h>
  24. #include <signal.h>
  25. #include "getopt.h"
  26.  
  27. /* Size of chunks read from files which must be parsed into lines.  */
  28. #define SDIFF_BUFSIZE ((size_t) 65536)
  29.  
  30. /* Default name of the diff program */
  31. #ifndef DIFF_PROGRAM
  32. #define DIFF_PROGRAM "/usr/bin/diff"
  33. #endif
  34.  
  35. /* Users' editor of nonchoice */
  36. #ifndef DEFAULT_EDITOR_PROGRAM
  37. #define DEFAULT_EDITOR_PROGRAM "ed"
  38. #endif
  39.  
  40. extern char const version_string[];
  41. char *program_name;
  42.  
  43. static char const *diffbin = DIFF_PROGRAM;
  44. static char const *edbin = DEFAULT_EDITOR_PROGRAM;
  45. static char const **diffargv;
  46.  
  47. static char *tmpname;
  48. static int volatile tmpmade;
  49.  
  50. #if HAVE_FORK
  51. static pid_t volatile diffpid;
  52. #endif
  53.  
  54. struct line_filter;
  55.  
  56. static FILE *ck_fopen PARAMS((char const *, char const *));
  57. static RETSIGTYPE catchsig PARAMS((int));
  58. static char const *expand_name PARAMS((char *, int, char const *));
  59. static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*));
  60. static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*));
  61. static int lf_snarf PARAMS((struct line_filter *, char *, size_t));
  62. static int skip_white PARAMS((void));
  63. static size_t ck_fread PARAMS((char *, size_t, FILE *));
  64. static size_t lf_refill PARAMS((struct line_filter *));
  65. static void checksigs PARAMS((void));
  66. static void ck_fclose PARAMS((FILE *));
  67. static void ck_fflush PARAMS((FILE *));
  68. static void ck_fwrite PARAMS((char const *, size_t, FILE *));
  69. static void cleanup PARAMS((void));
  70. static void diffarg PARAMS((char const *));
  71. static void execdiff PARAMS((void));
  72. static void exiterr PARAMS((void));
  73. static void fatal PARAMS((char const *));
  74. static void flush_line PARAMS((void));
  75. static void give_help PARAMS((void));
  76. static void lf_copy PARAMS((struct line_filter *, int, FILE *));
  77. static void lf_init PARAMS((struct line_filter *, FILE *));
  78. static void lf_skip PARAMS((struct line_filter *, int));
  79. static void perror_fatal PARAMS((char const *));
  80. static void trapsigs PARAMS((void));
  81. static void try_help PARAMS((char const *));
  82. static void untrapsig PARAMS((int));
  83. static void usage PARAMS((void));
  84.  
  85. /* this lossage until the gnu libc conquers the universe */
  86. #if HAVE_TMPNAM
  87. #define private_tempnam() tmpnam ((char *) 0)
  88. #else
  89. #ifndef PVT_tmpdir
  90. #define PVT_tmpdir "/tmp"
  91. #endif
  92. #ifndef TMPDIR_ENV
  93. #define TMPDIR_ENV "TMPDIR"
  94. #endif
  95. static char *private_tempnam PARAMS((void));
  96. static int exists PARAMS((char const *));
  97. #endif
  98. static int diraccess PARAMS((char const *));
  99.  
  100. void error PARAMS((int, int, char const *, ...));
  101. VOID *xmalloc PARAMS((size_t));
  102. VOID *xrealloc PARAMS((VOID *, size_t));
  103. extern int xmalloc_exit_failure;
  104.  
  105. /* Options: */
  106.  
  107. /* name of output file if -o spec'd */
  108. static char *out_file;
  109.  
  110. /* do not print common lines if true, set by -s option */
  111. static int suppress_common_flag;
  112.  
  113. static struct option const longopts[] =
  114. {
  115.   {"ignore-blank-lines", 0, 0, 'B'},
  116.   {"speed-large-files", 0, 0, 'H'},
  117.   {"ignore-matching-lines", 1, 0, 'I'},
  118.   {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
  119.   {"text", 0, 0, 'a'},
  120.   {"ignore-space-change", 0, 0, 'b'},
  121.   {"minimal", 0, 0, 'd'},
  122.   {"ignore-case", 0, 0, 'i'},
  123.   {"left-column", 0, 0, 'l'},
  124.   {"output", 1, 0, 'o'},
  125.   {"suppress-common-lines", 0, 0, 's'},
  126.   {"expand-tabs", 0, 0, 't'},
  127.   {"width", 1, 0, 'w'},
  128.   {"version", 0, 0, 'v'},
  129.   {"help", 0, 0, 129},
  130.   {0, 0, 0, 0}
  131. };
  132.  
  133. static void
  134. try_help (reason_msgid)
  135.      char const *reason_msgid;
  136. {
  137.   if (reason_msgid)
  138.     error (0, 0, "%s", gettext (reason_msgid));
  139.   error (2, 0, gettext ("Try `%s --help' for more information."), program_name);
  140. }
  141.  
  142. static char const * const option_help_msgid[] = {
  143.   "",
  144.   "-o FILE  --output=FILE  Operate interactively, sending output to FILE.",
  145.   "",
  146.   "-i  --ignore-case  Consider upper- and lower-case to be the same.",
  147.   "-W  --ignore-all-space  Ignore all white space.",
  148.   "-b  --ignore-space-change  Ignore changes in the amount of white space.",
  149.   "-B  --ignore-blank-lines  Ignore changes whose lines are all blank.",
  150.   "-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE.",
  151.   "-a  --text  Treat all files as text.",
  152.   "",
  153.   "-w NUM  --width=NUM  Output at most NUM (default 130) characters per line.",
  154.   "-l  --left-column  Output only the left column of common lines.",
  155.   "-s  --suppress-common-lines  Do not output common lines.",
  156.   "",
  157.   "-t  --expand-tabs  Expand tabs to spaces in output.",
  158.   "",
  159.   "-d  --minimal  Try hard to find a smaller set of changes.",
  160.   "-H  --speed-large-files  Assume large files and many scattered small changes.",
  161.   "",
  162.   "-v  --version  Output version info.",
  163.   "--help  Output this help.",
  164.   "",
  165.   0
  166. };
  167.  
  168. static void
  169. usage ()
  170. {
  171.   char const * const *p;
  172.  
  173.   printf (gettext ("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name);
  174.   for (p = option_help_msgid;  *p;  p++)
  175.     if (**p)
  176.       printf ("  %s\n", gettext (*p));
  177.     else
  178.       putchar ('\n');
  179.   printf (gettext ("If a FILE is `-', read standard input.\n"));
  180. }
  181.  
  182. static void
  183. cleanup ()
  184. {
  185. #if HAVE_FORK
  186.   if (0 < diffpid)
  187.     kill (diffpid, SIGPIPE);
  188. #endif
  189.   if (tmpmade)
  190.     unlink (tmpname);
  191. }
  192.  
  193. static void
  194. exiterr ()
  195. {
  196.   cleanup ();
  197.   untrapsig (0);
  198.   checksigs ();
  199.   exit (2);
  200. }
  201.  
  202. static void
  203. fatal (msgid)
  204.      char const *msgid;
  205. {
  206.   error (0, 0, "%s", gettext (msgid));
  207.   exiterr ();
  208. }
  209.  
  210. static void
  211. perror_fatal (msg)
  212.      char const *msg;
  213. {
  214.   int e = errno;
  215.   checksigs ();
  216.   error (0, e, "%s", msg);
  217.   exiterr ();
  218. }
  219.  
  220. static FILE *
  221. ck_fopen (fname, type)
  222.      char const *fname, *type;
  223. {
  224.   FILE *r = fopen (fname, type);
  225.   if (!r)
  226.     perror_fatal (fname);
  227.   return r;
  228. }
  229.  
  230. static void
  231. ck_fclose (f)
  232.      FILE *f;
  233. {
  234.   if (fclose (f))
  235.     perror_fatal ("fclose");
  236. }
  237.  
  238. static size_t
  239. ck_fread (buf, size, f)
  240.      char *buf;
  241.      size_t size;
  242.      FILE *f;
  243. {
  244.   size_t r = fread (buf, sizeof (char), size, f);
  245.   if (r == 0 && ferror (f))
  246.     perror_fatal (gettext ("read failed"));
  247.   return r;
  248. }
  249.  
  250. static void
  251. ck_fwrite (buf, size, f)
  252.      char const *buf;
  253.      size_t size;
  254.      FILE *f;
  255. {
  256.   if (fwrite (buf, sizeof (char), size, f) != size)
  257.     perror_fatal (gettext ("write failed"));
  258. }
  259.  
  260. static void
  261. ck_fflush (f)
  262.      FILE *f;
  263. {
  264.   if (fflush (f) != 0)
  265.     perror_fatal (gettext ("write failed"));
  266. }
  267.  
  268. static char const *
  269. expand_name (name, is_dir, other_name)
  270.      char *name;
  271.      int is_dir;
  272.      char const *other_name;
  273. {
  274.   if (strcmp (name, "-") == 0)
  275.     fatal ("cannot interactively merge standard input");
  276.   if (!is_dir)
  277.     return name;
  278.   else
  279.     {
  280.       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
  281.       char const *p = filename_lastdirchar (other_name);
  282.       char const *base = p ? p+1 : other_name;
  283.       size_t namelen = strlen (name), baselen = strlen (base);
  284.       char *r = xmalloc (namelen + baselen + 2);
  285.       memcpy (r, name, namelen);
  286.       r[namelen] = '/';
  287.       memcpy (r + namelen + 1, base, baselen + 1);
  288.       return r;
  289.     }
  290. }
  291.  
  292.  
  293.  
  294. struct line_filter {
  295.   FILE *infile;
  296.   char *bufpos;
  297.   char *buffer;
  298.   char *buflim;
  299. };
  300.  
  301. static void
  302. lf_init (lf, infile)
  303.      struct line_filter *lf;
  304.      FILE *infile;
  305. {
  306.   lf->infile = infile;
  307.   lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
  308.   lf->buflim[0] = '\n';
  309. }
  310.  
  311. /* Fill an exhausted line_filter buffer from its INFILE */
  312. static size_t
  313. lf_refill (lf)
  314.      struct line_filter *lf;
  315. {
  316.   size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
  317.   lf->bufpos = lf->buffer;
  318.   lf->buflim = lf->buffer + s;
  319.   lf->buflim[0] = '\n';
  320.   checksigs ();
  321.   return s;
  322. }
  323.  
  324. /* Advance LINES on LF's infile, copying lines to OUTFILE */
  325. static void
  326. lf_copy (lf, lines, outfile)
  327.      struct line_filter *lf;
  328.      int lines;
  329.      FILE *outfile;
  330. {
  331.   char *start = lf->bufpos;
  332.  
  333.   while (lines)
  334.     {
  335.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  336.       if (! lf->bufpos)
  337.     {
  338.       ck_fwrite (start, lf->buflim - start, outfile);
  339.       if (! lf_refill (lf))
  340.         return;
  341.       start = lf->bufpos;
  342.     }
  343.       else
  344.     {
  345.       --lines;
  346.       ++lf->bufpos;
  347.     }
  348.     }
  349.  
  350.   ck_fwrite (start, lf->bufpos - start, outfile);
  351. }
  352.  
  353. /* Advance LINES on LF's infile without doing output */
  354. static void
  355. lf_skip (lf, lines)
  356.      struct line_filter *lf;
  357.      int lines;
  358. {
  359.   while (lines)
  360.     {
  361.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  362.       if (! lf->bufpos)
  363.     {
  364.       if (! lf_refill (lf))
  365.         break;
  366.     }
  367.       else
  368.     {
  369.       --lines;
  370.       ++lf->bufpos;
  371.     }
  372.     }
  373. }
  374.  
  375. /* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
  376. static int
  377. lf_snarf (lf, buffer, bufsize)
  378.      struct line_filter *lf;
  379.      char *buffer;
  380.      size_t bufsize;
  381. {
  382.   char *start = lf->bufpos;
  383.  
  384.   for (;;)
  385.     {
  386.       char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
  387.       size_t s = next - start;
  388.       if (bufsize <= s)
  389.     return 0;
  390.       memcpy (buffer, start, s);
  391.       if (next < lf->buflim)
  392.     {
  393.       buffer[s] = 0;
  394.       lf->bufpos = next + 1;
  395.       return 1;
  396.     }
  397.       if (! lf_refill (lf))
  398.     return s ? 0 : EOF;
  399.       buffer += s;
  400.       bufsize -= s;
  401.       start = next;
  402.     }
  403. }
  404.  
  405.  
  406.  
  407. int
  408. main (argc, argv)
  409.      int argc;
  410.      char *argv[];
  411. {
  412.   int opt;
  413.   char *editor;
  414.   char *differ;
  415.  
  416.   initialize_main (&argc, &argv);
  417.   setlocale (LC_ALL, "");
  418.   program_name = argv[0];
  419.   xmalloc_exit_failure = 2;
  420.  
  421.   editor = getenv ("EDITOR");
  422.   if (editor)
  423.     edbin = editor;
  424.   differ = getenv ("DIFF");
  425.   if (differ)
  426.     diffbin = differ;
  427.  
  428.   diffarg ("diff");
  429.  
  430.   /* parse command line args */
  431.   while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
  432.      != EOF)
  433.     {
  434.       switch (opt)
  435.     {
  436.     case 'a':
  437.       diffarg ("-a");
  438.       break;
  439.  
  440.     case 'b':
  441.       diffarg ("-b");
  442.       break;
  443.  
  444.     case 'B':
  445.       diffarg ("-B");
  446.       break;
  447.  
  448.     case 'd':
  449.       diffarg ("-d");
  450.       break;
  451.  
  452.     case 'H':
  453.       diffarg ("-H");
  454.       break;
  455.  
  456.     case 'i':
  457.       diffarg ("-i");
  458.       break;
  459.  
  460.     case 'I':
  461.       diffarg ("-I");
  462.       diffarg (optarg);
  463.       break;
  464.  
  465.     case 'l':
  466.       diffarg ("--left-column");
  467.       break;
  468.  
  469.     case 'o':
  470.       out_file = optarg;
  471.       break;
  472.  
  473.     case 's':
  474.       suppress_common_flag = 1;
  475.       break;
  476.  
  477.     case 't':
  478.       diffarg ("-t");
  479.       break;
  480.  
  481.     case 'v':
  482.       printf ("sdiff - %s\n", version_string);
  483.       exit (0);
  484.  
  485.     case 'w':
  486.       diffarg ("-W");
  487.       diffarg (optarg);
  488.       break;
  489.  
  490.     case 'W':
  491.       diffarg ("-w");
  492.       break;
  493.  
  494.     case 129:
  495.       usage ();
  496.       if (ferror (stdout) || fclose (stdout) != 0)
  497.         fatal ("write failed");
  498.       exit (0);
  499.  
  500.     default:
  501.       try_help (0);
  502.     }
  503.     }
  504.  
  505.   if (argc - optind != 2)
  506.     try_help (argc - optind < 2 ? "missing operand" : "extra operand");
  507.  
  508.   if (! out_file)
  509.     {
  510.       /* easy case: diff does everything for us */
  511.       if (suppress_common_flag)
  512.     diffarg ("--suppress-common-lines");
  513.       diffarg ("-y");
  514.       diffarg ("--");
  515.       diffarg (argv[optind]);
  516.       diffarg (argv[optind + 1]);
  517.       diffarg (0);
  518.       execdiff ();
  519.     }
  520.   else
  521.     {
  522.       FILE *left, *right, *out, *diffout;
  523.       int interact_ok;
  524.       struct line_filter lfilt;
  525.       struct line_filter rfilt;
  526.       struct line_filter diff_filt;
  527.       int leftdir = diraccess (argv[optind]);
  528.       int rightdir = diraccess (argv[optind + 1]);
  529.  
  530.       if (leftdir && rightdir)
  531.     fatal ("both files to be compared are directories");
  532.  
  533.       left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
  534.       ;
  535.       right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
  536.       out = ck_fopen (out_file, "w");
  537.  
  538.       diffarg ("--sdiff-merge-assist");
  539.       diffarg ("--");
  540.       diffarg (argv[optind]);
  541.       diffarg (argv[optind + 1]);
  542.       diffarg (0);
  543.  
  544.       trapsigs ();
  545.  
  546. #if ! HAVE_FORK
  547.       {
  548.     size_t cmdsize = 1;
  549.     char *p, *command;
  550.     int i;
  551.  
  552.     for (i = 0;  diffargv[i];  i++)
  553.       cmdsize += system_quote_arg ((char *) 0, diffargv[i]) + 1;
  554.     command = p = xmalloc (cmdsize);
  555.     for (i = 0;  diffargv[i];  i++)
  556.       {
  557.         p += system_quote_arg (p, diffargv[i]);
  558.         *p++ = ' ';
  559.       }
  560.     p[-1] = '\0';
  561.     diffout = popen (command, "r");
  562.     if (!diffout)
  563.       perror_fatal (command);
  564.     free (command);
  565.       }
  566. #else /* HAVE_FORK */
  567.       {
  568.     int diff_fds[2];
  569.  
  570.     if (pipe (diff_fds) != 0)
  571.       perror_fatal ("pipe");
  572.  
  573.     diffpid = vfork ();
  574.     if (diffpid < 0)
  575.       perror_fatal ("fork");
  576.     if (!diffpid)
  577.       {
  578.         signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
  579.         signal (SIGPIPE, SIG_DFL);
  580.  
  581.         close (diff_fds[0]);
  582.         if (diff_fds[1] != STDOUT_FILENO)
  583.           {
  584.         dup2 (diff_fds[1], STDOUT_FILENO);
  585.         close (diff_fds[1]);
  586.           }
  587.  
  588.         execdiff ();
  589.       }
  590.  
  591.     close (diff_fds[1]);
  592.     diffout = fdopen (diff_fds[0], "r");
  593.     if (!diffout)
  594.       perror_fatal ("fdopen");
  595.       }
  596. #endif /* HAVE_FORK */
  597.  
  598.       lf_init (&diff_filt, diffout);
  599.       lf_init (&lfilt, left);
  600.       lf_init (&rfilt, right);
  601.  
  602.       interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
  603.  
  604.       ck_fclose (left);
  605.       ck_fclose (right);
  606.       ck_fclose (out);
  607.  
  608.       {
  609.     int wstatus;
  610.  
  611. #if ! HAVE_FORK
  612.     wstatus = pclose (diffout);
  613. #else
  614.     ck_fclose (diffout);
  615.     while (waitpid (diffpid, &wstatus, 0) < 0)
  616.       if (errno == EINTR)
  617.         checksigs ();
  618.       else
  619.         perror_fatal ("waitpid");
  620.     diffpid = 0;
  621. #endif
  622.  
  623.     if (tmpmade)
  624.       {
  625.         unlink (tmpname);
  626.         tmpmade = 0;
  627.       }
  628.  
  629.     if (! interact_ok)
  630.       exiterr ();
  631.  
  632.     if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
  633.       fatal ("subsidiary program failed");
  634.  
  635.     untrapsig (0);
  636.     checksigs ();
  637.     exit (WEXITSTATUS (wstatus));
  638.       }
  639.     }
  640.   return 0;            /* Fool `-Wall'.  */
  641. }
  642.  
  643. static void
  644. diffarg (a)
  645.      char const *a;
  646. {
  647.   static unsigned diffargs, diffarglim;
  648.  
  649.   if (diffargs == diffarglim)
  650.     {
  651.       diffarglim = diffarglim ? 2 * diffarglim : 16;
  652.       diffargv = (char const **) xrealloc (diffargv,
  653.                        diffarglim * sizeof (char const *));
  654.     }
  655.   diffargv[diffargs++] = a;
  656. }
  657.  
  658. static void
  659. execdiff ()
  660. {
  661.   execvp (diffbin, (char **) diffargv);
  662.   write (STDERR_FILENO, diffbin, strlen (diffbin));
  663.   write (STDERR_FILENO, ": not found\n", 12);
  664.   _exit (2);
  665. }
  666.  
  667.  
  668.  
  669.  
  670. /* Signal handling */
  671.  
  672. #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
  673. static int const sigs[] = {
  674. #ifdef SIGHUP
  675.        SIGHUP,
  676. #endif
  677. #ifdef SIGQUIT
  678.        SIGQUIT,
  679. #endif
  680. #ifdef SIGTERM
  681.        SIGTERM,
  682. #endif
  683. #ifdef SIGXCPU
  684.        SIGXCPU,
  685. #endif
  686. #ifdef SIGXFSZ
  687.        SIGXFSZ,
  688. #endif
  689.        SIGINT,
  690. #ifdef SIGPIPE
  691.        SIGPIPE,
  692. #endif
  693. };
  694.  
  695. /* Prefer `sigaction' if it is available, since `signal' can lose signals.  */
  696. #if HAVE_SIGACTION
  697. static struct sigaction initial_action[NUM_SIGS];
  698. #define initial_handler(i) (initial_action[i].sa_handler)
  699. #else
  700. static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
  701. #define initial_handler(i) (initial_action[i])
  702. #endif
  703.  
  704. static int volatile ignore_SIGINT;
  705. static int volatile signal_received;
  706. static int sigs_trapped;
  707.  
  708. static RETSIGTYPE
  709. catchsig (s)
  710.      int s;
  711. {
  712. #if ! HAVE_SIGACTION
  713.   signal (s, SIG_IGN);
  714. #endif
  715.   if (! (s == SIGINT && ignore_SIGINT))
  716.     signal_received = s;
  717. }
  718.  
  719. static void
  720. trapsigs ()
  721. {
  722.   int i;
  723.  
  724. #if HAVE_SIGACTION
  725.   struct sigaction catchaction;
  726.   bzero (&catchaction, sizeof (catchaction));
  727.   catchaction.sa_handler = catchsig;
  728. #ifdef SA_INTERRUPT
  729.   /* Non-Posix BSD-style systems like SunOS 4.1.x need this
  730.      so that `read' calls are interrupted properly.  */
  731.   catchaction.sa_flags = SA_INTERRUPT;
  732. #endif
  733.   sigemptyset (&catchaction.sa_mask);
  734.   for (i = 0;  i < NUM_SIGS;  i++)
  735.     sigaddset (&catchaction.sa_mask, sigs[i]);
  736.   for (i = 0;  i < NUM_SIGS;  i++)
  737.     {
  738.       sigaction (sigs[i], 0, &initial_action[i]);
  739.       if (initial_handler (i) != SIG_IGN)
  740.     sigaction (sigs[i], &catchaction, 0);
  741.     }
  742. #else /* ! HAVE_SIGACTION */
  743.   for (i = 0;  i < NUM_SIGS;  i++)
  744.     {
  745.       initial_action[i] = signal (sigs[i], SIG_IGN);
  746.       if (initial_action[i] != SIG_IGN)
  747.     signal (sigs[i], catchsig);
  748.     }
  749. #endif /* ! HAVE_SIGACTION */
  750.  
  751. #if !defined (SIGCHLD) && defined (SIGCLD)
  752. #define SIGCHLD SIGCLD
  753. #endif
  754. #ifdef SIGCHLD
  755.   /* System V fork+wait does not work if SIGCHLD is ignored.  */
  756.   signal (SIGCHLD, SIG_DFL);
  757. #endif
  758.  
  759.   sigs_trapped = 1;
  760. }
  761.  
  762. /* Untrap signal S, or all trapped signals if S is zero.  */
  763. static void
  764. untrapsig (s)
  765.      int s;
  766. {
  767.   int i;
  768.  
  769.   if (sigs_trapped)
  770.     for (i = 0;  i < NUM_SIGS;  i++)
  771.       if ((!s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
  772. #if HAVE_SIGACTION
  773.       sigaction (sigs[i], &initial_action[i], 0);
  774. #else
  775.       signal (sigs[i], initial_action[i]);
  776. #endif
  777. }
  778.  
  779. /* Exit if a signal has been received.  */
  780. static void
  781. checksigs ()
  782. {
  783.   int s = signal_received;
  784.   if (s)
  785.     {
  786.       cleanup ();
  787.  
  788. #if 0
  789.       /* Yield an exit status indicating that a signal was received.  */
  790.       untrapsig (s);
  791.       kill (getpid (), s);
  792. #endif
  793.  
  794.       /* That didn't work, so exit with error status.  */
  795.       exit (2);
  796.     }
  797. }
  798.  
  799.  
  800. static char const * const help_msgid[] = {
  801.   "l:\tuse the left version",
  802.   "r:\tuse the right version",
  803.   "e l:\tedit then use the left version",
  804.   "e r:\tedit then use the right version",
  805.   "e b:\tedit then use the left and right versions concatenated",
  806.   "e:\tedit a new version",
  807.   "s:\tsilently include common lines",
  808.   "v:\tverbosely include common lines",
  809.   "q:\tquit",
  810.   0
  811. };
  812.  
  813. static void
  814. give_help ()
  815. {
  816.   char const * const *p;
  817.  
  818.   for (p = help_msgid;  *p;  p++)
  819.     fprintf (stderr, "%s\n", gettext (*p));
  820. }
  821.  
  822. static int
  823. skip_white ()
  824. {
  825.   int c;
  826.   for (;;)
  827.     {
  828.       c = getchar ();
  829.       if (!ISSPACE (c) || c == '\n')
  830.     break;
  831.       checksigs ();
  832.     }
  833.   if (ferror (stdin))
  834.     perror_fatal (gettext ("read failed"));
  835.   return c;
  836. }
  837.  
  838. static void
  839. flush_line ()
  840. {
  841.   int c;
  842.   while ((c = getchar ()) != '\n' && c != EOF)
  843.     ;
  844.   if (ferror (stdin))
  845.     perror_fatal (gettext ("read failed"));
  846. }
  847.  
  848.  
  849. /* interpret an edit command */
  850. static int
  851. edit (left, lenl, right, lenr, outfile)
  852.      struct line_filter *left;
  853.      int lenl;
  854.      struct line_filter *right;
  855.      int lenr;
  856.      FILE *outfile;
  857. {
  858.   for (;;)
  859.     {
  860.       int cmd0, cmd1;
  861.       int gotcmd = 0;
  862.  
  863.       cmd1 = 0; /* Pacify `gcc -W'.  */
  864.  
  865.       while (!gotcmd)
  866.     {
  867.       if (putchar ('%') != '%')
  868.         perror_fatal (gettext ("write failed"));
  869.       ck_fflush (stdout);
  870.  
  871.       cmd0 = skip_white ();
  872.       switch (cmd0)
  873.         {
  874.         case 'l': case 'r': case 's': case 'v': case 'q':
  875.           if (skip_white () != '\n')
  876.         {
  877.           give_help ();
  878.           flush_line ();
  879.           continue;
  880.         }
  881.           gotcmd = 1;
  882.           break;
  883.  
  884.         case 'e':
  885.           cmd1 = skip_white ();
  886.           switch (cmd1)
  887.         {
  888.         case 'l': case 'r': case 'b':
  889.           if (skip_white () != '\n')
  890.             {
  891.               give_help ();
  892.               flush_line ();
  893.               continue;
  894.             }
  895.           gotcmd = 1;
  896.           break;
  897.         case '\n':
  898.           gotcmd = 1;
  899.           break;
  900.         default:
  901.           give_help ();
  902.           flush_line ();
  903.           continue;
  904.         }
  905.           break;
  906.  
  907.         case EOF:
  908.           if (feof (stdin))
  909.         {
  910.           gotcmd = 1;
  911.           cmd0 = 'q';
  912.           break;
  913.         }
  914.           /* Fall through.  */
  915.         default:
  916.           flush_line ();
  917.           /* Fall through.  */
  918.         case '\n':
  919.           give_help ();
  920.           continue;
  921.         }
  922.     }
  923.  
  924.       switch (cmd0)
  925.     {
  926.     case 'l':
  927.       lf_copy (left, lenl, outfile);
  928.       lf_skip (right, lenr);
  929.       return 1;
  930.     case 'r':
  931.       lf_copy (right, lenr, outfile);
  932.       lf_skip (left, lenl);
  933.       return 1;
  934.     case 's':
  935.       suppress_common_flag = 1;
  936.       break;
  937.     case 'v':
  938.       suppress_common_flag = 0;
  939.       break;
  940.     case 'q':
  941.       return 0;
  942.     case 'e':
  943.       if (! tmpname && ! (tmpname = private_tempnam ()))
  944.         perror_fatal ("tmpnam");
  945.  
  946.       tmpmade = 1;
  947.  
  948.       {
  949.         FILE *tmp = ck_fopen (tmpname, "w+");
  950.  
  951.         if (cmd1 == 'l' || cmd1 == 'b')
  952.           lf_copy (left, lenl, tmp);
  953.         else
  954.           lf_skip (left, lenl);
  955.  
  956.         if (cmd1 == 'r' || cmd1 == 'b')
  957.           lf_copy (right, lenr, tmp);
  958.         else
  959.           lf_skip (right, lenr);
  960.  
  961.         ck_fflush (tmp);
  962.  
  963.         {
  964.           int wstatus;
  965. #if ! HAVE_FORK
  966.           char *command = xmalloc (strlen (edbin) + strlen (tmpname) + 2);
  967.           sprintf (command, "%s %s", edbin, tmpname);
  968.           wstatus = system (command);
  969.           free (command);
  970. #else /* HAVE_FORK */
  971.           pid_t pid;
  972.  
  973.           ignore_SIGINT = 1;
  974.           checksigs ();
  975.  
  976.           pid = vfork ();
  977.           if (pid == 0)
  978.         {
  979.           char const *argv[3];
  980.           int i = 0;
  981.  
  982.           argv[i++] = edbin;
  983.           argv[i++] = tmpname;
  984.           argv[i++] = 0;
  985.  
  986.           execvp (edbin, (char **) argv);
  987.           write (STDERR_FILENO, edbin, strlen (edbin));
  988.           write (STDERR_FILENO, ": not found\n", 12);
  989.           _exit (1);
  990.         }
  991.  
  992.           if (pid < 0)
  993.         perror_fatal ("fork");
  994.  
  995.           while (waitpid (pid, &wstatus, 0) < 0)
  996.         if (errno == EINTR)
  997.           checksigs ();
  998.         else
  999.           perror_fatal ("waitpid");
  1000.  
  1001.           ignore_SIGINT = 0;
  1002. #endif /* HAVE_FORK */
  1003.  
  1004.           if (wstatus != 0)
  1005.         fatal ("subsidiary program failed");
  1006.         }
  1007.  
  1008.         if (fseek (tmp, 0L, SEEK_SET) != 0)
  1009.           perror_fatal ("fseek");
  1010.         {
  1011.           /* SDIFF_BUFSIZE is too big for a local var
  1012.          in some compilers, so we allocate it dynamically.  */
  1013.           char *buf = xmalloc (SDIFF_BUFSIZE);
  1014.           size_t size;
  1015.  
  1016.           while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
  1017.         {
  1018.           checksigs ();
  1019.           ck_fwrite (buf, size, outfile);
  1020.         }
  1021.           ck_fclose (tmp);
  1022.  
  1023.           free (buf);
  1024.         }
  1025.         return 1;
  1026.       }
  1027.     default:
  1028.       give_help ();
  1029.       break;
  1030.     }
  1031.     }
  1032. }
  1033.  
  1034.  
  1035.  
  1036. /* Alternately reveal bursts of diff output and handle user commands.  */
  1037. static int
  1038. interact (diff, left, right, outfile)
  1039.      struct line_filter *diff;
  1040.      struct line_filter *left;
  1041.      struct line_filter *right;
  1042.      FILE *outfile;
  1043. {
  1044.   for (;;)
  1045.     {
  1046.       char diff_help[256];
  1047.       int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
  1048.  
  1049.       if (snarfed <= 0)
  1050.     return snarfed;
  1051.  
  1052.       checksigs ();
  1053.  
  1054.       switch (diff_help[0])
  1055.     {
  1056.     case ' ':
  1057.       puts (diff_help + 1);
  1058.       break;
  1059.     case 'i':
  1060.       {
  1061.         int lenl = atoi (diff_help + 1), lenr, lenmax;
  1062.         char *p = strchr (diff_help, ',');
  1063.  
  1064.         if (!p)
  1065.           fatal (diff_help);
  1066.         lenr = atoi (p + 1);
  1067.         lenmax = max (lenl, lenr);
  1068.  
  1069.         if (suppress_common_flag)
  1070.           lf_skip (diff, lenmax);
  1071.         else
  1072.           lf_copy (diff, lenmax, stdout);
  1073.  
  1074.         lf_copy (left, lenl, outfile);
  1075.         lf_skip (right, lenr);
  1076.         break;
  1077.       }
  1078.     case 'c':
  1079.       {
  1080.         int lenl = atoi (diff_help + 1), lenr;
  1081.         char *p = strchr (diff_help, ',');
  1082.  
  1083.         if (!p)
  1084.           fatal (diff_help);
  1085.         lenr = atoi (p + 1);
  1086.         lf_copy (diff, max (lenl, lenr), stdout);
  1087.         if (! edit (left, lenl, right, lenr, outfile))
  1088.           return 0;
  1089.         break;
  1090.       }
  1091.     default:
  1092.       fatal (diff_help);
  1093.       break;
  1094.     }
  1095.     }
  1096. }
  1097.  
  1098.  
  1099.  
  1100. /* temporary lossage: this is torn from gnu libc */
  1101. /* Return nonzero if DIR is an existing directory.  */
  1102. static int
  1103. diraccess (dir)
  1104.      char const *dir;
  1105. {
  1106.   struct stat buf;
  1107.   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
  1108. }
  1109.  
  1110. #if ! HAVE_TMPNAM
  1111.  
  1112. /* Return zero if we know that FILE does not exist.  */
  1113. static int
  1114. exists (file)
  1115.      char const *file;
  1116. {
  1117.   struct stat buf;
  1118.   return stat (file, &buf) == 0 || errno != ENOENT;
  1119. }
  1120.  
  1121. /* These are the characters used in temporary filenames.  */
  1122. static char const letters[] =
  1123.   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  1124.  
  1125. /* Generate a temporary filename and return it (in a newly allocated buffer).
  1126.    Use the prefix "dif" as in tempnam.
  1127.    This goes through a cyclic pattern of all possible
  1128.    filenames consisting of five decimal digits of the current pid and three
  1129.    of the characters in `letters'.  Each potential filename is
  1130.    tested for an already-existing file of the same name, and no name of an
  1131.    existing file will be returned.  When the cycle reaches its end
  1132.    return 0.  */
  1133. static char *
  1134. private_tempnam ()
  1135. {
  1136.   char const *dir = getenv (TMPDIR_ENV);
  1137.   static char const tmpdir[] = PVT_tmpdir;
  1138.   size_t index;
  1139.   char *buf;
  1140.   pid_t pid = getpid ();
  1141.   size_t dlen;
  1142.  
  1143.   if (!dir)
  1144.     dir = tmpdir;
  1145.  
  1146.   dlen = strlen (dir);
  1147.  
  1148.   /* Remove trailing slashes from the directory name.  */
  1149.   while (dlen && dir[dlen - 1] == '/')
  1150.     --dlen;
  1151.  
  1152.   buf = xmalloc (dlen + 1 + 3 + 5 + 1 + 3 + 1);
  1153.  
  1154.   sprintf (buf, "%.*s/.", (int) dlen, dir);
  1155.   if (diraccess (buf))
  1156.     {
  1157.       for (index = 0;
  1158.        index < ((sizeof (letters) - 1) * (sizeof (letters) - 1)
  1159.             * (sizeof (letters) - 1));
  1160.        ++index)
  1161.     {
  1162.       /* Construct a file name and see if it already exists.
  1163.  
  1164.          We use a single counter in INDEX to cycle each of three
  1165.          character positions through each of 62 possible letters.  */
  1166.  
  1167.       sprintf (buf, "%.*s/dif%.5lu.%c%c%c", (int) dlen, dir,
  1168.            (unsigned long) pid % 100000,
  1169.            letters[index % (sizeof (letters) - 1)],
  1170.            letters[(index / (sizeof (letters) - 1))
  1171.                % (sizeof (letters) - 1)],
  1172.            letters[index / ((sizeof (letters) - 1) *
  1173.                      (sizeof (letters) - 1))]);
  1174.  
  1175.       if (!exists (buf))
  1176.         return buf;
  1177.     }
  1178.       errno = EEXIST;
  1179.     }
  1180.  
  1181.   /* Don't free buf; `free' might change errno.  We'll exit soon anyway.  */
  1182.   return 0;
  1183. }
  1184.  
  1185. #endif /* ! HAVE_TMPNAM */
  1186.