home *** CD-ROM | disk | FTP | other *** search
/ ftp.mactech.com 2010 / ftp.mactech.com.tar / ftp.mactech.com / online / source / c / compilers / DiffUtils.sit.hqx / DiffUtils / src / sdiff.c < prev    next >
Text File  |  1993-11-15  |  25KB  |  1,205 lines

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