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