1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may use this file only in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23 /* Copyright (c) 1988 AT&T */
24 /* All Rights Reserved */
25 /*
26 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
28 */
29 /*
30 * Copyright 2006-2020 J. Schilling
31 *
32 * @(#)delta.c 1.115 20/09/17 J. Schilling
33 */
34 #if defined(sun)
35 #pragma ident "@(#)delta.c 1.115 20/09/17 J. Schilling"
36 #endif
37 /*
38 * @(#)delta.c 1.40 06/12/12
39 */
40
41 #if defined(sun)
42 #pragma ident "@(#)delta.c"
43 #pragma ident "@(#)sccs:cmd/delta.c"
44 #endif
45
46 # define NEED_PRINTF_J /* Need defines for js_snprintf()? */
47 # include <schily/resource.h>
48 # define SCCS_MAIN
49 # include <defines.h>
50 # include <version.h>
51 # include <had.h>
52 # include <i18n.h>
53 # include <schily/utsname.h>
54 # include <schily/fcntl.h>
55 # include <schily/wait.h>
56 # define VMS_VFORK_OK
57 # include <schily/vfork.h>
58 # include <ccstypes.h>
59 # include <schily/sysexits.h>
60
61 #ifdef NO_FSDIFF
62 #undef USE_FSDIFF
63 #endif
64
65 # define LENMR 60 /* Maximum length of an MR record */
66
67 static FILE *Diffin, *Gin;
68 static FILE *Cs; /* The changeset file */
69 static Nparms N; /* Keep -N parameters */
70 static Xparms X; /* Keep -X parameters */
71 static struct packet gpkt;
72 static struct utsname un;
73 static int num_files;
74 static int number_of_lines; /* # of lines in the g-file */
75 static off_t size_of_file; /* The size of the g-file */
76 static off_t Szqfile;
77 static off_t Checksum_offset;
78 static char Zhold[MAXPATHLEN]; /* temporary z-file name */
79 #if defined(PROTOTYPES) && defined(INS_BASE)
80 static char BDiffpgmp[] = NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "bin/" "bdiff");
81 #else
82 /*
83 * XXX If you are using a K&R compiler and like to install to a path
84 * XXX different from "/usr/ccs/bin/", you need to edit this string.
85 */
86 static char BDiffpgmp[] = NOGETTEXT("/usr/ccs/bin/bdiff");
87 #endif
88 static char BDiffpgm[] = NOGETTEXT("/usr/bin/bdiff");
89 static char BDiffpgm2[] = NOGETTEXT("/bin/bdiff");
90 #ifdef USE_FSDIFF
91 #if defined(PROTOTYPES) && defined(INS_BASE)
92 static char FDiffpgmp[] = NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "bin/" "fsdiff");
93 #else
94 static char FDiffpgmp[] = NOGETTEXT("/usr/ccs/bin/fsdiff");
95 #endif
96 #endif
97 #if defined(PROTOTYPES) && defined(INS_BASE)
98 static char Diffpgmp[] = NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "bin/" "diff");
99 #else
100 static char Diffpgmp[] = NOGETTEXT("/usr/ccs/bin/diff");
101 #endif
102 static char Diffpgm[] = NOGETTEXT("/usr/bin/diff");
103 static char Diffpgm2[] = NOGETTEXT("/bin/diff");
104 static char *diffpgm = "";
105 static char *ilist, *elist, *glist, Cmrs[300], *Nsid;
106 static char Pfilename[FILESIZE];
107 static char *uuname;
108 static char *Cwd = "";
109 static char *Dfilename;
110 static int processed_files;
111 static int did_intr;
112
113 static struct timespec gfile_mtime; /* Timestamp for -o */
114 static time_t cutoff = MAX_TIME;
115
116 static struct sid sid;
117
118 static void clean_up __PR((void));
119 static void enter __PR((struct packet *pkt, int ch, int n, struct sid *sidp));
120
121 int main __PR((int argc, char **argv));
122 static void delta __PR((char *file));
123 static int mkdelt __PR((struct packet *pkt, struct sid *sp, struct sid *osp,
124 int diffloop, int orig_nlines));
125 static void mkixg __PR((struct packet *pkt, int reason, int ch));
126 static void putmrs __PR((struct packet *pkt));
127 static void putcmrs __PR((struct packet *pkt));
128 static struct pfile *rdpfile __PR((struct packet *pkt, struct sid *sp));
129 static FILE * dodiff __PR((char *newf, char *oldf, int difflim));
130 static int getdiff __PR((char *type, int *plinenum));
131 static void insert __PR((struct packet *pkt, int linenum, int n, int ser, int off));
132 static void delete __PR((struct packet *pkt, int linenum, int n, int ser));
133 static void after __PR((struct packet *pkt, int n));
134 static void before __PR((struct packet *pkt, int n));
135 static char * linerange __PR((char *cp, int *low, int *high));
136 static void skipline __PR((char *lp, int num));
137 static char * rddiff __PR((char *s, int n, int *ll));
138 static void fgetchk __PR((char *file, struct packet *pkt));
139 static void warnctl __PR((char *file, off_t nline));
140 static void warnnull __PR((char *file, off_t nline));
141 static int fixghash __PR((struct packet *pkt, int ser));
142 static RETSIGTYPE intr __PR((int));
143 static void setintr __PR((void));
144
145 int
main(argc,argv)146 main(argc,argv)
147 int argc;
148 register char *argv[];
149 {
150 register int i;
151 register char *p;
152 int no_arg, c;
153 extern int Fcnt;
154 int current_optind;
155
156 /*
157 * Set locale for all categories.
158 */
159 setlocale(LC_ALL, "");
160
161 sccs_setinsbase(INS_BASE);
162
163 /*
164 * Set directory to search for general l10n SCCS messages.
165 */
166 #ifdef PROTOTYPES
167 (void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
168 NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
169 #else
170 (void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
171 NOGETTEXT("/usr/ccs/lib/locale/"));
172 #endif
173
174 (void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
175
176 tzset(); /* Set up timezome related vars */
177
178 #ifdef SCHILY_BUILD
179 save_args(argc, argv);
180 #endif
181 set_clean_up(clean_up);
182 Fflags = FTLEXIT | FTLMSG | FTLCLN;
183 #ifdef SCCS_FATALHELP
184 Fflags |= FTLFUNC;
185 Ffunc = sccsfatalhelp;
186 #endif
187
188 current_optind = 1;
189 optind = 1;
190 opterr = 0;
191 no_arg = 0;
192 i = 1;
193 /*CONSTCOND*/
194 while (1) {
195 if(current_optind < optind) {
196 current_optind = optind;
197 argv[i] = 0;
198 if (optind > i+1 ) {
199 if( (argv[i+1][0]!='-')&&(no_arg==0) ) {
200 argv[i+1] = NULL;
201 } else {
202 optind = i+1;
203 current_optind = optind;
204 }
205 }
206 }
207 no_arg = 0;
208 i = current_optind;
209 c = getopt(argc, argv, "()-r:bdpsnm:g:y:fhoqkzC:D:N:X:V(version)");
210
211 /* this takes care of options given after
212 ** file names.
213 */
214 if(c == EOF) {
215 if (optind < argc) {
216 /* if it's due to -- then break; */
217 if(argv[i][0] == '-' &&
218 argv[i][1] == '-') {
219 argv[i] = 0;
220 break;
221 }
222 optind++;
223 current_optind = optind;
224 continue;
225 } else {
226 break;
227 }
228 }
229 p = optarg;
230 switch (c) {
231
232 case 'r':
233 if (*p == 0) continue;
234 chksid(sid_ab(p,&sid),&sid);
235 break;
236 case 'g':
237 glist = p;
238 break;
239 case 'y':
240 if (optarg == argv[i+1]) {
241 Comments = "";
242 no_arg = 1;
243 } else {
244 Comments = p;
245 }
246 break;
247 case 'm':
248 Mrs = p;
249 repl(Mrs,'\n',' ');
250 break;
251 case 'b':
252 case 'd':
253 case 'p':
254 case 'n':
255 case 's':
256 case 'f': /* force delta without p. file (NSE only) */
257 if (p) {
258 if (*p) {
259 sprintf(SccsError,
260 gettext("value after %c arg (cm7)"),
261 c);
262 fatal(SccsError);
263 }
264 }
265 break;
266 case 'h': /* allow diffh for large files (NSE only) */
267 case 'k': /* get(1) without keyword expand */
268 case 'o': /* use original file date */
269 break;
270 case 'q': /* enable NSE mode */
271 if(p) {
272 if (*p) {
273 nsedelim = p;
274 }
275 } else {
276 nsedelim = (char *) 0;
277 }
278 break;
279 case 'z':
280 break;
281 case 'C':
282 Cwd = p;
283 break;
284 case 'D':
285 Dfilename = p;
286 break;
287
288 case 'N': /* Bulk names */
289 initN(&N);
290 if (optarg == argv[i+1]) {
291 no_arg = 1;
292 break;
293 }
294 N.n_parm = p;
295 break;
296
297 case 'X':
298 X.x_parm = optarg;
299 X.x_flags = XO_PREPEND_FILE|XO_MAIL|\
300 XO_USER|XO_DATE|XO_NULLPATH|\
301 XO_NOBULK|XO_G_PATH;
302 if (!parseX(&X))
303 goto err;
304 break;
305
306 case 'V': /* version */
307 printf(gettext(
308 "delta %s-SCCS version %s %s (%s-%s-%s)\n"),
309 PROVIDER,
310 VERSION,
311 VDATE,
312 HOST_CPU, HOST_VENDOR, HOST_OS);
313 exit(EX_OK);
314
315 default:
316 err:
317 fatal(gettext("Usage: delta [ -bdknops ][ -g sid-list ][ -m mr-list ][ -r SID ]\n\t[ -y[comment] ][ -D diff-file ][ -N[bulk-spec]][ -Xxopts ] s.filename..."));
318 }
319
320 /*
321 * Make sure that we only collect option letters from
322 * the range 'a'..'z' and 'A'..'Z'.
323 */
324 if (ALPHA(c) &&
325 (had[LOWER(c)? c-'a' : NLOWER+c-'A']++)) {
326 if (c != 'X')
327 fatal(gettext("key letter twice (cm2)"));
328 }
329 }
330 #ifdef NO_BDIFF
331 #if !(defined(SVR4) && defined(sun))
332 had['d' - 'a'] = 1; /* Do not use "bdiff" */
333 #endif
334 #endif
335
336 for(i=1; i<argc; i++){
337 if(argv[i]) {
338 num_files++;
339 }
340 }
341 if ((HADF || HADH) && !HADQ) {
342 fatal(gettext("unknown key letter (cm1)"));
343 }
344 if (num_files == 0) {
345 if (HADD != 0) Fflags &= ~FTLEXIT;
346 fatal(gettext("missing file arg (cm3)"));
347 exit(2);
348 }
349
350 setsig();
351 xsethome(NULL);
352 if (HADUCN) { /* Parse -N args */
353 parseN(&N);
354
355 if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
356 fatal(gettext("-Ns. not supported in off-tree project mode"));
357 }
358
359 /*
360 * Get the name of our machine to be used for the lockfile.
361 */
362 uname(&un);
363 uuname = un.nodename;
364 setintr(); /* Catch ^C */
365
366 /*
367 * Set up a project global lock on the changeset file.
368 * Since we set FTLJMP, we do not need to unlockchset() from clean_up().
369 */
370 if (SETHOME_CHSET()) {
371 lockchset(getppid(), getpid(), uuname);
372 if (HADUCN && exists(changesetgfile)) {
373 /*
374 * Should we open/create the file even if it does not
375 * yet exist? This may change with the final concept.
376 */
377 Cs = xfopen(changesetgfile, O_WRONLY|O_APPEND|O_BINARY);
378 }
379 }
380 timerchsetlock();
381
382 Fflags &= ~FTLEXIT;
383 Fflags |= FTLJMP;
384 for (i=1; i<argc; i++)
385 if ((p=argv[i]) != NULL)
386 do_file(p, delta, 1, N.n_sdot, &X);
387
388 /*
389 * Only remove the global lock it it was created by us and not by
390 * our parent.
391 */
392 if (SETHOME_CHSET()) {
393 if (HADUCN) {
394 bulkchdir(&N);
395 if (Cs)
396 fclose(Cs);
397 }
398 unlockchset(getpid(), uuname);
399 }
400
401 return (Fcnt ? 1 : 0);
402 }
403
404 /*
405 * Create the delta for one file
406 */
407 static void
delta(file)408 delta(file)
409 char *file;
410 {
411 static int first = 1;
412 int n, linenum;
413 int ghash;
414 char type;
415 register int ser;
416 extern char had_dir, had_standinp;
417 char nsid[SID_STRSIZE];
418 char dfilename[FILESIZE];
419 char gfilename[FILESIZE];
420 char *ifile;
421 char line[BUFSIZ]; /* Buffer for uuencoded IO, see below */
422 struct stats stats;
423 struct pfile *pp = NULL;
424 struct stat sbuf;
425 int inserted, deleted, orig;
426 int newser;
427 uid_t holduid;
428 gid_t holdgid;
429 FILE *efp;
430 int status;
431 int diffloop;
432 int difflim;
433
434 Gin = NULL;
435 if (setjmp(Fjmp))
436 return;
437
438 /*
439 * In order to make the global lock with a potentially long duration
440 * not look as if it was expired, we refresh it for every file in our
441 * task list. This is needed since another SCCS instance on a different
442 * NFS machine cannot use kill() to check for a still active process.
443 */
444 if (SETHOME_CHSET()) {
445 if (HADUCN)
446 bulkchdir(&N); /* Done by bulkprepare() anyway */
447 refreshchsetlock();
448 }
449
450 if (HADUCN && !(X.x_opts & XO_NOBULK)) {
451 #ifdef __needed__
452 char *ofile = file;
453 #endif
454
455 file = bulkprepare(&N, file);
456 if (file == NULL) {
457 #ifdef __needed__
458 if (N.n_ifile)
459 ofile = N.n_ifile;
460 #endif
461 /*
462 * The error is typically
463 * "directory specified as s-file (cm14)"
464 */
465 fatal(gettext(bulkerror(&N)));
466 }
467 ifile = N.n_ifile;
468 } else {
469 ifile = NULL;
470 }
471 if (X.x_opts & XO_G_PATH)
472 ifile = X.x_gpath;
473
474 /*
475 * Init and check for validity of file name but do not open the file.
476 * This prevents us from potentially damaging files with lockit().
477 */
478 sinit(&gpkt, file, SI_INIT);
479
480 /*
481 * Lock out any other user who may be trying to process
482 * the same file.
483 */
484 if (!islockchset(copy(auxf(gpkt.p_file, 'z'), Zhold))) {
485 if (lockit(Zhold, SCCS_LOCK_ATTEMPTS, getpid(), uuname)) {
486 lockfatal(Zhold, getpid(), uuname);
487 } else {
488 timersetlockfile(Zhold);
489 }
490 } else {
491 /*
492 * We are updating the changeset history file and thus should
493 * not append changeset records to the changeset file.
494 */
495 if (Cs) {
496 fclose(Cs);
497 Cs = NULL;
498 }
499 }
500
501 sinit(&gpkt, file, SI_OPEN);
502 gpkt.p_enter = enter;
503 if ((gpkt.p_flags & PF_V6) == 0)
504 gpkt.p_flags |= PF_GMT;
505 if (first) {
506 first = 0;
507 dohist(file);
508 }
509 gpkt.p_reopen = 1;
510 if (X.x_opts & XO_PREPEND_FILE) {
511 gpkt.no_chksum = 1;
512 gpkt.do_chksum = 0;
513 }
514 gpkt.p_stdout = stdout;
515 gfilename[0] = '\0';
516 strlcatl(gfilename, sizeof (gfilename),
517 Cwd,
518 ifile ? ifile :
519 auxf(gpkt.p_file, 'g'),
520 (char *)0);
521 Gin = xfopen(gfilename, O_RDONLY|O_BINARY);
522 #ifdef USE_SETVBUF
523 setvbuf(Gin, NULL, _IOFBF, VBUF_SIZE);
524 #endif
525 Pfilename[0] = '\0';
526 if (!HADF || exists(auxf(gpkt.p_file,'p')))
527 pp = rdpfile(&gpkt, &sid);
528
529 Cmrs[0] = 0;
530 if (pp != NULL && pp->pf_cmrlist != NULL)
531 strlcpy(Cmrs, pp->pf_cmrlist, sizeof (Cmrs));
532
533 if (dodelt(&gpkt,&stats,(struct sid *) 0,0) == 0)
534 fmterr(&gpkt);
535
536 if ((HADF) && !exists(auxf(gpkt.p_file,'p'))) {
537 /* if no p. file exists delta can still happen if
538 * -f flag given (in NSE mode) - uses the same logic
539 * as get -e to assign a new SID */
540 gpkt.p_reqsid.s_rel = 0;
541 gpkt.p_reqsid.s_lev = 0;
542 gpkt.p_reqsid.s_br = 0;
543 gpkt.p_reqsid.s_seq = 0;
544 gpkt.p_cutoff = cutoff;
545 ilist = 0;
546 elist = 0;
547 ser = getser(&gpkt);
548 newsid(&gpkt, 0); /* sets gpkt.p_reqsid */
549 } else {
550 gpkt.p_cutoff = pp->pf_date;
551 ilist = pp->pf_ilist;
552 elist = pp->pf_elist;
553 if ((ser = sidtoser(&pp->pf_gsid,&gpkt)) == 0 ||
554 sidtoser(&pp->pf_nsid,&gpkt))
555 fatal(gettext("invalid sid in p-file (de3)"));
556 gpkt.p_reqsid = pp->pf_nsid;
557 }
558 sid_ba(&gpkt.p_reqsid, nsid);
559 Nsid=nsid;
560 gfile_mtime.tv_sec = gfile_mtime.tv_nsec = 0;
561 if ((HADO || HADQ) && stat(gfilename, &sbuf) == 0) {
562 /*
563 * When specifying -o (original date) and when
564 * in NSE mode, the mtime of the clear file is remembered for
565 * use as delta time. Sccs is thus now vulnerable to clock
566 * skew between NFS server and host machine and to a mis-set
567 * clock when file is last changed.
568 * In non-original date mode, sccs is vulnerable to a mis-set of
569 * the local clock while calling 'delta'.
570 */
571 gfile_mtime.tv_sec = sbuf.st_mtime;
572 gfile_mtime.tv_nsec = stat_mnsecs(&sbuf);
573 }
574
575 doie(&gpkt,ilist,elist,glist);
576 setup(&gpkt,ser);
577 finduser(&gpkt);
578 doflags(&gpkt);
579 permiss(&gpkt);
580 donamedflags(&gpkt);
581 dometa(&gpkt);
582 flushto(&gpkt, EUSERTXT, FLUSH_NOCOPY);
583 gpkt.p_chkeof = 1;
584 /* if encode flag is set, encode the g-file before diffing it
585 * with the s.file
586 */
587 if (gpkt.p_encoding & EF_UUENCODE) {
588 efp = xfcreat(auxf(gpkt.p_file,'e'),0644);
589 #ifdef USE_SETVBUF
590 setvbuf(efp, NULL, _IOFBF, VBUF_SIZE);
591 #endif
592 encode(Gin,efp);
593 fclose(efp);
594 if (Gin)
595 fclose(Gin);
596 Gin = xfopen(auxf(gpkt.p_file,'e'), O_RDONLY|O_BINARY);
597 #ifdef USE_SETVBUF
598 setvbuf(Gin, NULL, _IOFBF, VBUF_SIZE);
599 #endif
600 }
601
602 dfilename[0] = '\0';
603 if ((X.x_opts & XO_PREPEND_FILE) == 0) {
604 /*
605 * Extract the reference version to diff against.
606 */
607 copy(auxf(gpkt.p_file,'d'),dfilename);
608 gpkt.p_gout = xfcreat(dfilename,(mode_t)0444);
609 #ifdef USE_SETVBUF
610 setvbuf(gpkt.p_gout, NULL, _IOFBF, VBUF_SIZE);
611 #endif
612 while (readmod(&gpkt)) {
613 if (gpkt.p_flags & PF_NONL) {
614 gpkt.p_line[--(gpkt.p_line_length)] = '\0';
615 gpkt.p_line_length -= 2; /* ^AN */
616 }
617 if (fwrite(gpkt.p_lineptr, 1, gpkt.p_line_length,
618 gpkt.p_gout) != gpkt.p_line_length)
619 xmsg(dfilename, NOGETTEXT("delta"));
620 if (gpkt.p_flags & PF_NONL) {
621 gpkt.p_line_length += 2; /* ^AN */
622 gpkt.p_line[(gpkt.p_line_length)++] = '\n';
623 }
624 }
625 if (fflush(gpkt.p_gout) == EOF)
626 xmsg(dfilename, NOGETTEXT("delta"));
627 #ifdef HAVE_FSYNC
628 if (fsync(fileno(gpkt.p_gout)) < 0)
629 xmsg(dfilename, NOGETTEXT("delta"));
630 #endif
631 if (fclose(gpkt.p_gout) == EOF)
632 xmsg(dfilename, NOGETTEXT("delta"));
633 gpkt.p_gout = NULL;
634 orig = gpkt.p_glnno;
635 } else {
636 /*
637 * We don't count the previous version and we
638 * do not have real diffs, so we need to use
639 * an estimation.
640 */
641 orig = stats.s_ins + stats.s_unc - stats.s_del;
642 }
643
644 gpkt.p_glnno = 0;
645 gpkt.p_verbose = (HADS) ? 0 : 1;
646 gpkt.p_did_id = 0;
647 number_of_lines = size_of_file = 0;
648
649 gpkt.p_ghash = 0; /* Reset ghash from previous readmod() calls */
650
651 if (gpkt.p_sflags[EXPANDFLAG - 'a'] &&
652 *(gpkt.p_sflags[EXPANDFLAG - 'a']) == '\0') {
653 gpkt.p_did_id = 1; /* No need to scan for keywds */
654 }
655 if ((gpkt.p_encoding & EF_UUENCODE) == 0) {
656 /*
657 * Compute the checksum and scan for unsupported characters.
658 * This method supports nul bytes.
659 */
660 fgetchk(gfilename, &gpkt);
661 number_of_lines = gpkt.p_glines;
662 } else {
663 /*
664 * This does not support nul bytes, but this is uuencoded text.
665 */
666 while (fgets(line,sizeof(line),Gin) != NULL) {
667 register int len = strlen(line);
668 register char **sflags = gpkt.p_sflags;
669
670 gpkt.p_ghash += usum(line, len);
671 if (line[len-1] == '\n') {
672 number_of_lines++;
673 }
674 if (gpkt.p_did_id == 0) {
675 gpkt.p_did_id = chkid(line,
676 sflags[IDFLAG - 'a'],
677 sflags);
678 }
679 }
680 }
681 gpkt.p_ghash &= 0xFFFF;
682 if (stat(gfilename, &Statbuf) == 0) {
683 size_of_file = Statbuf.st_size;
684 }
685 if ((X.x_opts & XO_PREPEND_FILE) == 0) {
686 if (Gin)
687 fclose(Gin);
688 Gin = NULL;
689 }
690 if (gpkt.p_verbose && (num_files > 1 || had_dir || had_standinp))
691 fprintf(gpkt.p_stdout,"\n%s:\n",gpkt.p_file);
692 if (HADD != 0) {
693 if (number_of_lines > 70000 && size_of_file > 3670000) {
694 fprintf(stderr,
695 gettext("Warning: the file is greater than 70000 lines and 3.5Mb\n"));
696 } else {
697 if (size_of_file > 5872000) {
698 fprintf(stderr,
699 gettext("Warning: the file is greater than 5.6Mb\n"));
700 }
701 }
702 }
703
704 if (!gpkt.p_did_id && !HADQ &&
705 (!gpkt.p_sflags[EXPANDFLAG - 'a'] ||
706 *(gpkt.p_sflags[EXPANDFLAG - 'a']))) {
707 if (gpkt.p_sflags[IDFLAG - 'a']) {
708 if(!(*gpkt.p_sflags[IDFLAG - 'a']))
709 fatal(gettext("no id keywords (cm6)"));
710 else
711 fatal(gettext("invalid id keywords (cm10)"));
712 } else if (gpkt.p_verbose) {
713 fprintf(stderr,gettext("No id keywords (cm7)\n"));
714 }
715 }
716
717 /*
718 The following while loop executes 'bdiff' on g-file and
719 d-file. If 'bdiff' fails (usually because segmentation
720 limit it is using is too large for 'diff'), it is
721 invoked again, with a lower segmentation limit.
722 */
723 difflim = 24000;
724 diffloop = 0;
725 ghash = gpkt.p_ghash; /* Save ghash value */
726 gpkt.p_reopen = 1; /* Let it stay open */
727
728 if (X.x_opts & XO_PREPEND_FILE) {
729 int oihash = gpkt.p_ihash; /* Remember hash from sinit() */
730
731 grewind(&gpkt);
732 gpkt.p_reopen = 1; /* Let it stay open */
733 gpkt.do_chksum = 1; /* No old g-file, do it now */
734 gpkt.p_ihash = oihash; /* Restore hash */
735 if (gpkt.p_flags & PF_V6) {
736 /*
737 * We do not read the "current" file as it does not yet
738 * exist, but we compute the sum of the hashes from the
739 * old file and the new beginning that is a file with
740 * the name of the g-file.
741 */
742 if (gpkt.p_hash != NULL) {
743 ghash += gpkt.p_hash[ser];
744 ghash &= 0xFFFF;
745 }
746 }
747 }
748
749 /*CONSTCOND*/
750 while (1) {
751 inserted = deleted = 0;
752 gpkt.p_glnno = 0;
753 gpkt.p_upd = 1;
754 gpkt.p_wrttn = 1;
755 getline(&gpkt); /* Read the magic line */
756 gpkt.p_chash = 0; /* Reset signed hash */
757 gpkt.p_uchash = 0; /* Reset unsigned hash */
758 gpkt.p_wrttn = 1;
759 gpkt.p_ghash = ghash; /* ghash may be destroyed in loop */
760
761 if (glist && gpkt.p_flags & PF_V6) {
762 gpkt.p_ghash = 0; /* write 00000 before correcting */
763 }
764 if (HADF) {
765 newser = mkdelt(&gpkt, &gpkt.p_reqsid, &gpkt.p_gotsid,
766 diffloop, orig);
767 } else {
768 newser = mkdelt(&gpkt,&pp->pf_nsid,&pp->pf_gsid,
769 diffloop, orig);
770 }
771 diffloop = 1;
772 flushto(&gpkt, EUSERTXT, FLUSH_COPY);
773
774 if (X.x_opts & XO_PREPEND_FILE) {
775 /*
776 * Since we do not call "diff", we come here only once.
777 */
778 Diffin = Gin;
779 rewind(Diffin);
780 Gin = NULL;
781 inserted += number_of_lines;
782 insert(&gpkt, 0, number_of_lines, newser, 0);
783 status = 0; /* Causes a break from while */
784 } else {
785 if (gpkt.p_encoding & EF_UUENCODE) {
786 Diffin = dodiff(auxf(gpkt.p_file,'e'),
787 dfilename, difflim);
788 } else {
789 Diffin = dodiff(gfilename, dfilename, difflim);
790 }
791 type = 0; /* Make GCC quiet */
792 while ((n = getdiff(&type,&linenum)) != 0) {
793 if (type == INS) {
794 inserted += n;
795 insert(&gpkt, linenum , n, newser, 1);
796 } else {
797 deleted += n;
798 delete(&gpkt,linenum,n,newser);
799 }
800 }
801 }
802 /*
803 * If the modifications added lines via putline() after the
804 * code above did hit EOF in the input s.file and thus caused
805 * a grewind(), we need to fix gpkt.p_onhash by the delta
806 * that results from these putline() calls.
807 */
808 gpkt.p_onhash += gpkt.p_nhash;
809 if (Diffin)
810 fclose(Diffin);
811 Diffin = NULL;
812 /*
813 * Call readmod() only if there is remaining input from the
814 * s.file. This happens in case that we did not hit EOF on the
815 * input before.
816 */
817 if (gpkt.p_iop && stell(&gpkt) > 0)
818 while (readmod(&gpkt))
819 ;
820 if ((X.x_opts & XO_PREPEND_FILE) == 0)
821 wait(&status);
822 /*
823 Check top byte (exit code of child).
824 */
825
826 /*
827 * 32 is the exit code below after a failed execlp() call.
828 */
829 if (WEXITSTATUS(status) == 32) { /* 'execl' failed */
830 sprintf(SccsError,
831 gettext("cannot execute '%s' (de12)"), diffpgm);
832 fatal(SccsError);
833 }
834 if ((status != 0) && (HADD == 0)) { /* diff failed */
835 /*
836 Re-try.
837 */
838 if (difflim -= 3000) { /* reduce segmentation */
839 fprintf(stderr,
840 gettext("'%s' failed, re-trying, segmentation = %d (de13)\n"),
841 diffpgm,
842 difflim);
843 xrm(&gpkt); /* Close x-file */
844 /*
845 * Rewind s-file.
846 */
847 grewind(&gpkt);
848 gpkt.p_reopen = 1; /* Let it stay open */
849 }
850 else
851 /* tried up to 500 lines, can't go on */
852 /*
853 TRANSLATION_NOTE
854 "diff" refers to the UNIX "diff" program, used by this SCCS "delta"
855 command, to check the differences found between two files.
856 */
857 fatal(gettext("diff failed (de4)"));
858 } else { /* no need to try again, worked */
859 break; /* exit while loop */
860 }
861 }
862 /*
863 * gpkt.p_nhash has been cleared by grewind(), restore remembered value.
864 */
865 gpkt.p_nhash = gpkt.p_onhash;
866
867 if (gpkt.p_encoding & EF_UUENCODE) {
868 unlink(auxf(gpkt.p_file,'e'));
869 }
870 if (dfilename[0]) {
871 unlink(dfilename);
872 dfilename[0] = '\0';
873 }
874 /*
875 * If we had a glist, the checked out file will differ from the checked
876 * in file. Check out the real new content and recompute + correct the
877 * ghash.
878 */
879 if (glist && gpkt.p_flags & PF_V6)
880 ghash = fixghash(&gpkt, newser);
881
882 stats.s_ins = inserted;
883 stats.s_del = deleted;
884 stats.s_unc = orig - deleted;
885 if (gpkt.p_verbose) {
886 fprintf(gpkt.p_stdout, gettext("%d inserted\n"), stats.s_ins);
887 fprintf(gpkt.p_stdout, gettext("%d deleted\n"), stats.s_del);
888 fprintf(gpkt.p_stdout, gettext("%d unchanged\n"),stats.s_unc);
889 }
890 flushline(&gpkt,&stats);
891 stat(gpkt.p_file,&sbuf);
892 processed_files = TRUE;
893 rename(auxf(gpkt.p_file,'x'),gpkt.p_file);
894 chmod(gpkt.p_file, (unsigned int)sbuf.st_mode);
895
896 chown(gpkt.p_file, (unsigned int)sbuf.st_uid,
897 (unsigned int)sbuf.st_gid);
898 if (HADO) {
899 struct timespec ts[2];
900 extern dtime_t Timenow;
901
902 ts[0].tv_sec = Timenow.dt_sec;
903 ts[0].tv_nsec = Timenow.dt_nsec;
904 ts[1].tv_sec = gfile_mtime.tv_sec;
905 ts[1].tv_nsec = gfile_mtime.tv_nsec;
906
907 /*
908 * As SunPro make and gmake call sccs get when the time
909 * if s.file equals the time stamp of the g-file, make
910 * sure the s.file is a bit older.
911 */
912 if (!(gpkt.p_flags & PF_V6)) {
913 struct timespec tn;
914
915 getnstimeofday(&tn);
916 ts[1].tv_nsec = tn.tv_nsec;
917 }
918 if (ts[1].tv_nsec > 500000000)
919 ts[1].tv_nsec -= 500000000;
920
921 utimensat(AT_FDCWD, gpkt.p_file, ts, 0);
922 }
923 if (!HADF || Pfilename[0] != '\0') {
924 char *qfile;
925
926 if (exists(qfile = auxf(gpkt.p_file, 'q'))) {
927 Szqfile = Statbuf.st_size;
928 }
929 if (Szqfile) {
930 rename(qfile, Pfilename);
931 }
932 else {
933 xunlink(Pfilename);
934 }
935 }
936
937 if ((gpkt.p_flags & PF_V6) && Cs && gpkt.p_init_path) {
938 char cbuf[2*MAXPATHLEN];
939
940 gpkt.p_ghash = ghash; /* Restore correct value */
941 change_ba(&gpkt, cbuf, sizeof (cbuf));
942 fprintf(Cs, "%s\n", cbuf);
943 }
944 sclose(&gpkt);
945 clean_up();
946 if (!HADN) {
947 fflush(gpkt.p_stdout);
948 holduid=geteuid();
949 holdgid=getegid();
950 setuid(getuid());
951 setgid(getgid());
952 unlink(gfilename);
953 if (N.n_get) {
954 doget(gpkt.p_file, gfilename, newser);
955 if (HADO)
956 dogtime(&gpkt, gfilename, &gfile_mtime);
957 }
958 setuid(holduid);
959 setgid(holdgid);
960 }
961 if (did_intr)
962 exit(2);
963 }
964
965 /*
966 * Make the delta table for the current file
967 */
968 static int
mkdelt(pkt,sp,osp,diffloop,orig_nlines)969 mkdelt(pkt,sp,osp,diffloop,orig_nlines)
970 struct packet *pkt;
971 struct sid *sp, *osp;
972 int diffloop;
973 int orig_nlines;
974 {
975 extern dtime_t Timenow;
976 struct deltab dt;
977 char str[max(BUFSIZ, SID_STRSIZE)]; /* Buffer for delta table IO */
978 int newser;
979 register char *p;
980 int ser_inc, opred, nulldel;
981
982 if (!diffloop && pkt->p_verbose) {
983 sid_ba(sp,str);
984 fprintf(pkt->p_stdout,"%s\n",str);
985 fflush(pkt->p_stdout);
986 }
987 putmagic(pkt, "00000");
988 newstats(pkt,str,"0");
989 dt.d_sid = *sp;
990
991 /*
992 Check if 'null' deltas should be inserted
993 (only if 'null' flag is in file and
994 releases are being skipped) and set
995 'nulldel' indicator appropriately.
996 */
997 if (pkt->p_sflags[NULLFLAG - 'a'] && (sp->s_rel > osp->s_rel + 1) &&
998 !sp->s_br && !sp->s_seq &&
999 !osp->s_br && !osp->s_seq)
1000 nulldel = 1;
1001 else
1002 nulldel = 0;
1003 /*
1004 Calculate how many serial numbers are needed.
1005 */
1006 if (nulldel)
1007 ser_inc = sp->s_rel - osp->s_rel;
1008 else
1009 ser_inc = 1;
1010 /*
1011 Find serial number of the new delta.
1012 */
1013 newser = dt.d_serial = maxser(pkt) + ser_inc;
1014 /*
1015 Find old predecessor's serial number.
1016 */
1017 opred = sidtoser(osp,pkt);
1018 if (nulldel)
1019 dt.d_pred = newser - 1; /* set predecessor to 'null' delta */
1020 else
1021 dt.d_pred = opred;
1022
1023 #ifdef NO_NANOSECS
1024 time2dt(&dt.d_dtime, Timenow, 0); /* Timenow was set by dodelt() */
1025 #else
1026 dt.d_dtime = Timenow; /* Timenow was set by dodelt() */
1027 #endif
1028
1029 /* Since the NSE always preserves the clear file after delta and
1030 * makes it read only (no get is required since keywords are not
1031 * supported), the delta time is set to be the mtime of the clear
1032 * file.
1033 */
1034 if ((HADO || HADQ) && (gfile_mtime.tv_sec != 0)) {
1035 time2dt(&dt.d_dtime, gfile_mtime.tv_sec, gfile_mtime.tv_nsec);
1036 }
1037 if (X.x_opts & XO_DATE) {
1038 dt.d_dtime = X.x_dtime;
1039 }
1040 strlcpy(dt.d_pgmr, logname(), LOGSIZE);
1041 if (X.x_user)
1042 strlcpy(dt.d_pgmr, X.x_user, LOGSIZE);
1043 dt.d_type = 'D';
1044 del_ba(&dt,str, pkt->p_flags & ~PF_GMT);
1045 putline(pkt,str);
1046 if (ilist)
1047 mkixg(pkt,INCLUSER,INCLUDE);
1048 if (elist)
1049 mkixg(pkt,EXCLUSER,EXCLUDE);
1050 if (glist)
1051 mkixg(pkt,IGNRUSER,IGNORE);
1052 if (Mrs) {
1053 if ((p = pkt->p_sflags[VALFLAG - 'a']) == NULL)
1054 fatal(gettext("MRs not allowed (de8)"));
1055 if (*p && !diffloop && valmrs(pkt,p))
1056 fatal(gettext("invalid MRs (de9)"));
1057 putmrs(pkt);
1058 } else if (pkt->p_sflags[VALFLAG - 'a'] && !HADQ) {
1059 fatal(gettext("MRs required (de10)"));
1060 }
1061 /*
1062 *
1063 * CMF enhancement
1064 *
1065 */
1066 if (pkt->p_sflags[CMFFLAG - 'a']) {
1067 if (Mrs) {
1068 cmrerror(gettext("input CMR's ignored"));
1069 Mrs = NOGETTEXT("");
1070 }
1071 if (!deltack(pkt->p_file,Cmrs,Nsid, pkt->p_sflags[CMFFLAG - 'a'], pkt->p_sflags)) {
1072 fatal(gettext("Delta denied due to CMR difficulties"));
1073 }
1074 putcmrs(pkt); /* this routine puts cmrs on the out put file */
1075 }
1076 if (pkt->p_flags & PF_V6) {
1077 Checksum_offset = ftell(gpkt.p_xiop);
1078 gpkt.p_mail = X.x_mail;
1079 sidext_ba(pkt, &dt);
1080 gpkt.p_mail = NULL;
1081 }
1082
1083 sprintf(str, NOGETTEXT("%c%c "), CTLCHAR, COMMENTS);
1084 putline(pkt,str);
1085 {
1086 char *comment = savecmt(Comments);
1087 putline(pkt,comment);
1088 putline(pkt,"\n");
1089 }
1090 sprintf(str,CTLSTR,CTLCHAR,EDELTAB);
1091 putline(pkt,str);
1092 if (nulldel) /* insert 'null' deltas */
1093 while (--ser_inc) {
1094 sprintf(str, NOGETTEXT("%c%c %s/%s/%05d\n"),
1095 CTLCHAR, STATS,
1096 NOGETTEXT("00000"), NOGETTEXT("00000"),
1097 orig_nlines);
1098 putline(pkt,str);
1099 dt.d_sid.s_rel -= 1;
1100 dt.d_serial -= 1;
1101 if (ser_inc != 1)
1102 dt.d_pred -= 1;
1103 else
1104 dt.d_pred = opred; /* point to old pred */
1105 del_ba(&dt,str, pkt->p_flags);
1106 putline(pkt,str);
1107 sprintf(str, NOGETTEXT("%c%c "), CTLCHAR, COMMENTS);
1108 putline(pkt,str);
1109 putline(pkt,NOGETTEXT("AUTO NULL DELTA\n"));
1110 sprintf(str,CTLSTR,CTLCHAR,EDELTAB);
1111 putline(pkt,str);
1112 }
1113 return(newser);
1114 }
1115
1116 /*
1117 * Write include/exclude/ignore table for delta table
1118 */
1119 static void
mkixg(pkt,reason,ch)1120 mkixg(pkt,reason,ch)
1121 struct packet *pkt;
1122 int reason;
1123 char ch;
1124 {
1125 int n;
1126 char str[BUFSIZ]; /* Only limits the size of a single entry */
1127
1128 sprintf(str, NOGETTEXT("%c%c"), CTLCHAR, ch);
1129 putline(pkt,str);
1130 for (n = maxser(pkt); n; n--) {
1131 if (pkt->p_apply[n].a_reason == reason) {
1132 sprintf(str, NOGETTEXT(" %u"), n);
1133 putline(pkt,str);
1134 }
1135 }
1136 putline(pkt,"\n");
1137 }
1138
1139 static void
putmrs(pkt)1140 putmrs(pkt)
1141 struct packet *pkt;
1142 {
1143 register char **argv;
1144 char str[LENMR+6];
1145 extern char **Varg;
1146
1147 for (argv = &Varg[VSTART]; *argv; argv++) {
1148 sprintf(str, NOGETTEXT("%c%c %s\n"), CTLCHAR, MRNUM, *argv);
1149 if (strcmp(str,NOGETTEXT("\001m \012")))
1150 putline(pkt,str);
1151 }
1152 }
1153
1154
1155
1156 /*
1157 *
1158 * putcmrs takes the cmrs list on the Mrs line built by deltack
1159 * and puts them in the packet
1160 *
1161 */
1162 static void
putcmrs(pkt)1163 putcmrs(pkt)
1164 struct packet *pkt;
1165 {
1166 char str[510];
1167 sprintf(str, NOGETTEXT("%c%c %s\n"), CTLCHAR, MRNUM, Cmrs);
1168 putline(pkt,str);
1169 }
1170
1171
1172 static char ambig[] = NOGETTEXT("ambiguous `r' keyletter value (de15)");
1173
1174 /*
1175 * Read the p-file and write a new version as q-file.
1176 */
1177 static struct pfile *
rdpfile(pkt,sp)1178 rdpfile(pkt,sp)
1179 register struct packet *pkt;
1180 struct sid *sp;
1181 {
1182 char *user;
1183 struct pfile pf;
1184 static struct pfile goodpf;
1185 char line[BUFSIZ]; /* Limits the line length of a p-file */
1186 int cnt, uniq, fd;
1187 FILE *in, *out;
1188 char *outname;
1189
1190 uniq = cnt = -1;
1191 if ((user=logname()) == NULL)
1192 fatal(gettext("User ID not in password file (cm9)"));
1193 zero((char *)&goodpf,sizeof(goodpf));
1194 in = xfopen(auxf(pkt->p_file,'p'), O_RDONLY|O_BINARY);
1195 outname = auxf(pkt->p_file, 'q');
1196 if ((fd=open(outname, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0444)) < 0) {
1197 efatal(gettext("cannot create lock file (cm4)"));
1198 }
1199 #ifdef HAVE_FCHMOD
1200 fchmod(fd, (mode_t)0644);
1201 #else
1202 chmod(outname, (mode_t)0644);
1203 #endif
1204 out = fdfopen(fd, O_WRONLY|O_BINARY);
1205 while (fgets(line,sizeof(line),in) != NULL) {
1206 pf_ab(line,&pf,1);
1207 pf.pf_date = cutoff;
1208 if (equal(pf.pf_user,user)||getuid()==0) {
1209 if (sp->s_rel == 0) {
1210 if (++cnt) {
1211 if (fflush(out) == EOF)
1212 xmsg(outname, NOGETTEXT("rdpfile"));
1213 #ifdef HAVE_FSYNC
1214 if (fsync(fileno(out)) < 0)
1215 xmsg(outname, NOGETTEXT("rdpfile"));
1216 #endif
1217 if (fclose(out) == EOF)
1218 xmsg(outname, NOGETTEXT("rdpfile"));
1219 fclose(in);
1220 fatal(gettext("missing -r argument (de1)"));
1221 }
1222 goodpf = pf;
1223 continue;
1224 }
1225 else if ((sp->s_rel == pf.pf_nsid.s_rel &&
1226 sp->s_lev == pf.pf_nsid.s_lev &&
1227 sp->s_br == pf.pf_nsid.s_br &&
1228 sp->s_seq == pf.pf_nsid.s_seq) ||
1229 (sp->s_rel == pf.pf_gsid.s_rel &&
1230 sp->s_lev == pf.pf_gsid.s_lev &&
1231 sp->s_br == pf.pf_gsid.s_br &&
1232 sp->s_seq == pf.pf_gsid.s_seq)) {
1233 if (++uniq) {
1234 if (fflush(out) == EOF)
1235 xmsg(outname, NOGETTEXT("rdpfile"));
1236 #ifdef HAVE_FSYNC
1237 if (fsync(fileno(out)) < 0)
1238 xmsg(outname, NOGETTEXT("rdpfile"));
1239 #endif
1240 if (fclose(out) == EOF)
1241 xmsg(outname, NOGETTEXT("rdpfile"));
1242 fclose(in);
1243 fatal(ambig);
1244 }
1245 goodpf = pf;
1246 continue;
1247 }
1248 }
1249 if(fputs(line,out)==EOF)
1250 xmsg(outname, NOGETTEXT("rdpfile"));
1251 }
1252 fflush(stderr);
1253 if (fflush(out) == EOF)
1254 xmsg(outname, NOGETTEXT("rdpfile"));
1255 #ifdef HAVE_FSYNC
1256 if (fsync(fileno(out)) < 0)
1257 xmsg(outname, NOGETTEXT("rdpfile"));
1258 #endif
1259 if (fclose(out) == EOF)
1260 xmsg(outname, NOGETTEXT("rdpfile"));
1261 copy(auxf(pkt->p_file,'p'),Pfilename);
1262 fclose(in);
1263 if (!goodpf.pf_user[0])
1264 fatal(gettext("login name or SID specified not in p-file (de2)"));
1265 return(&goodpf);
1266 }
1267
1268 /*
1269 * Open a FILE * to the diff output.
1270 */
1271 static FILE *
dodiff(newf,oldf,difflim)1272 dodiff(newf,oldf,difflim)
1273 char *newf, *oldf;
1274 int difflim;
1275 {
1276 register int i;
1277 register int n;
1278 int pfd[2];
1279 FILE *iop;
1280 char num[10];
1281 struct rlimit rlim;
1282
1283 if (HADUCD)
1284 return (xfopen(Dfilename, O_RDONLY|O_BINARY));
1285 xpipe(pfd);
1286 if ((i = vfork()) < 0) {
1287 int errsav = errno;
1288
1289 close(pfd[0]);
1290 close(pfd[1]);
1291 errno = errsav;
1292 efatal(gettext("cannot fork, try again (de11)"));
1293 }
1294 else if (i == 0) {
1295 #ifdef set_child_standard_fds
1296 set_child_standard_fds(STDIN_FILENO,
1297 pfd[1],
1298 STDERR_FILENO);
1299 #ifdef F_SETFD
1300 fcntl(pfd[0], F_SETFD, 1);
1301 n = getdtablesize(); /* We are single threaded, so cache */
1302 for (i = 5; i < n; i++)
1303 fcntl(i, F_SETFD, 1);
1304 #endif
1305 #else
1306 close(pfd[0]);
1307 close(1);
1308 dup(pfd[1]);
1309 close(pfd[1]);
1310
1311 #if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
1312 /*
1313 * Set max # of file descriptors to allow bdiff to hold all
1314 * files open and to reduce the number of files to close.
1315 */
1316 getrlimit(RLIMIT_NOFILE, &rlim);
1317 if (rlim.rlim_cur > 20)
1318 rlim.rlim_cur = 20; /* Suffifient for bdiff/diff */
1319 setrlimit(RLIMIT_NOFILE, &rlim);
1320 #endif
1321 n = getdtablesize(); /* We are single threaded, so cache */
1322 for (i = 5; i < n; i++)
1323 close(i);
1324 #endif
1325 sprintf(num, NOGETTEXT("%d"), difflim);
1326 /*
1327 * Since we support un-uuencoded handling of binary data, we
1328 * need to call diff -a with delta -d. It is granted that
1329 * Diffpgmp is a diff(1) version that supports -a.
1330 */
1331 if (HADD) {
1332 #if defined(PROTOTYPES) && defined(INS_BASE)
1333 diffpgm = Diffpgmp;
1334 execl(Diffpgmp, Diffpgmp, "-a", oldf, newf, (char *)0);
1335 #endif
1336 diffpgm = Diffpgm;
1337 execl(Diffpgm, Diffpgm, "-a", oldf, newf, (char *)0);
1338 diffpgm = Diffpgm2;
1339 execl(Diffpgm2, Diffpgm2, "-a", oldf, newf, (char *)0);
1340 #ifdef USE_FSDIFF
1341 } else if (HADB) {
1342 #else
1343 } else {
1344 #endif
1345 #if defined(PROTOTYPES) && defined(INS_BASE)
1346 diffpgm = BDiffpgmp;
1347 execl(BDiffpgmp,BDiffpgmp,oldf,newf,num,"-s", (char *)0);
1348 #endif
1349 diffpgm = BDiffpgm;
1350 execl(BDiffpgm,BDiffpgm,oldf,newf,num,"-s", (char *)0);
1351 diffpgm = BDiffpgm2;
1352 execl(BDiffpgm2,BDiffpgm2,oldf,newf,num,"-s", (char *)0);
1353 #ifdef USE_FSDIFF
1354 } else {
1355 diffpgm = FDiffpgmp;
1356 execl(FDiffpgmp, FDiffpgmp, oldf, newf, (char *)0);
1357 #endif
1358 }
1359 close(1);
1360 _exit(32); /* tell parent that 'execl' failed */
1361 }
1362 else {
1363 close(pfd[1]);
1364 iop = fdfopen(pfd[0], O_RDONLY|O_BINARY);
1365 return(iop);
1366 }
1367 /*NOTREACHED*/
1368 return (0); /* fake for gcc */
1369 }
1370
1371 /*
1372 * Parse the part of the diff output that contains the line numbers and the
1373 * delete/append/change instructions.
1374 */
1375 static int
getdiff(type,plinenum)1376 getdiff(type,plinenum)
1377 register char *type;
1378 register int *plinenum;
1379 {
1380 #define LINE_SIZE 1024 /* not too large for memset() */
1381 long line[LINE_SIZE/sizeof (long) + 1]; /* align to speed up memset() */
1382 register char *p;
1383 int num_lines = 0;
1384 static int chg_num, chg_ln;
1385 int lowline, highline;
1386 int llen;
1387
1388 if ((p = rddiff((char *)line, LINE_SIZE+1, &llen)) == NULL)
1389 return(0);
1390
1391 if (*p == '-') {
1392 *type = INS;
1393 *plinenum = chg_ln;
1394 num_lines = chg_num;
1395 }
1396 else {
1397 p = linerange(p,&lowline,&highline);
1398 *plinenum = lowline;
1399
1400 switch(*p++) {
1401 case 'd':
1402 num_lines = highline - lowline + 1;
1403 *type = DEL;
1404 skipline((char *)line, num_lines);
1405 break;
1406
1407 case 'a':
1408 linerange(p,&lowline,&highline);
1409 num_lines = highline - lowline + 1;
1410 *type = INS;
1411 break;
1412
1413 case 'c':
1414 chg_ln = lowline;
1415 num_lines = highline - lowline + 1;
1416 linerange(p,&lowline,&highline);
1417 chg_num = highline - lowline + 1;
1418 *type = DEL;
1419 skipline((char *)line, num_lines);
1420 break;
1421 }
1422 }
1423
1424 return(num_lines);
1425 }
1426
1427 /*
1428 * Skip the next chunk of kept lines from the old file and then
1429 * insert the new lines from the diff output.
1430 */
1431 static void
insert(pkt,linenum,n,ser,off)1432 insert(pkt, linenum, n, ser, off)
1433 struct packet *pkt;
1434 int linenum, n, ser;
1435 int off;
1436 {
1437 long str[LINE_SIZE/sizeof (long) + 1]; /* align for memset() */
1438 char *s = (char *)str;
1439 int first;
1440 int nonl = 0;
1441 int llen;
1442
1443 /*
1444 * We only count newlines and thus need to add one to number_of_lines
1445 * to get the same view as bdiff/diff.
1446 */
1447 if ((pkt->p_props & CK_NONL) && (linenum + n) >= (number_of_lines+1)) {
1448 if ((pkt->p_encoding & EF_UUENCODE) == 0) {
1449 nonl++;
1450 }
1451 }
1452
1453 after(pkt, linenum);
1454 sprintf(s, NOGETTEXT("%c%c %d\n"), CTLCHAR, INS, ser);
1455 putline(pkt, s);
1456 if (off)
1457 off = 2; /* strlen("> ") from diff */
1458 while (--n >= 0) {
1459 first = 1;
1460 for (;;) { /* Loop over partial line */
1461 if (rddiff(s, LINE_SIZE+1, &llen) == NULL) {
1462 fatal(gettext("Cannot read the diffs file (de19)"));
1463 }
1464 if (first) {
1465 first = 0;
1466 if (n == 0 && nonl) { /* No newline at end */
1467 putctlnnl(pkt); /* ^AN escape */
1468 } else if (s[off] == CTLCHAR) /* ^A escape? */
1469 putctl(pkt);
1470 putlline(pkt, s+off, llen-off); /* Skip "> " */
1471 } else {
1472 putlline(pkt, s, llen);
1473 }
1474 if (s[llen-1] == '\n') {
1475 break;
1476 }
1477 }
1478 }
1479 sprintf(s, NOGETTEXT("%c%c %d\n"), CTLCHAR, END, ser);
1480 putline(pkt, s);
1481 }
1482
1483 /*
1484 * Add delete markers in the weave data.
1485 */
1486 static void
delete(pkt,linenum,n,ser)1487 delete(pkt, linenum, n, ser)
1488 struct packet *pkt;
1489 int linenum, n, ser;
1490 {
1491 char str[BUFSIZ]; /* Only used for ^A lines in the weave */
1492
1493 before(pkt, linenum);
1494 sprintf(str, NOGETTEXT("%c%c %d\n"), CTLCHAR, DEL, ser);
1495 putline(pkt, str);
1496 after(pkt, linenum + n - 1);
1497 sprintf(str, NOGETTEXT("%c%c %d\n"), CTLCHAR, END, ser);
1498 putline(pkt, str);
1499 }
1500
1501 static void
after(pkt,n)1502 after(pkt, n)
1503 struct packet *pkt;
1504 int n;
1505 {
1506 before(pkt, n);
1507 if (pkt->p_glnno == n) {
1508 for (;;) {
1509 if (pkt->p_line[pkt->p_line_length-1] != '\n') {
1510 getline(pkt);
1511 }
1512 else {
1513 putline(pkt, (char *)0);
1514 break;
1515 }
1516 }
1517 }
1518 }
1519
1520 static void
before(pkt,n)1521 before(pkt, n)
1522 struct packet *pkt;
1523 int n;
1524 {
1525 while (pkt->p_glnno < n) {
1526 if (!readmod(pkt))
1527 break;
1528 }
1529 }
1530
1531 /*
1532 * Parse the line range from the diff output
1533 */
1534 static char *
linerange(cp,low,high)1535 linerange(cp, low, high)
1536 char *cp;
1537 int *low, *high;
1538 {
1539 cp = satoi(cp, low);
1540 if (*cp == ',')
1541 cp = satoi(++cp, high);
1542 else
1543 *high = *low;
1544
1545 return(cp);
1546 }
1547
1548 /*
1549 * Skip lines from the diff outout, e.g. lines that start with "< ".
1550 */
1551 static void
skipline(lp,num)1552 skipline(lp, num)
1553 char *lp;
1554 int num;
1555 {
1556 int llen;
1557
1558 for (++num; --num; ) {
1559 do {
1560 (void)rddiff(lp, LINE_SIZE+1, &llen);
1561 } while (lp[llen-1] != '\n');
1562 }
1563 }
1564
1565 /*
1566 * This is the central read routine.
1567 * fgets() is a faulty construction as it does not return the # of bytes read.
1568 * fgets() always nul terminates the bytes read, so filling the buffer allows
1569 * to detect how many bytes have been read.
1570 */
1571 static char *
rddiff(s,n,ll)1572 rddiff(s, n, ll)
1573 char *s; /* Buffer to fill */
1574 int n; /* size of buffer */
1575 int *ll; /* # of bytes read */
1576 {
1577 char *r;
1578
1579 (void) memset(s, '\377', n); /* Filling allows to detect real end */
1580 if ((r = fgets(s, n, Diffin)) != NULL) {
1581 int l = strlen(r);
1582
1583 if (r[l-1] != '\n') { /* Rare case: too long or with nul */
1584 for (l = n; --l >= 0; )
1585 if (r[l] == '\0')
1586 break;
1587 }
1588 *ll = l;
1589 if (HADP) {
1590 if (fwrite(s, 1, l, gpkt.p_stdout) != l)
1591 FAILPUT;
1592 }
1593 }
1594 return (r);
1595 }
1596
1597 static void
enter(pkt,ch,n,sidp)1598 enter(pkt,ch,n,sidp)
1599 struct packet *pkt;
1600 char ch;
1601 int n;
1602 struct sid *sidp;
1603 {
1604 char str[SID_STRSIZE];
1605 register struct apply *ap;
1606
1607 sid_ba(sidp,str);
1608 ap = &pkt->p_apply[n];
1609 if (pkt->p_cutoff > pkt->p_idel[n].i_datetime.tv_sec)
1610 switch(ap->a_code) {
1611
1612 case SX_EMPTY:
1613 switch (ch) {
1614 case INCLUDE:
1615 condset(ap,APPLY,INCLUSER);
1616 break;
1617 case EXCLUDE:
1618 condset(ap,NOAPPLY,EXCLUSER);
1619 break;
1620 case IGNORE:
1621 condset(ap,SX_EMPTY,IGNRUSER);
1622 break;
1623 }
1624 break;
1625 case APPLY:
1626 fatal(gettext("internal error in delta/enter() (de5)"));
1627 break;
1628 case NOAPPLY:
1629 fatal(gettext("internal error in delta/enter() (de6)"));
1630 break;
1631 default:
1632 fatal(gettext("internal error in delta/enter() (de7)"));
1633 break;
1634 }
1635 }
1636
1637 static void
clean_up()1638 clean_up()
1639 {
1640 uname(&un);
1641 uuname = un.nodename;
1642 if (mylock(auxf(gpkt.p_file,'z'), getpid(),uuname)) {
1643 sclose(&gpkt);
1644 sfree(&gpkt);
1645 if (gpkt.p_xiop) {
1646 fclose(gpkt.p_xiop);
1647 gpkt.p_xiop = NULL;
1648 unlink(auxf(gpkt.p_file,'x'));
1649 }
1650 if(Gin) {
1651 fclose(Gin);
1652 Gin = NULL;
1653 }
1654 unlink(auxf(gpkt.p_file,'d'));
1655 unlink(auxf(gpkt.p_file,'q'));
1656 if (gpkt.p_encoding & EF_UUENCODE) {
1657 unlink(auxf(gpkt.p_file,'e'));
1658 }
1659 xrm(&gpkt);
1660 ffreeall();
1661 uname(&un);
1662 uuname = un.nodename;
1663 timersetlockfile(NULL);
1664 if (!islockchset(Zhold))
1665 unlockit(Zhold, getpid(), uuname);
1666 }
1667 if (SETHOME_CHSET() && !processed_files) {
1668 unlockchset(getpid(), uuname);
1669 }
1670 }
1671
1672 /*
1673 * Compute the checksum for the new version and check for characters that
1674 * are not allowed in the file.
1675 *
1676 * SCCSv4 disallows ^A (0x01) at the start of a line, nul bytes and requires a
1677 * newline at the end of the file.
1678 *
1679 * SCCSv6 currently disallows nul bytes in the file.
1680 * Since our diff(1) implementation supports to treat even files with
1681 * nul bytes as text files, we could support nul bytes in the future.
1682 *
1683 * Both versions allow an unlimited line length.
1684 *
1685 * Since fgets() does not report the amount of bytes read, we cannot support
1686 * nul bytes on platforms with record oriented IO (VMS).
1687 */
1688 /*ARGSUSED*/
1689 static void
fgetchk(file,pkt)1690 fgetchk(file, pkt)
1691 char *file;
1692 struct packet *pkt;
1693 {
1694 FILE *inptr;
1695 #ifndef RECORD_IO
1696 char *p = NULL; /* Intialize to make gcc quiet */
1697 char *pn = NULL;
1698 char line[VBUF_SIZE+1];
1699 #else
1700 char line[BUFSIZ];
1701 int search_on = 0;
1702 #endif
1703 off_t nline;
1704 off_t soh = -1; /* Last line # that starts with ^A */
1705 int isctl;
1706 int idx = 0;
1707 int warned = 0;
1708 int nwarned = 0;
1709 char chkflags = 0;
1710 char lastchar;
1711 unsigned int sum = 0;
1712 int pktv6 = pkt->p_flags & PF_V6;
1713
1714 inptr = xfopen(file, O_RDONLY|O_BINARY);
1715 #ifdef USE_SETVBUF
1716 setvbuf(inptr, NULL, _IOFBF, VBUF_SIZE);
1717 #endif
1718 /*
1719 * This gives the illusion that a zero-length file ends
1720 * in a newline so that it won't be mistaken for a
1721 * binary file.
1722 */
1723 lastchar = '\n';
1724 (void)memset(line, '\377', sizeof (line));
1725 nline = 0;
1726 #ifndef RECORD_IO
1727 /*
1728 * In most cases (non record oriented I/O), we can optimize the way we
1729 * scan files for '\0' bytes, line-ends '\n' and ^A '\1'. The optimized
1730 * algorithm allows to avoid to do a reverse scan for '\0' from the end
1731 * of the buffer.
1732 */
1733 while ((idx = fread(line, 1, sizeof (line) - 1, inptr)) > 0) {
1734 sum += usum(line, idx);
1735 if (lastchar == '\n' && line[0] == CTLCHAR) {
1736 chkflags |= CK_CTLCHAR;
1737 soh = nline;
1738 if (pktv6 == 0)
1739 goto err;
1740 if (!warned) {
1741 warnctl(file, nline+1);
1742 warned = 1;
1743 }
1744 }
1745 lastchar = line[idx-1];
1746 p = findbytes(line, idx, '\0');
1747 if (p != NULL) {
1748 chkflags |= CK_NULL;
1749 pn = p;
1750 }
1751 for (p = line;
1752 (p = findbytes(p, idx - (p-line), '\n')) != NULL; p++) {
1753 if (pn && p > pn) { /* '\0' before '\n' */
1754 if (pktv6) {
1755 if (!nwarned) {
1756 warnnull(file, nline);
1757 nwarned = 1;
1758 }
1759 } else {
1760 goto err;
1761 }
1762 }
1763 nline++;
1764 if ((p - line) >= (idx-1)) /* '\n' last in buf */
1765 break;
1766
1767 if (p[1] == CTLCHAR) {
1768 chkflags |= CK_CTLCHAR;
1769 err:
1770 if (pktv6 &&
1771 (p[1] == CTLCHAR)) {
1772 if (!warned) {
1773 warnctl(file, nline+1);
1774 warned = 1;
1775 }
1776 continue;
1777 }
1778 fclose(inptr);
1779 isctl = soh == nline;
1780 sprintf(SccsError,
1781 gettext(
1782 isctl ?
1783 "file '%s' begins with '\\001' on line %jd (de20)":
1784 "file '%s' contains '\\000' on line %jd (de14)"),
1785 file, (Intmax_t)++nline);
1786 fatal(SccsError);
1787 }
1788 }
1789 line[idx] = '\0';
1790 if (pkt->p_did_id == 0) {
1791 pkt->p_did_id =
1792 chkid(line, pkt->p_sflags[IDFLAG - 'a'], pkt->p_sflags);
1793 }
1794 }
1795 #else /* !RECORD_IO */
1796 /*
1797 * We support nul bytes with fgets() by pre-filling the buffer.
1798 */
1799 while (fgets(line, sizeof (line), inptr) != NULL) {
1800 if (lastchar == '\n' && line[0] == CTLCHAR) {
1801 chkflags |= CK_CTLCHAR;
1802 soh = nline;
1803 if (pktv6 == 0) {
1804 nline++;
1805 goto err;
1806 }
1807 if (!warned) {
1808 warnctl(file, nline+1);
1809 warned = 1;
1810 }
1811 }
1812 search_on = 0;
1813 for (idx = sizeof (line)-1; idx >= 0; idx--) {
1814 if (search_on > 0) {
1815 if (line[idx] == '\0') {
1816 chkflags |= CK_NULL;
1817 err:
1818 if (pktv6) {
1819 if ((chkflags & CK_NULL) && !nwarned) {
1820 warnnull(file, nline);
1821 nwarned = 1;
1822 }
1823 continue;
1824 }
1825 fclose(inptr);
1826 isctl = soh == nline;
1827 sprintf(SccsError,
1828 gettext(
1829 isctl ?
1830 "file '%s' begins with '\\001' on line %jd (de20)":
1831 "file '%s' contains '\\000' on line %jd (de14)"),
1832 file, (Intmax_t)nline);
1833 fatal(SccsError);
1834 }
1835 } else {
1836 if (line[idx] == '\0') {
1837 sum += usum(line, idx);
1838 search_on = 1;
1839 lastchar = line[idx-1];
1840 if (lastchar == '\n') {
1841 nline++;
1842 }
1843 }
1844 }
1845 }
1846 if (pkt->p_did_id == 0) {
1847 pkt->p_did_id =
1848 chkid(line, pkt->p_sflags[IDFLAG - 'a'], pkt->p_sflags);
1849 }
1850 (void)memset(line, '\377', sizeof (line));
1851 }
1852 #endif /* !RECORD_IO */
1853 fclose(inptr);
1854
1855 pkt->p_ghash = sum & 0xFFFF;
1856 pkt->p_glines = nline;
1857
1858 if (lastchar != '\n')
1859 chkflags |= CK_NONL;
1860 pkt->p_props |= chkflags;
1861
1862 if (chkflags & CK_NONL) {
1863 #ifndef RECORD_IO
1864 if (!pktv6)
1865 if (pn && nline == 0) /* Found null byte but no newline */
1866 goto err;
1867 #endif
1868 if (pktv6 == 0) {
1869 sprintf(SccsError,
1870 gettext("No newline at end of file '%s' (de18)"),
1871 file);
1872 fatal(SccsError);
1873 } else {
1874 fprintf(stderr,
1875 gettext("WARNING [%s]: No newline at end of file (de18)\n"),
1876 file);
1877 }
1878 }
1879 }
1880
1881 static void
warnctl(file,nline)1882 warnctl(file, nline)
1883 char *file;
1884 off_t nline;
1885 {
1886 fprintf(stderr,
1887 gettext(
1888 "WARNING [%s]: line %jd begins with ^A\n"),
1889 file, (Intmax_t)nline);
1890 }
1891
1892 static void
warnnull(file,nline)1893 warnnull(file, nline)
1894 char *file;
1895 off_t nline;
1896 {
1897 fprintf(stderr,
1898 gettext(
1899 "WARNING [%s]: line %jd contains a '\\000'\n"),
1900 file, (Intmax_t)nline);
1901 }
1902
1903 static int
fixghash(pkt,ser)1904 fixghash(pkt, ser)
1905 struct packet *pkt;
1906 int ser;
1907 {
1908 char ghbuf[10];
1909 signed char *q;
1910 struct packet pk2;
1911 struct stats stats;
1912
1913 putline(pkt,(char *) 0); /* Flush last unwritten line */
1914 fflush(pkt->p_xiop); /* Flush stdio buffers */
1915
1916 sinit(&pk2, auxf(pkt->p_file,'x'), SI_OPEN|SI_FORCE);
1917
1918 pk2.do_chksum = 0; /* Checksums are still wrong */
1919 pk2.p_stdout = stderr;
1920 pk2.p_cutoff = MAX_TIME;
1921
1922 if (dodelt(&pk2, &stats, (struct sid *) 0, 0) == 0)
1923 fmterr(&pk2);
1924 flushto(&pk2, EUSERTXT, FLUSH_NOCOPY);
1925
1926 pk2.p_chkeof = 1;
1927 pk2.p_gotsid = pk2.p_idel[ser].i_sid;
1928 pk2.p_reqsid = pk2.p_gotsid;
1929
1930 setup(&pk2, ser);
1931
1932 pk2.p_ghash = 0;
1933 while (readmod(&pk2)) /* Compute ghash for gotten content */
1934 ;
1935 pk2.p_ghash &= 0xFFFF;
1936
1937 fseek(pkt->p_xiop, Checksum_offset, SEEK_SET);
1938 fprintf(pkt->p_xiop, "%c%c s %5.5d\n",
1939 CTLCHAR, SIDEXTENS, pk2.p_ghash);
1940 pkt->p_nhash -= 5 * '0';
1941 sprintf(ghbuf, "%5.5d", pk2.p_ghash);
1942 q = (signed char *) ghbuf;
1943 while (*q)
1944 pkt->p_nhash += *q++;
1945
1946 return (pk2.p_ghash);
1947 }
1948
1949 /* SVR4.0 does not support getdtablesize(). */
1950
1951 #ifndef HAVE_GETDTABLESIZE
1952 int
getdtablesize()1953 getdtablesize()
1954 {
1955 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
1956 struct rlimit rlim;
1957
1958 rlim.rlim_cur = 20;
1959 getrlimit(RLIMIT_NOFILE, &rlim);
1960 return (rlim.rlim_cur);
1961 #endif
1962 return (20);
1963 }
1964 #endif
1965
1966 /*
1967 * Since we introduced a global lock file, this is needed in order to
1968 * be able to remove the global lock file in case that no real work has
1969 * yet been done. This typically helps to automatically remove the global
1970 * lock file if you type ^C while delta(1) is asking for delta comments and
1971 * did not yet process any file.
1972 */
1973 static RETSIGTYPE
intr(sig)1974 intr(sig)
1975 int sig;
1976 {
1977 did_intr = TRUE;
1978 if (!processed_files) {
1979 clean_up();
1980 exit(2);
1981 }
1982 }
1983
1984 static void
setintr()1985 setintr()
1986 {
1987 #if defined(HAVE_SIGPROCMASK) && defined(SA_RESTART)
1988 struct sigaction sa;
1989
1990 sigemptyset(&sa.sa_mask);
1991 sa.sa_handler = intr;
1992 sa.sa_flags = SA_RESTART;
1993 (void) sigaction(SIGINT, &sa, (struct sigaction *)0);
1994 #else
1995 #ifdef HAVE_SIGSETMASK
1996 struct sigvec sv;
1997
1998 sv.sv_mask = 0;
1999 sv.sv_handler = intr;
2000 sv.sv_flags = 0;
2001 (void) sigvec(SIGINT, &sv, (struct sigvec *)0);
2002 #else
2003 #ifdef HAVE_SIGSET
2004 sigset(SIGINT, intr);
2005 #else
2006 signal(SIGINT, intr);
2007 #endif
2008 #endif
2009 #endif
2010 }
2011