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