]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/rcscmds.c
This commit was generated by cvs2svn to compensate for changes in r163799,
[FreeBSD/FreeBSD.git] / contrib / cvs / src / rcscmds.c
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  * Copyright (c) 1989-1992, Brian Berliner
4  * 
5  * You may distribute under the terms of the GNU General Public License as
6  * specified in the README file that comes with the CVS source distribution.
7  * 
8  * The functions in this file provide an interface for performing 
9  * operations directly on RCS files. 
10  *
11  * $FreeBSD$
12  */
13
14 #include "cvs.h"
15 #include <assert.h>
16 #include <stdio.h>
17 #include "diffrun.h"
18
19 /* This file, rcs.h, and rcs.c, together sometimes known as the "RCS
20    library", are intended to define our interface to RCS files.
21
22    Whether there will also be a version of RCS which uses this
23    library, or whether the library will be packaged for uses beyond
24    CVS or RCS (many people would like such a thing) is an open
25    question.  Some considerations:
26
27    1.  An RCS library for CVS must have the capabilities of the
28    existing CVS code which accesses RCS files.  In particular, simple
29    approaches will often be slow.
30
31    2.  An RCS library should not use code from the current RCS
32    (5.7 and its ancestors).  The code has many problems.  Too few
33    comments, too many layers of abstraction, too many global variables
34    (the correct number for a library is zero), too much intricately
35    interwoven functionality, and too many clever hacks.  Paul Eggert,
36    the current RCS maintainer, agrees.
37
38    3.  More work needs to be done in terms of separating out the RCS
39    library from the rest of CVS (for example, cvs_output should be
40    replaced by a callback, and the declarations should be centralized
41    into rcs.h, and probably other such cleanups).
42
43    4.  To be useful for RCS and perhaps for other uses, the library
44    may need features beyond those needed by CVS.
45
46    5.  Any changes to the RCS file format *must* be compatible.  Many,
47    many tools (not just CVS and RCS) can at least import this format.
48    RCS and CVS must preserve the current ability to import/export it
49    (preferably improved--magic branches are currently a roadblock).
50    See doc/RCSFILES in the CVS distribution for documentation of this
51    file format.
52
53    On a related note, see the comments at diff_exec, later in this file,
54    for more on the diff library.  */
55
56 static void RCS_output_diff_options PROTO ((const char *, const char *,
57                                             const char *, const char *));
58
59
60 /* Stuff to deal with passing arguments the way libdiff.a wants to deal
61    with them.  This is a crufty interface; there is no good reason for it
62    to resemble a command line rather than something closer to "struct
63    log_data" in log.c.  */
64
65 /* First call call_diff_setup to setup any initial arguments.  The
66    argument will be parsed into whitespace separated words and added
67    to the global call_diff_argv list.
68
69    Then, optionally, call call_diff_arg for each additional argument
70    that you'd like to pass to the diff library.
71
72    Finally, call call_diff or call_diff3 to produce the diffs.  */
73
74 static char **call_diff_argv;
75 static int call_diff_argc;
76 static int call_diff_argc_allocated;
77
78 static void call_diff_add_arg PROTO ((const char *));
79 static void call_diff_setup PROTO ((const char *prog));
80 static int call_diff PROTO ((const char *out));
81 static int call_diff3 PROTO ((char *out));
82
83 static void call_diff_write_output PROTO((const char *, size_t));
84 static void call_diff_flush_output PROTO((void));
85 static void call_diff_write_stdout PROTO((const char *));
86 static void call_diff_error PROTO((const char *, const char *, const char *));
87
88 /* VARARGS */
89 static void 
90 call_diff_setup (prog)
91     const char *prog;
92 {
93     char *cp;
94     int i;
95     char *call_diff_prog;
96
97     /* clean out any malloc'ed values from call_diff_argv */
98     for (i = 0; i < call_diff_argc; i++)
99     {
100         if (call_diff_argv[i])
101         {
102             free (call_diff_argv[i]);
103             call_diff_argv[i] = (char *) 0;
104         }
105     }
106     call_diff_argc = 0;
107
108     call_diff_prog = xstrdup (prog);
109
110     /* put each word into call_diff_argv, allocating it as we go */
111     for (cp = strtok (call_diff_prog, " \t");
112          cp != NULL;
113          cp = strtok ((char *) NULL, " \t"))
114         call_diff_add_arg (cp);
115     free (call_diff_prog);
116 }
117
118 static void
119 call_diff_arg (s)
120     const char *s;
121 {
122     call_diff_add_arg (s);
123 }
124
125 static void
126 call_diff_add_arg (s)
127     const char *s;
128 {
129     /* allocate more argv entries if we've run out */
130     if (call_diff_argc >= call_diff_argc_allocated)
131     {
132         call_diff_argc_allocated += 50;
133         call_diff_argv = (char **)
134             xrealloc ((char *) call_diff_argv,
135                       call_diff_argc_allocated * sizeof (char **));
136     }
137
138     if (s)
139         call_diff_argv[call_diff_argc++] = xstrdup (s);
140     else
141         /* Not post-incremented on purpose!  */
142         call_diff_argv[call_diff_argc] = (char *) 0;
143 }
144
145 /* Callback function for the diff library to write data to the output
146    file.  This is used when we are producing output to stdout.  */
147
148 static void
149 call_diff_write_output (text, len)
150     const char *text;
151     size_t len;
152 {
153     if (len > 0)
154         cvs_output (text, len);
155 }
156
157 /* Call back function for the diff library to flush the output file.
158    This is used when we are producing output to stdout.  */
159
160 static void
161 call_diff_flush_output ()
162 {
163     cvs_flushout ();
164 }
165
166 /* Call back function for the diff library to write to stdout.  */
167
168 static void
169 call_diff_write_stdout (text)
170     const char *text;
171 {
172     cvs_output (text, 0);
173 }
174
175 /* Call back function for the diff library to write to stderr.  */
176
177 static void
178 call_diff_error (format, a1, a2)
179     const char *format;
180     const char *a1;
181     const char *a2;
182 {
183     /* FIXME: Should we somehow indicate that this error is coming from
184        the diff library?  */
185     error (0, 0, format, a1, a2);
186 }
187
188 /* This set of callback functions is used if we are sending the diff
189    to stdout.  */
190
191 static struct diff_callbacks call_diff_stdout_callbacks =
192 {
193     call_diff_write_output,
194     call_diff_flush_output,
195     call_diff_write_stdout,
196     call_diff_error
197 };
198
199 /* This set of callback functions is used if we are sending the diff
200    to a file.  */
201
202 static struct diff_callbacks call_diff_file_callbacks =
203 {
204     (void (*) PROTO((const char *, size_t))) NULL,
205     (void (*) PROTO((void))) NULL,
206     call_diff_write_stdout,
207     call_diff_error
208 };
209
210
211
212 static int
213 call_diff (out)
214     const char *out;
215 {
216     if (out == RUN_TTY)
217         return diff_run (call_diff_argc, call_diff_argv, NULL,
218                          &call_diff_stdout_callbacks);
219     else
220         return diff_run (call_diff_argc, call_diff_argv, out,
221                          &call_diff_file_callbacks);
222 }
223
224
225
226 static int
227 call_diff3 (out)
228     char *out;
229 {
230     if (out == RUN_TTY)
231         return diff3_run (call_diff_argc, call_diff_argv, NULL,
232                           &call_diff_stdout_callbacks);
233     else
234         return diff3_run (call_diff_argc, call_diff_argv, out,
235                           &call_diff_file_callbacks);
236 }
237
238
239
240 /* Merge revisions REV1 and REV2. */
241
242 int
243 RCS_merge(rcs, path, workfile, options, rev1, rev2)
244     RCSNode *rcs;
245     const char *path;
246     const char *workfile;
247     const char *options;
248     const char *rev1;
249     const char *rev2;
250 {
251     char *xrev1, *xrev2;
252     char *tmp1, *tmp2;
253     char *diffout = NULL;
254     int retval;
255
256     if (options != NULL && options[0] != '\0')
257       assert (options[0] == '-' && options[1] == 'k');
258
259     cvs_output ("RCS file: ", 0);
260     cvs_output (rcs->path, 0);
261     cvs_output ("\n", 1);
262
263     /* Calculate numeric revision numbers from rev1 and rev2 (may be
264        symbolic). */
265     xrev1 = RCS_gettag (rcs, rev1, 0, NULL);
266     xrev2 = RCS_gettag (rcs, rev2, 0, NULL);
267
268     /* Check out chosen revisions.  The error message when RCS_checkout
269        fails is not very informative -- it is taken verbatim from RCS 5.7,
270        and relies on RCS_checkout saying something intelligent upon failure. */
271     cvs_output ("retrieving revision ", 0);
272     cvs_output (xrev1, 0);
273     cvs_output ("\n", 1);
274
275     tmp1 = cvs_temp_name();
276     if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1,
277                       (RCSCHECKOUTPROC)0, NULL))
278     {
279         cvs_outerr ("rcsmerge: co failed\n", 0);
280         error_exit();
281     }
282
283     cvs_output ("retrieving revision ", 0);
284     cvs_output (xrev2, 0);
285     cvs_output ("\n", 1);
286
287     tmp2 = cvs_temp_name();
288     if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2,
289                       (RCSCHECKOUTPROC)0, NULL))
290     {
291         cvs_outerr ("rcsmerge: co failed\n", 0);
292         error_exit();
293     }
294
295     /* Merge changes. */
296     cvs_output ("Merging differences between ", 0);
297     cvs_output (xrev1, 0);
298     cvs_output (" and ", 0);
299     cvs_output (xrev2, 0);
300     cvs_output (" into ", 0);
301     cvs_output (workfile, 0);
302     cvs_output ("\n", 1);
303
304     /* Remember that the first word in the `call_diff_setup' string is used now
305        only for diagnostic messages -- CVS no longer forks to run diff3. */
306     diffout = cvs_temp_name();
307     call_diff_setup ("diff3");
308     call_diff_arg ("-E");
309     call_diff_arg ("-am");
310
311     call_diff_arg ("-L");
312     call_diff_arg (workfile);
313     call_diff_arg ("-L");
314     call_diff_arg (xrev1);
315     call_diff_arg ("-L");
316     call_diff_arg (xrev2);
317
318     call_diff_arg ("--");
319     call_diff_arg (workfile);
320     call_diff_arg (tmp1);
321     call_diff_arg (tmp2);
322
323     retval = call_diff3 (diffout);
324
325     if (retval == 1)
326         cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0);
327     else if (retval == 2)
328         error_exit();
329
330     if (diffout)
331         copy_file (diffout, workfile);
332
333     /* Clean up. */
334     {
335         int save_noexec = noexec;
336         noexec = 0;
337         if (unlink_file (tmp1) < 0)
338         {
339             if (!existence_error (errno))
340                 error (0, errno, "cannot remove temp file %s", tmp1);
341         }
342         free (tmp1);
343         if (unlink_file (tmp2) < 0)
344         {
345             if (!existence_error (errno))
346                 error (0, errno, "cannot remove temp file %s", tmp2);
347         }
348         free (tmp2);
349         if (diffout)
350         {
351             if (unlink_file (diffout) < 0)
352             {
353                 if (!existence_error (errno))
354                     error (0, errno, "cannot remove temp file %s", diffout);
355             }
356             free (diffout);
357         }
358         free (xrev1);
359         free (xrev2);
360         noexec = save_noexec;
361     }
362
363     return retval;
364 }
365
366 /* Diff revisions and/or files.  OPTS controls the format of the diff
367    (it contains options such as "-w -c", &c), or "" for the default.
368    OPTIONS controls keyword expansion, as a string starting with "-k",
369    or "" to use the default.  REV1 is the first revision to compare
370    against; it must be non-NULL.  If REV2 is non-NULL, compare REV1
371    and REV2; if REV2 is NULL compare REV1 with the file in the working
372    directory, whose name is WORKFILE.  LABEL1 and LABEL2 are default
373    file labels, and (if non-NULL) should be added as -L options
374    to diff.  Output goes to stdout.
375
376    Return value is 0 for success, -1 for a failure which set errno,
377    or positive for a failure which printed a message on stderr.
378
379    This used to exec rcsdiff, but now calls RCS_checkout and diff_exec.
380
381    An issue is what timezone is used for the dates which appear in the
382    diff output.  rcsdiff uses the -z flag, which is not presently
383    processed by CVS diff, but I'm not sure exactly how hard to worry
384    about this--any such features are undocumented in the context of
385    CVS, and I'm not sure how important to users.  */
386 int
387 RCS_exec_rcsdiff(rcsfile, opts, options, rev1, rev1_cache, rev2,
388                  label1, label2, workfile )
389     RCSNode *rcsfile;
390     const char *opts;
391     const char *options;
392     const char *rev1;
393     const char *rev1_cache;
394     const char *rev2;
395     const char *label1;
396     const char *label2;
397     const char *workfile;
398 {
399     char *tmpfile1 = NULL;
400     char *tmpfile2 = NULL;
401     const char *use_file1, *use_file2;
402     int status, retval;
403
404
405     cvs_output ("\
406 ===================================================================\n\
407 RCS file: ", 0);
408     cvs_output (rcsfile->path, 0);
409     cvs_output ("\n", 1);
410
411     /* Historically, `cvs diff' has expanded the $Name keyword to the
412        empty string when checking out revisions.  This is an accident,
413        but no one has considered the issue thoroughly enough to determine
414        what the best behavior is.  Passing NULL for the `nametag' argument
415        preserves the existing behavior. */
416
417     cvs_output ("retrieving revision ", 0);
418     cvs_output (rev1, 0);
419     cvs_output ("\n", 1);
420
421     if (rev1_cache != NULL)
422         use_file1 = rev1_cache;
423     else
424     {
425         tmpfile1 = cvs_temp_name();
426         status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1,
427                                (RCSCHECKOUTPROC)0, NULL);
428         if (status > 0)
429         {
430             retval = status;
431             goto error_return;
432         }
433         else if (status < 0)
434         {
435             error( 0, errno,
436                    "cannot check out revision %s of %s", rev1, rcsfile->path );
437             retval = 1;
438             goto error_return;
439         }
440         use_file1 = tmpfile1;
441     }
442
443     if (rev2 == NULL)
444     {
445         assert (workfile != NULL);
446         use_file2 = workfile;
447     }
448     else
449     {
450         tmpfile2 = cvs_temp_name ();
451         cvs_output ("retrieving revision ", 0);
452         cvs_output (rev2, 0);
453         cvs_output ("\n", 1);
454         status = RCS_checkout (rcsfile, NULL, rev2, NULL, options,
455                                tmpfile2, (RCSCHECKOUTPROC)0, NULL);
456         if (status > 0)
457         {
458             retval = status;
459             goto error_return;
460         }
461         else if (status < 0)
462         {
463             error (0, errno,
464                    "cannot check out revision %s of %s", rev2, rcsfile->path);
465             return 1;
466         }
467         use_file2 = tmpfile2;
468     }
469
470     RCS_output_diff_options (opts, rev1, rev2, workfile);
471     status = diff_exec( use_file1, use_file2, label1, label2, opts, RUN_TTY );
472     if (status >= 0)
473     {
474         retval = status;
475         goto error_return;
476     }
477     else if (status < 0)
478     {
479         error (0, errno,
480                "cannot diff %s and %s", use_file1, use_file2);
481         retval = 1;
482         goto error_return;
483     }
484
485  error_return:
486     {
487         /* Call CVS_UNLINK() below rather than unlink_file to avoid the check
488          * for noexec.
489          */
490         if( tmpfile1 != NULL )
491         {
492             if( CVS_UNLINK( tmpfile1 ) < 0 )
493             {
494                 if( !existence_error( errno ) )
495                     error( 0, errno, "cannot remove temp file %s", tmpfile1 );
496             }
497             free( tmpfile1 );
498         }
499         if( tmpfile2 != NULL )
500         {
501             if( CVS_UNLINK( tmpfile2 ) < 0 )
502             {
503                 if( !existence_error( errno ) )
504                     error( 0, errno, "cannot remove temp file %s", tmpfile2 );
505             }
506             free (tmpfile2);
507         }
508     }
509
510     return retval;
511 }
512
513
514
515 /* Show differences between two files.  This is the start of a diff library.
516
517    Some issues:
518
519    * Should option parsing be part of the library or the caller?  The
520    former allows the library to add options without changing the callers,
521    but it causes various problems.  One is that something like --brief really
522    wants special handling in CVS, and probably the caller should retain
523    some flexibility in this area.  Another is online help (the library could
524    have some feature for providing help, but how does that interact with
525    the help provided by the caller directly?).  Another is that as things
526    stand currently, there is no separate namespace for diff options versus
527    "cvs diff" options like -l (that is, if the library adds an option which
528    conflicts with a CVS option, it is trouble).
529
530    * This isn't required for a first-cut diff library, but if there
531    would be a way for the caller to specify the timestamps that appear
532    in the diffs (rather than the library getting them from the files),
533    that would clean up the kludgy utime() calls in patch.c.
534
535    Show differences between FILE1 and FILE2.  Either one can be
536    DEVNULL to indicate a nonexistent file (same as an empty file
537    currently, I suspect, but that may be an issue in and of itself).
538    OPTIONS is a list of diff options, or "" if none.  At a minimum,
539    CVS expects that -c (update.c, patch.c) and -n (update.c) will be
540    supported.  Other options, like -u, --speed-large-files, &c, will
541    be specified if the user specified them.
542
543    OUT is a filename to send the diffs to, or RUN_TTY to send them to
544    stdout.  Error messages go to stderr.  Return value is 0 for
545    success, -1 for a failure which set errno, 1 for success (and some
546    differences were found), or >1 for a failure which printed a
547    message on stderr.  */
548
549 int
550 diff_exec (file1, file2, label1, label2, options, out)
551     const char *file1;
552     const char *file2;
553     const char *label1;
554     const char *label2;
555     const char *options;
556     const char *out;
557 {
558     char *args;
559
560 #ifdef PRESERVE_PERMISSIONS_SUPPORT
561     /* If either file1 or file2 are special files, pretend they are
562        /dev/null.  Reason: suppose a file that represents a block
563        special device in one revision becomes a regular file.  CVS
564        must find the `difference' between these files, but a special
565        file contains no data useful for calculating this metric.  The
566        safe thing to do is to treat the special file as an empty file,
567        thus recording the regular file's full contents.  Doing so will
568        create extremely large deltas at the point of transition
569        between device files and regular files, but this is probably
570        very rare anyway.
571
572        There may be ways around this, but I think they are fraught
573        with danger. -twp */
574
575     if (preserve_perms &&
576         strcmp (file1, DEVNULL) != 0 &&
577         strcmp (file2, DEVNULL) != 0)
578     {
579         struct stat sb1, sb2;
580
581         if (CVS_LSTAT (file1, &sb1) < 0)
582             error (1, errno, "cannot get file information for %s", file1);
583         if (CVS_LSTAT (file2, &sb2) < 0)
584             error (1, errno, "cannot get file information for %s", file2);
585
586         if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
587             file1 = DEVNULL;
588         if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
589             file2 = DEVNULL;
590     }
591 #endif
592
593     args = xmalloc (strlen (options) + 10);
594     /* The first word in this string is used only for error reporting. */
595     sprintf (args, "diff %s", options);
596     call_diff_setup (args);
597     if (label1)
598         call_diff_arg (label1);
599     if (label2)
600         call_diff_arg (label2);
601     call_diff_arg ("--");
602     call_diff_arg (file1);
603     call_diff_arg (file2);
604     free (args);
605
606     return call_diff (out);
607 }
608
609 /* Print the options passed to DIFF, in the format used by rcsdiff.
610    The rcsdiff code that produces this output is extremely hairy, and
611    it is not clear how rcsdiff decides which options to print and
612    which not to print.  The code below reproduces every rcsdiff run
613    that I have seen. */
614
615 static void
616 RCS_output_diff_options (opts, rev1, rev2, workfile)
617     const char *opts;
618     const char *rev1;
619     const char *rev2;
620     const char *workfile;
621 {
622     char *tmp;
623
624     tmp = (char *) xmalloc (strlen (opts) + strlen (rev1) + 10);
625
626     sprintf (tmp, "diff%s -r%s", opts, rev1);
627     cvs_output (tmp, 0);
628     free (tmp);
629
630     if (rev2)
631     {
632         cvs_output (" -r", 3);
633         cvs_output (rev2, 0);
634     }
635     else
636     {
637         assert (workfile != NULL);
638         cvs_output (" ", 1);
639         cvs_output (workfile, 0);
640     }
641     cvs_output ("\n", 1);
642 }