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