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