Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / gnu / usr.bin / rcs / rcsdiff / rcsdiff.c
1 /* Compare RCS revisions.  */
2
3 /* Copyright 1982, 1988, 1989 Walter Tichy
4    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5    Distributed under license by the Free Software Foundation, Inc.
6
7 This file is part of RCS.
8
9 RCS is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 RCS is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RCS; see the file COPYING.
21 If not, write to the Free Software Foundation,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 Report problems and direct all questions to:
25
26     rcs-bugs@cs.purdue.edu
27
28 */
29
30 /*
31  * $FreeBSD: src/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c,v 1.8 1999/08/27 23:36:55 peter Exp $
32  * $DragonFly: src/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c,v 1.2 2003/06/17 04:25:48 dillon Exp $
33  *
34  * Revision 5.19  1995/06/16 06:19:24  eggert
35  * Update FSF address.
36  *
37  * Revision 5.18  1995/06/01 16:23:43  eggert
38  * (main): Pass "--binary" if -kb and if --binary makes a difference.
39  * Don't treat + options specially.
40  *
41  * Revision 5.17  1994/03/17 14:05:48  eggert
42  * Specify subprocess input via file descriptor, not file name.  Remove lint.
43  *
44  * Revision 5.16  1993/11/09 17:40:15  eggert
45  * -V now prints version on stdout and exits.  Don't print usage twice.
46  *
47  * Revision 5.15  1993/11/03 17:42:27  eggert
48  * Add -z.  Ignore -T.  Pass -Vn to `co'.  Add Name keyword.
49  * Put revision numbers in -c output.  Improve quality of diagnostics.
50  *
51  * Revision 5.14  1992/07/28  16:12:44  eggert
52  * Add -V.  Use co -M for better dates with traditional diff -c.
53  *
54  * Revision 5.13  1992/02/17  23:02:23  eggert
55  * Output more readable context diff headers.
56  * Suppress needless checkout and comparison of identical revisions.
57  *
58  * Revision 5.12  1992/01/24  18:44:19  eggert
59  * Add GNU diff 1.15.2's new options.  lint -> RCS_lint
60  *
61  * Revision 5.11  1992/01/06  02:42:34  eggert
62  * Update usage string.
63  *
64  * Revision 5.10  1991/10/07  17:32:46  eggert
65  * Remove lint.
66  *
67  * Revision 5.9  1991/08/19  03:13:55  eggert
68  * Add RCSINIT, -r$.  Tune.
69  *
70  * Revision 5.8  1991/04/21  11:58:21  eggert
71  * Add -x, RCSINIT, MS-DOS support.
72  *
73  * Revision 5.7  1990/12/13  06:54:07  eggert
74  * GNU diff 1.15 has -u.
75  *
76  * Revision 5.6  1990/11/01  05:03:39  eggert
77  * Remove unneeded setid check.
78  *
79  * Revision 5.5  1990/10/04  06:30:19  eggert
80  * Accumulate exit status across files.
81  *
82  * Revision 5.4  1990/09/27  01:31:43  eggert
83  * Yield 1, not EXIT_FAILURE, when diffs are found.
84  *
85  * Revision 5.3  1990/09/11  02:41:11  eggert
86  * Simplify -kkvl test.
87  *
88  * Revision 5.2  1990/09/04  17:07:19  eggert
89  * Diff's argv was too small by 1.
90  *
91  * Revision 5.1  1990/08/29  07:13:55  eggert
92  * Add -kkvl.
93  *
94  * Revision 5.0  1990/08/22  08:12:46  eggert
95  * Add -k, -V.  Don't use access().  Add setuid support.
96  * Remove compile-time limits; use malloc instead.
97  * Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options.
98  * Add GNU diff's flags.  Make lock and temp files faster and safer.
99  * Ansify and Posixate.
100  *
101  * Revision 4.6  89/05/01  15:12:27  narten
102  * changed copyright header to reflect current distribution rules
103  *
104  * Revision 4.5  88/08/09  19:12:41  eggert
105  * Use execv(), not system(); yield exit status like diff(1)s; allow cc -R.
106  *
107  * Revision 4.4  87/12/18  11:37:46  narten
108  * changes Jay Lepreau made in the 4.3 BSD version, to add support for
109  * "-i", "-w", and "-t" flags and to permit flags to be bundled together,
110  * merged in.
111  *
112  * Revision 4.3  87/10/18  10:31:42  narten
113  * Updating version numbers. Changes relative to 1.1 actually
114  * relative to 4.1
115  *
116  * Revision 1.3  87/09/24  13:59:21  narten
117  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
118  * warnings)
119  *
120  * Revision 1.2  87/03/27  14:22:15  jenkins
121  * Port to suns
122  *
123  * Revision 4.1  83/05/03  22:13:19  wft
124  * Added default branch, option -q, exit status like diff.
125  * Added fterror() to replace faterror().
126  *
127  * Revision 3.6  83/01/15  17:52:40  wft
128  * Expanded mainprogram to handle multiple RCS files.
129  *
130  * Revision 3.5  83/01/06  09:33:45  wft
131  * Fixed passing of -c (context) option to diff.
132  *
133  * Revision 3.4  82/12/24  15:28:38  wft
134  * Added call to catchsig().
135  *
136  * Revision 3.3  82/12/10  16:08:17  wft
137  * Corrected checking of return code from diff; improved error msgs.
138  *
139  * Revision 3.2  82/12/04  13:20:09  wft
140  * replaced getdelta() with gettree(). Changed diagnostics.
141  *
142  * Revision 3.1  82/11/28  19:25:04  wft
143  * Initial revision.
144  *
145  */
146 #include "rcsbase.h"
147
148 #if DIFF_L
149 static char const *setup_label P((struct buf*,char const*,char const[datesize]));
150 #endif
151 static void cleanup P((void));
152
153 static int exitstatus;
154 static RILE *workptr;
155 static struct stat workstat;
156
157 mainProg(rcsdiffId, "rcsdiff", "$DragonFly: src/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c,v 1.2 2003/06/17 04:25:48 dillon Exp $")
158 {
159     static char const cmdusage[] =
160             "\nrcsdiff usage: rcsdiff -ksubst -q -rrev1 [-rrev2] -Vn -xsuff -zzone [diff options] file ...";
161
162     int  revnums;                 /* counter for revision numbers given */
163     char const *rev1, *rev2;    /* revision numbers from command line */
164     char const *xrev1, *xrev2;  /* expanded revision numbers */
165     char const *expandarg, *lexpandarg, *suffixarg, *versionarg, *zonearg;
166 #if DIFF_L
167     static struct buf labelbuf[2];
168     int file_labels;
169     char const **diff_label1, **diff_label2;
170     char date2[datesize];
171 #endif
172     char const *cov[10 + !DIFF_L];
173     char const **diffv, **diffp, **diffpend;    /* argv for subsidiary diff */
174     char const **pp, *p, *diffvstr;
175     struct buf commarg;
176     struct buf numericrev;      /* expanded revision number */
177     struct hshentries *gendeltas;       /* deltas to be generated */
178     struct hshentry * target;
179     char *a, *dcp, **newargv;
180     int no_diff_means_no_output;
181     register c;
182
183     exitstatus = DIFF_SUCCESS;
184
185     bufautobegin(&commarg);
186     bufautobegin(&numericrev);
187     revnums = 0;
188     rev1 = rev2 = xrev2 = 0;
189 #if DIFF_L
190     file_labels = 0;
191 #endif
192     expandarg = suffixarg = versionarg = zonearg = 0;
193     no_diff_means_no_output = true;
194     suffixes = X_DEFAULT;
195
196     /*
197     * Room for runv extra + args [+ --binary] [+ 2 labels]
198     * + 1 file + 1 trailing null.
199     */
200     diffv = tnalloc(char const*, 1 + argc + !!OPEN_O_BINARY + 2*DIFF_L + 2);
201     diffp = diffv + 1;
202     *diffp++ = DIFF;
203
204     argc = getRCSINIT(argc, argv, &newargv);
205     argv = newargv;
206     while (a = *++argv,  0<--argc && *a++=='-') {
207         dcp = a;
208         while ((c = *a++)) switch (c) {
209             case 'r':
210                     switch (++revnums) {
211                         case 1: rev1=a; break;
212                         case 2: rev2=a; break;
213                         default: error("too many revision numbers");
214                     }
215                     goto option_handled;
216             case '-': case 'D':
217                     no_diff_means_no_output = false;
218                     /* fall into */
219             case 'C': case 'F': case 'I': case 'L': case 'W':
220 #if DIFF_L
221                     if (c == 'L'  &&  file_labels++ == 2)
222                         faterror("too many -L options");
223 #endif
224                     *dcp++ = c;
225                     if (*a)
226                         do *dcp++ = *a++;
227                         while (*a);
228                     else {
229                         if (!--argc)
230                             faterror("-%c needs following argument%s",
231                                     c, cmdusage
232                             );
233                         *diffp++ = *argv++;
234                     }
235                     break;
236             case 'y':
237                     no_diff_means_no_output = false;
238                     /* fall into */
239             case 'B': case 'H':
240             case '0': case '1': case '2': case '3': case '4':
241             case '5': case '6': case '7': case '8': case '9':
242             case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
243             case 'h': case 'i': case 'n': case 'p':
244             case 't': case 'u': case 'w':
245                     *dcp++ = c;
246                     break;
247             case 'q':
248                     quietflag=true;
249                     break;
250             case 'x':
251                     suffixarg = *argv;
252                     suffixes = *argv + 2;
253                     goto option_handled;
254             case 'z':
255                     zonearg = *argv;
256                     zone_set(*argv + 2);
257                     goto option_handled;
258             case 'T':
259                     /* Ignore -T, so that RCSINIT can contain -T.  */
260                     if (*a)
261                             goto unknown;
262                     break;
263             case 'V':
264                     versionarg = *argv;
265                     setRCSversion(versionarg);
266                     goto option_handled;
267             case 'k':
268                     expandarg = *argv;
269                     if (0 <= str2expmode(expandarg+2))
270                         goto option_handled;
271                     /* fall into */
272             default:
273             unknown:
274                     error("unknown option: %s%s", *argv, cmdusage);
275             };
276       option_handled:
277         if (dcp != *argv+1) {
278             *dcp = 0;
279             *diffp++ = *argv;
280         }
281     } /* end of option processing */
282
283     for (pp = diffv+2, c = 0;  pp<diffp;  )
284             c += strlen(*pp++) + 1;
285     diffvstr = a = tnalloc(char, c + 1);
286     for (pp = diffv+2;  pp<diffp;  ) {
287             p = *pp++;
288             *a++ = ' ';
289             while ((*a = *p++))
290                     a++;
291     }
292     *a = 0;
293
294 #if DIFF_L
295     diff_label1 = diff_label2 = 0;
296     if (file_labels < 2) {
297             if (!file_labels)
298                     diff_label1 = diffp++;
299             diff_label2 = diffp++;
300     }
301 #endif
302     diffpend = diffp;
303
304     cov[1] = CO;
305     cov[2] = "-q";
306 #   if !DIFF_L
307         cov[3] = "-M";
308 #   endif
309
310     /* Now handle all pathnames.  */
311     if (nerror)
312         cleanup();
313     else if (argc < 1)
314         faterror("no input file%s", cmdusage);
315     else
316         for (;  0 < argc;  cleanup(), ++argv, --argc) {
317             ffree();
318
319             if (pairnames(argc, argv, rcsreadopen, true, false)  <=  0)
320                     continue;
321             diagnose("===================================================================\nRCS file: %s\n",RCSname);
322             if (!rev2) {
323                 /* Make sure work file is readable, and get its status.  */
324                 if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) {
325                     eerror(workname);
326                     continue;
327                 }
328             }
329
330
331             gettree(); /* reads in the delta tree */
332
333             if (!Head) {
334                     rcserror("no revisions present");
335                     continue;
336             }
337             if (revnums==0  ||  !*rev1)
338                     rev1  =  Dbranch ? Dbranch : Head->num;
339
340             if (!fexpandsym(rev1, &numericrev, workptr)) continue;
341             if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue;
342             xrev1=target->num;
343 #if DIFF_L
344             if (diff_label1)
345                 *diff_label1 = setup_label(&labelbuf[0], target->num, target->date);
346 #endif
347
348             lexpandarg = expandarg;
349             if (revnums==2) {
350                     if (!fexpandsym(
351                             *rev2 ? rev2  : Dbranch ? Dbranch  : Head->num,
352                             &numericrev,
353                             workptr
354                     ))
355                         continue;
356                     if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue;
357                     xrev2=target->num;
358                     if (no_diff_means_no_output  &&  xrev1 == xrev2)
359                         continue;
360             } else if (
361                         target->lockedby
362                 &&      !lexpandarg
363                 &&      Expand == KEYVAL_EXPAND
364                 &&      WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
365             )
366                     lexpandarg = "-kkvl";
367             Izclose(&workptr);
368 #if DIFF_L
369             if (diff_label2)
370                 if (revnums == 2)
371                     *diff_label2 = setup_label(&labelbuf[1], target->num, target->date);
372                 else {
373                     time2date(workstat.st_mtime, date2);
374                     *diff_label2 = setup_label(&labelbuf[1], (char*)0, date2);
375                 }
376 #endif
377
378             diagnose("retrieving revision %s\n", xrev1);
379             bufscpy(&commarg, "-p");
380             bufscat(&commarg, rev1); /* not xrev1, for $Name's sake */
381
382             pp = &cov[3 + !DIFF_L];
383             *pp++ = commarg.string;
384             if (lexpandarg) *pp++ = lexpandarg;
385             if (suffixarg) *pp++ = suffixarg;
386             if (versionarg) *pp++ = versionarg;
387             if (zonearg) *pp++ = zonearg;
388             *pp++ = RCSname;
389             *pp = 0;
390
391             diffp = diffpend;
392 #           if OPEN_O_BINARY
393                     if (Expand == BINARY_EXPAND)
394                             *diffp++ = "--binary";
395 #           endif
396             diffp[0] = maketemp(0);
397             if (runv(-1, diffp[0], cov)) {
398                     rcserror("co failed");
399                     continue;
400             }
401             if (!rev2) {
402                     diffp[1] = workname;
403                     if (*workname == '-') {
404                         char *dp = ftnalloc(char, strlen(workname)+3);
405                         diffp[1] = dp;
406                         *dp++ = '.';
407                         *dp++ = SLASH;
408                         VOID strcpy(dp, workname);
409                     }
410             } else {
411                     diagnose("retrieving revision %s\n",xrev2);
412                     bufscpy(&commarg, "-p");
413                     bufscat(&commarg, rev2); /* not xrev2, for $Name's sake */
414                     cov[3 + !DIFF_L] = commarg.string;
415                     diffp[1] = maketemp(1);
416                     if (runv(-1, diffp[1], cov)) {
417                             rcserror("co failed");
418                             continue;
419                     }
420             }
421             if (!rev2)
422                     diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workname);
423             else
424                     diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2);
425
426             diffp[2] = 0;
427             switch (runv(-1, (char*)0, diffv)) {
428                     case DIFF_SUCCESS:
429                             break;
430                     case DIFF_FAILURE:
431                             if (exitstatus == DIFF_SUCCESS)
432                                     exitstatus = DIFF_FAILURE;
433                             break;
434                     default:
435                             workerror("diff failed");
436             }
437         }
438
439     tempunlink();
440     exitmain(exitstatus);
441 }
442
443     static void
444 cleanup()
445 {
446     if (nerror) exitstatus = DIFF_TROUBLE;
447     Izclose(&finptr);
448     Izclose(&workptr);
449 }
450
451 #if RCS_lint
452 #       define exiterr rdiffExit
453 #endif
454     void
455 exiterr()
456 {
457     tempunlink();
458     _exit(DIFF_TROUBLE);
459 }
460
461 #if DIFF_L
462         static char const *
463 setup_label(b, num, date)
464         struct buf *b;
465         char const *num;
466         char const date[datesize];
467 {
468         char *p;
469         char datestr[datesize + zonelenmax];
470         VOID date2str(date, datestr);
471         bufalloc(b,
472                 strlen(workname)
473                 + sizeof datestr + 4
474                 + (num ? strlen(num) : 0)
475         );
476         p = b->string;
477         if (num)
478                 VOID sprintf(p, "-L%s\t%s\t%s", workname, datestr, num);
479         else
480                 VOID sprintf(p, "-L%s\t%s", workname, datestr);
481         return p;
482 }
483 #endif