1 # include	"../hdr/defines.h"
2 # include	"../hdr/had.h"
3 
4 static char Sccsid[] = "@(#)delta.c	4.10	02/02/88";
5 USXALLOC();
6 
7 # ifdef LOGDELTA
8 char	LogFile[] = "/usr/adm/sccs-log";
9 FILE	*Logf;
10 # endif
11 
12 char	Diffpgm[] = "/usr/local/bdiff";
13 FILE	*Diffin;
14 int	Debug = 0;
15 struct packet gpkt;
16 struct sid sid;
17 int	num_files;
18 char	had[26];
19 char	*ilist, *elist, *glist;
20 char	*Comments, *Mrs;
21 int	Domrs;
22 int	verbosity;
23 int	Did_id;
24 long	Szqfile;
25 char	Pfilename[FILESIZE];
26 FILE	*Xiop;
27 int	Xcreate;
28 
29 main(argc,argv)
30 int argc;
31 register char *argv[];
32 {
33 	register int i;
34 	register char *p;
35 	char c;
36 	int testmore;
37 	extern delta();
38 	extern int Fcnt;
39 
40 	Fflags = FTLEXIT | FTLMSG | FTLCLN;
41 	for(i=1; i<argc; i++)
42 		if(argv[i][0] == '-' && (c=argv[i][1])) {
43 			p = &argv[i][2];
44 			testmore = 0;
45 			switch (c) {
46 
47 			case 'r':
48 				if (!p[0]) {
49 					argv[i] = 0;
50 					continue;
51 				}
52 				chksid(sid_ab(p,&sid),&sid);
53 				break;
54 			case 'g':
55 				glist = p;
56 				break;
57 			case 'y':
58 				Comments = p;
59 				break;
60 			case 'm':
61 				Mrs = p;
62 				break;
63 			case 'p':
64 			case 'n':
65 			case 's':
66 				testmore++;
67 				break;
68 			default:
69 				fatal("unknown key letter (cm1)");
70 			}
71 
72 			if (testmore) {
73 				testmore = 0;
74 				if (*p) {
75 					sprintf(Error, "value after %c arg (cm7)",c);
76 					fatal(Error);
77 				}
78 			}
79 			if (had[c - 'a']++)
80 				fatal("key letter twice (cm2)");
81 			argv[i] = 0;
82 		}
83 		else num_files++;
84 
85 	if (num_files == 0)
86 		fatal("missing file arg (cm3)");
87 	if (!HADS)
88 		verbosity = -1;
89 # ifdef LOGDELTA
90 	Logf = fopen(LogFile, "a");
91 # endif
92 	setsig();
93 	Fflags &= ~FTLEXIT;
94 	Fflags |= FTLJMP;
95 	for (i=1; i<argc; i++)
96 		if (p=argv[i])
97 			do_file(p,delta);
98 # ifdef LOGDELTA
99 	if (Logf != NULL)
100 		fclose(Logf);
101 # endif
102 	exit(Fcnt ? 1 : 0);
103 }
104 
105 
106 delta(file)
107 char *file;
108 {
109 	static int first = 1;
110 	register char *p;
111 	int n, linenum;
112 	char type;
113 	register int ser;
114 	extern char had_dir, had_standinp;
115 	extern char *Sflags[];
116 	char dfilename[FILESIZE];
117 	char gfilename[FILESIZE];
118 	char line[512];
119 	FILE *gin;
120 	struct stats stats;
121 	struct pfile *pp;
122 	int inserted, deleted, orig;
123 	int newser;
124 	int status;
125 	int diffloop;
126 	int difflim;
127 
128 	if (setjmp(Fjmp))
129 		return;
130 	if (first) {
131 		first = 0;
132 		dohist(file);
133 	}
134 	sinit(&gpkt,file,1);
135 	if (lockit(auxf(gpkt.p_file,'z'),2,getpid()))
136 		fatal("cannot create lock file (cm4)");
137 	gpkt.p_reopen = 1;
138 	gpkt.p_stdout = stdout;
139 	copy(auxf(gpkt.p_file,'g'),gfilename);
140 	gin = xfopen(gfilename,0);
141 	pp = rdpfile(&gpkt,&sid);
142 	gpkt.p_cutoff = pp->pf_date;
143 	ilist = pp->pf_ilist;
144 	elist = pp->pf_elist;
145 
146 	if (dodelt(&gpkt,&stats,0,0) == 0)
147 		fmterr(&gpkt);
148 	if ((ser = sidtoser(&pp->pf_gsid,&gpkt)) == 0 ||
149 		sidtoser(&pp->pf_nsid,&gpkt))
150 			fatal("invalid sid in p-file (de3)");
151 	doie(&gpkt,ilist,elist,glist);
152 	setup(&gpkt,ser);
153 	finduser(&gpkt);
154 	doflags(&gpkt);
155 	bcopy(&pp->pf_nsid,&gpkt.p_reqsid,sizeof(gpkt.p_reqsid));
156 	permiss(&gpkt);
157 	flushto(&gpkt,EUSERTXT,1);
158 	gpkt.p_chkeof = 1;
159 	copy(auxf(gpkt.p_file,'d'),dfilename);
160 	gpkt.p_gout = xfcreat(dfilename,0444);
161 	while(readmod(&gpkt)) {
162 		chkid(gpkt.p_line);
163 		fputs(gpkt.p_line,gpkt.p_gout);
164 	}
165 	fclose(gpkt.p_gout);
166 	orig = gpkt.p_glnno;
167 	gpkt.p_glnno = 0;
168 	gpkt.p_verbose = verbosity;
169 	Did_id = 0;
170 	while (fgets(line,sizeof(line),gin) != NULL && !chkid(line))
171 		;
172 	fclose(gin);
173 	if (gpkt.p_verbose && (num_files > 1 || had_dir || had_standinp))
174 		fprintf(gpkt.p_stdout,"\n%s:\n",gpkt.p_file);
175 	if (!Did_id)
176 		if (Sflags[IDFLAG - 'a'])
177 			fatal("no id keywords (cm6)");
178 		else if (gpkt.p_verbose)
179 			fprintf(stderr,"No id keywords (cm7)\n");
180 
181 	/*
182 	The following while loop executes 'bdiff' on g-file and
183 	d-file. If 'bdiff' fails (usually because segmentation
184 	limit it is using is too large for 'diff'), it is
185 	invoked again, with a lower segmentation limit.
186 	*/
187 	difflim = 3500;
188 	diffloop = 0;
189 	while (1) {
190 		inserted = deleted = 0;
191 		gpkt.p_glnno = 0;
192 		gpkt.p_upd = 1;
193 		gpkt.p_wrttn = 1;
194 		getline(&gpkt);
195 		gpkt.p_wrttn = 1;
196 		newser = mkdelt(&gpkt,&pp->pf_nsid,&pp->pf_gsid,
197 						diffloop,orig);
198 		diffloop = 1;
199 		flushto(&gpkt,EUSERTXT,0);
200 		Diffin = dodiff(auxf(gpkt.p_file,'g'),dfilename,difflim);
201 		while (n = getdiff(&type,&linenum)) {
202 			if (type == INS) {
203 				inserted += n;
204 				insert(&gpkt,linenum,n,newser);
205 			}
206 			else {
207 				deleted += n;
208 				delete(&gpkt,linenum,n,newser);
209 			}
210 		}
211 		fclose(Diffin);
212 		if (gpkt.p_iop)
213 			while (readmod(&gpkt))
214 				;
215 		wait(&status);
216 		if (status) {		/* diff failed */
217 			/*
218 			Check top byte (exit code of child).
219 			*/
220 			if (((status >> 8) & 0377) == 32) { /* 'execl' failed */
221 				sprintf(Error, "cannot execute '%s' (de12)", Diffpgm);
222 				fatal(Error);
223 			}
224 			/*
225 			Re-try.
226 			*/
227 			if (difflim -= 500) {	/* reduce segmentation */
228 				fprintf(stderr,
229 			"'%s' failed, re-trying, segmentation = %d (de13)\n",
230 					Diffpgm,difflim);
231 				fclose(Xiop);	/* set up */
232 				Xiop = 0;	/* for new x-file */
233 				Xcreate = 0;
234 				/*
235 				Re-open s-file.
236 				*/
237 				gpkt.p_iop = xfopen(gpkt.p_file,0);
238 				setbuf(gpkt.p_iop,gpkt.p_buf);
239 				/*
240 				Reset counters.
241 				*/
242 				gpkt.p_slnno = 0;
243 				gpkt.p_ihash = 0;
244 				gpkt.p_chash = 0;
245 				gpkt.p_nhash = 0;
246 				gpkt.p_keep = 0;
247 			}
248 			else
249 				/* tried up to 500 lines, can't go on */
250 				fatal("diff failed (de4)");
251 		}
252 		else {		/* no need to try again, worked */
253 			break;			/* exit while loop */
254 		}
255 	}
256 	unlink(dfilename);
257 	stats.s_ins = inserted;
258 	stats.s_del = deleted;
259 	stats.s_unc = orig - deleted;
260 	if (gpkt.p_verbose) {
261 		fprintf(gpkt.p_stdout,"%u inserted\n",stats.s_ins);
262 		fprintf(gpkt.p_stdout,"%u deleted\n",stats.s_del);
263 		fprintf(gpkt.p_stdout,"%u unchanged\n",stats.s_unc);
264 	}
265 	flushline(&gpkt,&stats);
266 	rename(auxf(gpkt.p_file,'x'),gpkt.p_file);
267 	if (Szqfile)
268 		rename(auxf(&gpkt.p_file,'q'),Pfilename);
269 	else {
270 		xunlink(Pfilename);
271 		xunlink(auxf(&gpkt.p_file,'q'));
272 	}
273 	clean_up(0);
274 	if (!HADN) {
275 		setuid(getuid());
276 		unlink(gfilename);
277 	}
278 }
279 
280 
281 mkdelt(pkt,sp,osp,diffloop,orig_nlines)
282 struct packet *pkt;
283 struct sid *sp, *osp;
284 int diffloop;
285 int orig_nlines;
286 {
287 	extern long Timenow;
288 	struct deltab dt;
289 	char str[128];
290 	int newser;
291 	extern char *Sflags[];
292 	register char *p;
293 	int ser_inc, opred, nulldel;
294 
295 	if (!diffloop && pkt->p_verbose) {
296 		sid_ba(sp,str);
297 		fprintf(pkt->p_stdout,"%s\n",str);
298 	}
299 	sprintf(str,"%c%c00000\n",CTLCHAR,HEAD);
300 	putline(pkt,str);
301 	newstats(pkt,str,"0");
302 	bcopy(sp,&dt.d_sid,sizeof(dt.d_sid));
303 
304 	/*
305 	Check if 'null' deltas should be inserted
306 	(only if 'null' flag is in file and
307 	releases are being skipped) and set
308 	'nulldel' indicator appropriately.
309 	*/
310 	if (Sflags[NULLFLAG - 'a'] && (sp->s_rel > osp->s_rel + 1) &&
311 			!sp->s_br && !sp->s_seq &&
312 			!osp->s_br && !osp->s_seq)
313 		nulldel = 1;
314 	else
315 		nulldel = 0;
316 	/*
317 	Calculate how many serial numbers are needed.
318 	*/
319 	if (nulldel)
320 		ser_inc = sp->s_rel - osp->s_rel;
321 	else
322 		ser_inc = 1;
323 	/*
324 	Find serial number of the new delta.
325 	*/
326 	newser = dt.d_serial = maxser(pkt) + ser_inc;
327 	/*
328 	Find old predecessor's serial number.
329 	*/
330 	opred = sidtoser(osp,pkt);
331 	if (nulldel)
332 		dt.d_pred = newser - 1;	/* set predecessor to 'null' delta */
333 	else
334 		dt.d_pred = opred;
335 	dt.d_datetime = Timenow;
336 	substr(logname(),dt.d_pgmr,0,LNLNAM);
337 	dt.d_type = 'D';
338 	del_ba(&dt,str);
339 	putline(pkt,str);
340 # ifdef LOGDELTA
341 	if (Logf != NULL) {
342 		if (pkt->p_file[0] != '/') {
343 			char buf[1024];
344 
345 			if (getwd(buf) != NULL)
346 				fprintf(Logf, "%s/", buf);
347 		}
348 		fprintf(Logf, "%s:\n%s%s\n", pkt->p_file, str + 5, Comments);
349 	}
350 # endif
351 	if (ilist)
352 		mkixg(pkt,INCLUSER,INCLUDE);
353 	if (elist)
354 		mkixg(pkt,EXCLUSER,EXCLUDE);
355 	if (glist)
356 		mkixg(pkt,IGNRUSER,IGNORE);
357 	if (Mrs) {
358 		if (!(p = Sflags[VALFLAG - 'a']))
359 			fatal("MRs not allowed (de8)");
360 		if (*p && !diffloop && valmrs(pkt,p))
361 			fatal("invalid MRs (de9)");
362 		putmrs(pkt);
363 	}
364 	else if (Sflags[VALFLAG - 'a'])
365 		fatal("MRs required (de10)");
366 	sprintf(str,"%c%c ",CTLCHAR,COMMENTS);
367 	putline(pkt,str);
368 	putline(pkt,Comments);
369 	putline(pkt,"\n");
370 	sprintf(str,CTLSTR,CTLCHAR,EDELTAB);
371 	putline(pkt,str);
372 	if (nulldel)			/* insert 'null' deltas */
373 		while (--ser_inc) {
374 			sprintf(str,"%c%c %s/%s/%05u\n", CTLCHAR, STATS, "00000", "00000", orig_nlines);
375 			putline(pkt,str);
376 			dt.d_sid.s_rel -= 1;
377 			dt.d_serial -= 1;
378 			if (ser_inc != 1)
379 				dt.d_pred -= 1;
380 			else
381 				dt.d_pred = opred;	/* point to old pred */
382 			del_ba(&dt,str);
383 			putline(pkt,str);
384 			sprintf(str,"%c%c ",CTLCHAR,COMMENTS);
385 			putline(pkt,str);
386 			putline(pkt,"AUTO NULL DELTA\n");
387 			sprintf(str,CTLSTR,CTLCHAR,EDELTAB);
388 			putline(pkt,str);
389 		}
390 	return(newser);
391 }
392 
393 
394 mkixg(pkt,reason,ch)
395 struct packet *pkt;
396 int reason;
397 char ch;
398 {
399 	int n;
400 	char str[512];
401 
402 	sprintf(str,"%c%c",CTLCHAR,ch);
403 	putline(pkt,str);
404 	for (n = maxser(pkt); n; n--) {
405 		if (pkt->p_apply[n].a_reason == reason) {
406 			sprintf(str," %u",n);
407 			putline(pkt,str);
408 		}
409 	}
410 	putline(pkt,"\n");
411 }
412 
413 
414 putmrs(pkt)
415 struct packet *pkt;
416 {
417 	register char **argv;
418 	char str[64];
419 	extern char *Varg[];
420 
421 	for (argv = &Varg[VSTART]; *argv; argv++) {
422 		sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv);
423 		putline(pkt,str);
424 	}
425 }
426 
427 
428 rdpfile(pkt,sp)
429 register struct packet *pkt;
430 struct sid *sp;
431 {
432 	char *user;
433 	struct pfile pf;
434 	static struct pfile goodpf;
435 	char line[512];
436 	int cnt, root;
437 	FILE *in, *out;
438 
439 	cnt = -1;
440 	user = logname();
441 	bzero(&goodpf,sizeof(goodpf));
442 	in = xfopen(auxf(pkt->p_file,'p'),0);
443 	out = xfcreat(auxf(pkt->p_file,'q'),0644);
444 	root = getuid() == 0;
445 	while (fgets(line,sizeof(line),in) != NULL) {
446 		pf_ab(line,&pf,1);
447 		if (root || equal(pf.pf_user,user)) {
448 			if (sp->s_rel == 0) {
449 				if (++cnt) {
450 					fclose(out);
451 					fclose(in);
452 					fatal("missing -r argument (de1)");
453 				}
454 				bcopy(&pf,&goodpf,sizeof(pf));
455 				continue;
456 			}
457 			else if (sp->s_rel == pf.pf_gsid.s_rel &&
458 				sp->s_lev == pf.pf_gsid.s_lev &&
459 				sp->s_br == pf.pf_gsid.s_br &&
460 				sp->s_seq == pf.pf_gsid.s_seq) {
461 					bcopy(&pf,&goodpf,sizeof(pf));
462 					continue;
463 			}
464 		}
465 		fputs(line,out);
466 	}
467 	fflush(out);
468 	fstat(fileno(out),&Statbuf);
469 	Szqfile = Statbuf.st_size;
470 	copy(auxf(pkt->p_file,'p'),Pfilename);
471 	fclose(out);
472 	fclose(in);
473 	if (!goodpf.pf_user[0])
474 		fatal("not in p-file (de2)");
475 	return(&goodpf);
476 }
477 
478 
479 dodiff(newf,oldf,difflim)
480 char *newf, *oldf;
481 int difflim;
482 {
483 	register int i;
484 	int pfd[2];
485 	FILE *iop;
486 	extern char Diffpgm[];
487 	char num[10];
488 
489 	xpipe(pfd);
490 	if ((i = fork()) < 0) {
491 		close(pfd[0]);
492 		close(pfd[1]);
493 		fatal("cannot fork, try again (de11)");
494 	}
495 	else if (i == 0) {
496 		close(pfd[0]);
497 		dup2(pfd[1], 1);
498 		if (pfd[1] != 1)
499 			close(pfd[1]);
500 		for (i = getdtablesize(); i > 4; i--)
501 			close(i);
502 		sprintf(num,"%d",difflim);
503 		execl(Diffpgm,Diffpgm,oldf,newf,num,"-s",0);
504 		close(1);
505 		exit(32);	/* tell parent that 'execl' failed */
506 	}
507 	else {
508 		close(pfd[1]);
509 		iop = fdopen(pfd[0],"r");
510 		return(iop);
511 	}
512 }
513 
514 
515 getdiff(type,plinenum)
516 register char *type;
517 register int *plinenum;
518 {
519 	char line[512];
520 	register char *p;
521 	int num_lines;
522 	static int chg_num, chg_ln;
523 	int lowline, highline;
524 
525 	if ((p = rddiff(line,sizeof (line))) == NULL)
526 		return(0);
527 
528 	if (*p == '-') {
529 		*type = INS;
530 		*plinenum = chg_ln;
531 		num_lines = chg_num;
532 	}
533 	else {
534 		p = linerange(p,&lowline,&highline);
535 		*plinenum = lowline;
536 
537 		switch(*p++) {
538 		case 'd':
539 			num_lines = highline - lowline + 1;
540 			*type = DEL;
541 			skiplines(line,num_lines);
542 			break;
543 
544 		case 'a':
545 			linerange(p,&lowline,&highline);
546 			num_lines = highline - lowline + 1;
547 			*type = INS;
548 			break;
549 
550 		case 'c':
551 			chg_ln = lowline;
552 			num_lines = highline - lowline + 1;
553 			linerange(p,&lowline,&highline);
554 			chg_num = highline - lowline + 1;
555 			*type = DEL;
556 			skiplines(line,num_lines);
557 			break;
558 		}
559 	}
560 
561 	return(num_lines);
562 }
563 
564 
565 insert(pkt,linenum,n,ser)
566 register struct packet *pkt;
567 register int linenum;
568 register int n;
569 int ser;
570 {
571 	char str[512];
572 
573 	after(pkt,linenum);
574 	sprintf(str,"%c%c %u\n",CTLCHAR,INS,ser);
575 	putline(pkt,str);
576 	for (++n; --n; ) {
577 		rddiff(str,sizeof(str));
578 		putline(pkt,&str[2]);
579 	}
580 	sprintf(str,"%c%c %u\n",CTLCHAR,END,ser);
581 	putline(pkt,str);
582 }
583 
584 
585 delete(pkt,linenum,n,ser)
586 register struct packet *pkt;
587 register int linenum;
588 int n;
589 register int ser;
590 {
591 	char str[512];
592 
593 	before(pkt,linenum);
594 	sprintf(str,"%c%c %u\n",CTLCHAR,DEL,ser);
595 	putline(pkt,str);
596 	after(pkt,linenum + n - 1);
597 	sprintf(str,"%c%c %u\n",CTLCHAR,END,ser);
598 	putline(pkt,str);
599 }
600 
601 
602 after(pkt,n)
603 register struct packet *pkt;
604 register int n;
605 {
606 	before(pkt,n);
607 	if (pkt->p_glnno == n)
608 		putline(pkt,0);
609 }
610 
611 
612 before(pkt,n)
613 register struct packet *pkt;
614 register int n;
615 {
616 	while (pkt->p_glnno < n) {
617 		if (!readmod(pkt))
618 			break;
619 	}
620 }
621 
622 
623 linerange(cp,low,high)
624 register char *cp;
625 register int *low, *high;
626 {
627 	cp = satoi(cp,low);
628 	if (*cp == ',')
629 		cp = satoi(++cp,high);
630 	else
631 		*high = *low;
632 
633 	return(cp);
634 }
635 
636 
637 skiplines(lp,num)
638 register char *lp;
639 register int num;
640 {
641 	for (++num;--num;)
642 		rddiff(lp,512);
643 }
644 
645 
646 rddiff(s,n)
647 register char *s;
648 register int n;
649 {
650 	register int r;
651 
652 	if ((r = fgets(s,n,Diffin)) != NULL && HADP)
653 		fputs(s,gpkt.p_stdout);
654 	return(r);
655 }
656 
657 
658 enter(pkt,ch,n,sidp)
659 struct packet *pkt;
660 char ch;
661 int n;
662 struct sid *sidp;
663 {
664 	char str[32];
665 	register struct apply *ap;
666 
667 	sid_ba(sidp,str);
668 	ap = &pkt->p_apply[n];
669 	if (pkt->p_cutoff > pkt->p_idel[n].i_datetime)
670 		switch(ap->a_code) {
671 
672 		case EMPTY:
673 			switch (ch) {
674 			case INCLUDE:
675 				condset(ap,APPLY,INCLUSER);
676 				break;
677 			case EXCLUDE:
678 				condset(ap,NOAPPLY,EXCLUSER);
679 				break;
680 			case IGNORE:
681 				condset(ap,EMPTY,IGNRUSER);
682 				break;
683 			}
684 			break;
685 		case APPLY:
686 			fatal("internal error in delta/enter() (de5)");
687 			break;
688 		case NOAPPLY:
689 			fatal("internal error in delta/enter() (de6)");
690 			break;
691 		default:
692 			fatal("internal error in delta/enter() (de7)");
693 			break;
694 		}
695 }
696 
697 
698 escdodelt()	/* dummy routine for dodelt() */
699 {
700 }
701 
702 
703 clean_up(n)
704 {
705 	if (gpkt.p_file[0])
706 		unlockit(auxf(gpkt.p_file,'z'),getpid());
707 	xrm(&gpkt);
708 	xfreeall();
709 }
710