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 #include <sys/cdefs.h>
17 __RCSID("$NetBSD: rcscmds.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
18
19 #include "cvs.h"
20 #include <stdio.h>
21 #include "diffrun.h"
22 #include "quotearg.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 (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_arg_allocated;
82
83 static int call_diff (const char *out);
84 static int call_diff3 (char *out);
85
86 static void call_diff_write_output (const char *, size_t);
87 static void call_diff_flush_output (void);
88 static void call_diff_write_stdout (const char *);
89 static void call_diff_error (const char *, const char *, const char *);
90
91
92
93 /* VARARGS */
94 static void
call_diff_add_arg(const char * s)95 call_diff_add_arg (const char *s)
96 {
97 TRACE (TRACE_DATA, "call_diff_add_arg (%s)", s);
98 run_add_arg_p (&call_diff_argc, &call_diff_arg_allocated, &call_diff_argv,
99 s);
100 }
101
102
103
104 static void
call_diff_setup(const char * prog,int argc,char * const * argv)105 call_diff_setup (const char *prog, int argc, char * const *argv)
106 {
107 int i;
108
109 /* clean out any malloc'ed values from call_diff_argv */
110 run_arg_free_p (call_diff_argc, call_diff_argv);
111 call_diff_argc = 0;
112
113 /* put each word into call_diff_argv, allocating it as we go */
114 call_diff_add_arg (prog);
115 for (i = 0; i < argc; i++)
116 call_diff_add_arg (argv[i]);
117 }
118
119
120
121 /* Callback function for the diff library to write data to the output
122 file. This is used when we are producing output to stdout. */
123
124 static void
call_diff_write_output(const char * text,size_t len)125 call_diff_write_output (const char *text, size_t len)
126 {
127 if (len > 0)
128 cvs_output (text, len);
129 }
130
131 /* Call back function for the diff library to flush the output file.
132 This is used when we are producing output to stdout. */
133
134 static void
call_diff_flush_output(void)135 call_diff_flush_output (void)
136 {
137 cvs_flushout ();
138 }
139
140 /* Call back function for the diff library to write to stdout. */
141
142 static void
call_diff_write_stdout(const char * text)143 call_diff_write_stdout (const char *text)
144 {
145 cvs_output (text, 0);
146 }
147
148 /* Call back function for the diff library to write to stderr. */
149
150 static void
call_diff_error(const char * format,const char * a1,const char * a2)151 call_diff_error (const char *format, const char *a1, const char *a2)
152 {
153 /* FIXME: Should we somehow indicate that this error is coming from
154 the diff library? */
155 error (0, 0, format, a1, a2);
156 }
157
158 /* This set of callback functions is used if we are sending the diff
159 to stdout. */
160
161 static struct diff_callbacks call_diff_stdout_callbacks =
162 {
163 call_diff_write_output,
164 call_diff_flush_output,
165 call_diff_write_stdout,
166 call_diff_error
167 };
168
169 /* This set of callback functions is used if we are sending the diff
170 to a file. */
171
172 static struct diff_callbacks call_diff_file_callbacks =
173 {
174 NULL,
175 NULL,
176 call_diff_write_stdout,
177 call_diff_error
178 };
179
180
181
182 static int
call_diff(const char * out)183 call_diff (const char *out)
184 {
185 call_diff_add_arg (NULL);
186
187 if (out == RUN_TTY)
188 return diff_run( call_diff_argc, call_diff_argv, NULL,
189 &call_diff_stdout_callbacks );
190 else
191 return diff_run( call_diff_argc, call_diff_argv, out,
192 &call_diff_file_callbacks );
193 }
194
195
196
197 static int
call_diff3(char * out)198 call_diff3 (char *out)
199 {
200 if (out == RUN_TTY)
201 return diff3_run (call_diff_argc, call_diff_argv, NULL,
202 &call_diff_stdout_callbacks);
203 else
204 return diff3_run (call_diff_argc, call_diff_argv, out,
205 &call_diff_file_callbacks);
206 }
207
208
209
210 /* Merge revisions REV1 and REV2. */
211
212 int
RCS_merge(RCSNode * rcs,const char * path,const char * workfile,const char * options,const char * rev1,const char * rev2)213 RCS_merge (RCSNode *rcs, const char *path, const char *workfile,
214 const char *options, const char *rev1, const char *rev2)
215 {
216 char *xrev1, *xrev2;
217 char *tmp1, *tmp2;
218 char *diffout = NULL;
219 int retval;
220
221 if (options != NULL && options[0] != '\0')
222 assert (options[0] == '-' && options[1] == 'k');
223
224 cvs_output ("RCS file: ", 0);
225 cvs_output (rcs->print_path, 0);
226 cvs_output ("\n", 1);
227
228 /* Calculate numeric revision numbers from rev1 and rev2 (may be
229 symbolic).
230 FIXME - No they can't. Both calls to RCS_merge are passing in
231 numeric revisions. */
232 xrev1 = RCS_gettag (rcs, rev1, 0, NULL);
233 xrev2 = RCS_gettag (rcs, rev2, 0, NULL);
234 assert (xrev1 && xrev2);
235
236 /* Check out chosen revisions. The error message when RCS_checkout
237 fails is not very informative -- it is taken verbatim from RCS 5.7,
238 and relies on RCS_checkout saying something intelligent upon failure. */
239 cvs_output ("retrieving revision ", 0);
240 cvs_output (xrev1, 0);
241 cvs_output ("\n", 1);
242
243 tmp1 = cvs_temp_name();
244 if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1, NULL, NULL))
245 {
246 cvs_outerr ("rcsmerge: co failed\n", 0);
247 exit (EXIT_FAILURE);
248 }
249
250 cvs_output ("retrieving revision ", 0);
251 cvs_output (xrev2, 0);
252 cvs_output ("\n", 1);
253
254 tmp2 = cvs_temp_name();
255 if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2, NULL, NULL))
256 {
257 cvs_outerr ("rcsmerge: co failed\n", 0);
258 exit (EXIT_FAILURE);
259 }
260
261 /* Merge changes. */
262 cvs_output ("Merging differences between ", 0);
263 cvs_output (xrev1, 0);
264 cvs_output (" and ", 0);
265 cvs_output (xrev2, 0);
266 cvs_output (" into ", 0);
267 cvs_output (workfile, 0);
268 cvs_output ("\n", 1);
269
270 /* Remember that the first word in the `call_diff_setup' string is used now
271 only for diagnostic messages -- CVS no longer forks to run diff3. */
272 diffout = cvs_temp_name();
273 call_diff_setup ("diff3", 0, NULL);
274 call_diff_add_arg ("-E");
275 call_diff_add_arg ("-am");
276
277 call_diff_add_arg ("-L");
278 call_diff_add_arg (workfile);
279 call_diff_add_arg ("-L");
280 call_diff_add_arg (xrev1);
281 call_diff_add_arg ("-L");
282 call_diff_add_arg (xrev2);
283
284 call_diff_add_arg ("--");
285 call_diff_add_arg (workfile);
286 call_diff_add_arg (tmp1);
287 call_diff_add_arg (tmp2);
288
289 retval = call_diff3 (diffout);
290
291 if (retval == 1)
292 cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0);
293 else if (retval == 2)
294 exit (EXIT_FAILURE);
295
296 if (diffout)
297 copy_file (diffout, workfile);
298
299 /* Clean up. */
300 {
301 int save_noexec = noexec;
302 noexec = 0;
303 if (unlink_file (tmp1) < 0)
304 {
305 if (!existence_error (errno))
306 error (0, errno, "cannot remove temp file %s", tmp1);
307 }
308 free (tmp1);
309 if (unlink_file (tmp2) < 0)
310 {
311 if (!existence_error (errno))
312 error (0, errno, "cannot remove temp file %s", tmp2);
313 }
314 free (tmp2);
315 if (diffout)
316 {
317 if (unlink_file (diffout) < 0)
318 {
319 if (!existence_error (errno))
320 error (0, errno, "cannot remove temp file %s", diffout);
321 }
322 free (diffout);
323 }
324 free (xrev1);
325 free (xrev2);
326 noexec = save_noexec;
327 }
328
329 return retval;
330 }
331
332 /* Diff revisions and/or files. OPTS controls the format of the diff
333 (it contains options such as "-w -c", &c), or "" for the default.
334 OPTIONS controls keyword expansion, as a string starting with "-k",
335 or "" to use the default. REV1 is the first revision to compare
336 against; it must be non-NULL. If REV2 is non-NULL, compare REV1
337 and REV2; if REV2 is NULL compare REV1 with the file in the working
338 directory, whose name is WORKFILE. LABEL1 and LABEL2 are default
339 file labels, and (if non-NULL) should be added as -L options
340 to diff. Output goes to stdout.
341
342 Return value is 0 for success, -1 for a failure which set errno,
343 or positive for a failure which printed a message on stderr.
344
345 This used to exec rcsdiff, but now calls RCS_checkout and diff_exec.
346
347 An issue is what timezone is used for the dates which appear in the
348 diff output. rcsdiff uses the -z flag, which is not presently
349 processed by CVS diff, but I'm not sure exactly how hard to worry
350 about this--any such features are undocumented in the context of
351 CVS, and I'm not sure how important to users. */
352 int
RCS_exec_rcsdiff(RCSNode * rcsfile,int diff_argc,char * const * diff_argv,const char * options,const char * rev1,const char * rev1_cache,const char * rev2,const char * label1,const char * label2,const char * workfile)353 RCS_exec_rcsdiff (RCSNode *rcsfile, int diff_argc,
354 char * const *diff_argv, const char *options,
355 const char *rev1, const char *rev1_cache, const char *rev2,
356 const char *label1, const char *label2, const char *workfile)
357 {
358 char *tmpfile1 = NULL;
359 char *tmpfile2 = NULL;
360 const char *use_file1, *use_file2;
361 int status, retval;
362
363
364 cvs_output ("\
365 ===================================================================\n\
366 RCS file: ", 0);
367 cvs_output (rcsfile->print_path, 0);
368 cvs_output ("\n", 1);
369
370 /* Historically, `cvs diff' has expanded the $Name keyword to the
371 empty string when checking out revisions. This is an accident,
372 but no one has considered the issue thoroughly enough to determine
373 what the best behavior is. Passing NULL for the `nametag' argument
374 preserves the existing behavior. */
375
376 cvs_output ("retrieving revision ", 0);
377 cvs_output (rev1, 0);
378 cvs_output ("\n", 1);
379
380 if (rev1_cache != NULL)
381 use_file1 = rev1_cache;
382 else
383 {
384 tmpfile1 = cvs_temp_name();
385 status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1,
386 NULL, NULL);
387 if (status > 0)
388 {
389 retval = status;
390 goto error_return;
391 }
392 else if (status < 0)
393 {
394 error( 0, errno,
395 "cannot check out revision %s of %s", rev1, rcsfile->path );
396 retval = 1;
397 goto error_return;
398 }
399 use_file1 = tmpfile1;
400 }
401
402 if (rev2 == NULL)
403 {
404 assert (workfile != NULL);
405 use_file2 = workfile;
406 }
407 else
408 {
409 tmpfile2 = cvs_temp_name ();
410 cvs_output ("retrieving revision ", 0);
411 cvs_output (rev2, 0);
412 cvs_output ("\n", 1);
413 status = RCS_checkout (rcsfile, NULL, rev2, NULL, options,
414 tmpfile2, NULL, NULL);
415 if (status > 0)
416 {
417 retval = status;
418 goto error_return;
419 }
420 else if (status < 0)
421 {
422 error (0, errno,
423 "cannot check out revision %s of %s", rev2, rcsfile->path);
424 return 1;
425 }
426 use_file2 = tmpfile2;
427 }
428
429 RCS_output_diff_options (diff_argc, diff_argv, rev1, rev2, workfile);
430 status = diff_exec (use_file1, use_file2, label1, label2,
431 diff_argc, diff_argv, RUN_TTY);
432 if (status >= 0)
433 {
434 retval = status;
435 goto error_return;
436 }
437 else if (status < 0)
438 {
439 error (0, errno,
440 "cannot diff %s and %s", use_file1, use_file2);
441 retval = 1;
442 goto error_return;
443 }
444
445 error_return:
446 {
447 /* Call CVS_UNLINK() below rather than unlink_file to avoid the check
448 * for noexec.
449 */
450 if( tmpfile1 != NULL )
451 {
452 if( CVS_UNLINK( tmpfile1 ) < 0 )
453 {
454 if( !existence_error( errno ) )
455 error( 0, errno, "cannot remove temp file %s", tmpfile1 );
456 }
457 free( tmpfile1 );
458 }
459 if( tmpfile2 != NULL )
460 {
461 if( CVS_UNLINK( tmpfile2 ) < 0 )
462 {
463 if( !existence_error( errno ) )
464 error( 0, errno, "cannot remove temp file %s", tmpfile2 );
465 }
466 free (tmpfile2);
467 }
468 }
469
470 return retval;
471 }
472
473
474
475 /* Show differences between two files. This is the start of a diff library.
476
477 Some issues:
478
479 * Should option parsing be part of the library or the caller? The
480 former allows the library to add options without changing the callers,
481 but it causes various problems. One is that something like --brief really
482 wants special handling in CVS, and probably the caller should retain
483 some flexibility in this area. Another is online help (the library could
484 have some feature for providing help, but how does that interact with
485 the help provided by the caller directly?). Another is that as things
486 stand currently, there is no separate namespace for diff options versus
487 "cvs diff" options like -l (that is, if the library adds an option which
488 conflicts with a CVS option, it is trouble).
489
490 * This isn't required for a first-cut diff library, but if there
491 would be a way for the caller to specify the timestamps that appear
492 in the diffs (rather than the library getting them from the files),
493 that would clean up the kludgy utime() calls in patch.c.
494
495 Show differences between FILE1 and FILE2. Either one can be
496 DEVNULL to indicate a nonexistent file (same as an empty file
497 currently, I suspect, but that may be an issue in and of itself).
498 OPTIONS is a list of diff options, or "" if none. At a minimum,
499 CVS expects that -c (update.c, patch.c) and -n (update.c) will be
500 supported. Other options, like -u, --speed-large-files, &c, will
501 be specified if the user specified them.
502
503 OUT is a filename to send the diffs to, or RUN_TTY to send them to
504 stdout. Error messages go to stderr. Return value is 0 for
505 success, -1 for a failure which set errno, 1 for success (and some
506 differences were found), or >1 for a failure which printed a
507 message on stderr. */
508
509 int
diff_exec(const char * file1,const char * file2,const char * label1,const char * label2,int dargc,char * const * dargv,const char * out)510 diff_exec (const char *file1, const char *file2, const char *label1,
511 const char *label2, int dargc, char * const *dargv,
512 const char *out)
513 {
514 TRACE (TRACE_FUNCTION, "diff_exec (%s, %s, %s, %s, %s)",
515 file1, file2, label1, label2, out);
516
517 #ifdef PRESERVE_PERMISSIONS_SUPPORT
518 /* If either file1 or file2 are special files, pretend they are
519 /dev/null. Reason: suppose a file that represents a block
520 special device in one revision becomes a regular file. CVS
521 must find the `difference' between these files, but a special
522 file contains no data useful for calculating this metric. The
523 safe thing to do is to treat the special file as an empty file,
524 thus recording the regular file's full contents. Doing so will
525 create extremely large deltas at the point of transition
526 between device files and regular files, but this is probably
527 very rare anyway.
528
529 There may be ways around this, but I think they are fraught
530 with danger. -twp */
531
532 if (preserve_perms &&
533 strcmp (file1, DEVNULL) != 0 &&
534 strcmp (file2, DEVNULL) != 0)
535 {
536 struct stat sb1, sb2;
537
538 if (lstat (file1, &sb1) < 0)
539 error (1, errno, "cannot get file information for %s", file1);
540 if (lstat (file2, &sb2) < 0)
541 error (1, errno, "cannot get file information for %s", file2);
542
543 if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
544 file1 = DEVNULL;
545 if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
546 file2 = DEVNULL;
547 }
548 #endif
549
550 /* The first arg to call_diff_setup is used only for error reporting. */
551 call_diff_setup ("diff", dargc, dargv);
552 if (label1)
553 call_diff_add_arg (label1);
554 if (label2)
555 call_diff_add_arg (label2);
556 call_diff_add_arg ("--");
557 call_diff_add_arg (file1);
558 call_diff_add_arg (file2);
559
560 return call_diff (out);
561 }
562
563 /* Print the options passed to DIFF, in the format used by rcsdiff.
564 The rcsdiff code that produces this output is extremely hairy, and
565 it is not clear how rcsdiff decides which options to print and
566 which not to print. The code below reproduces every rcsdiff run
567 that I have seen. */
568
569 static void
RCS_output_diff_options(int diff_argc,char * const * diff_argv,const char * rev1,const char * rev2,const char * workfile)570 RCS_output_diff_options (int diff_argc, char * const *diff_argv,
571 const char *rev1, const char *rev2,
572 const char *workfile)
573 {
574 int i;
575
576 cvs_output ("diff", 0);
577 for (i = 0; i < diff_argc; i++)
578 {
579 cvs_output (" ", 1);
580 cvs_output (quotearg_style (shell_quoting_style, diff_argv[i]), 0);
581 }
582 cvs_output (" -r", 3);
583 cvs_output (rev1, 0);
584
585 if (rev2)
586 {
587 cvs_output (" -r", 3);
588 cvs_output (rev2, 0);
589 }
590 else
591 {
592 assert (workfile != NULL);
593 cvs_output (" ", 1);
594 cvs_output (workfile, 0);
595 }
596 cvs_output ("\n", 1);
597 }
598