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