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