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 2003 Sun Microsystems, Inc. All rights reserved.
27  * Use is subject to license terms.
28  */
29 /*
30  * Copyright 2006-2020 J. Schilling
31  *
32  * @(#)comb.c	1.45 20/08/23 J. Schilling
33  */
34 #if defined(sun)
35 #pragma ident "@(#)comb.c 1.45 20/08/23 J. Schilling"
36 #endif
37 /*
38  * @(#)comb.c 1.15 06/12/12
39  */
40 
41 #if defined(sun)
42 #pragma ident	"@(#)comb.c"
43 #pragma ident	"@(#)sccs:cmd/comb.c"
44 #endif
45 #define		SCCS_MAIN		/* define global vars */
46 #include	<defines.h>
47 #include	<version.h>
48 #include	<had.h>
49 #include	<i18n.h>
50 #include	<schily/sysexits.h>
51 
52 static struct sid sid;
53 
54 static struct packet gpkt;
55 static Nparms	N;			/* Keep -N parameters		*/
56 static Xparms	X;			/* Keep -X parameters		*/
57 static int	num_files;
58 static int	Do_prs;
59 static char	*clist;
60 static char	*Val_ptr;
61 static char	Blank[]    =    " ";
62 static int	*Cvec;
63 static int	Cnt;
64 static FILE	*iop;
65 
66 static	void    clean_up __PR((void));
67 static	void	enter	__PR((struct packet *pkt,
68 				int ch, int n, struct sid *sidp));
69 
70 	int	main __PR((int argc, char **argv));
71 static void	comb __PR((char *file));
72 static struct sid *prtget __PR((struct idel *idp,
73 				int ser, FILE *fptr, char *file));
74 static int	getpred __PR((struct idel *idp, int *vec, int i));
75 
76 int
main(argc,argv)77 main(argc, argv)
78 int argc;
79 register char *argv[];
80 {
81 	register int i;
82 	register char *p;
83 	int  c;
84 	int testmore;
85 	extern int Fcnt;
86 	int current_optind;
87 	int no_arg;
88 
89 	/*
90 	 * Set locale for all categories.
91 	 */
92 	setlocale(LC_ALL, NOGETTEXT(""));
93 
94 	sccs_setinsbase(INS_BASE);
95 
96 	/*
97 	 * Set directory to search for general l10n SCCS messages.
98 	 */
99 #ifdef	PROTOTYPES
100 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
101 	    NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
102 #else
103 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
104 	    NOGETTEXT("/usr/ccs/lib/locale/"));
105 #endif
106 
107 	(void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
108 
109 	tzset();	/* Set up timezome related vars */
110 
111 #ifdef	SCHILY_BUILD
112 	save_args(argc, argv);
113 #endif
114 	set_clean_up(clean_up);
115 	Fflags = FTLEXIT | FTLMSG | FTLCLN;
116 #ifdef	SCCS_FATALHELP
117 	Fflags |= FTLFUNC;
118 	Ffunc = sccsfatalhelp;
119 #endif
120 
121 	current_optind = 1;
122 	optind = 1;
123 	opterr = 0;
124 	no_arg = 0;
125 	i = 1;
126 	/*CONSTCOND*/
127 	while (1) {
128 			if (current_optind < optind) {
129 				current_optind = optind;
130 				argv[i] = 0;
131 				if (optind > i+1) {
132 					if ((argv[i+1][0] != '-') &&
133 					    (no_arg == 0)) {
134 						argv[i+1] = NULL;
135 					} else {
136 						optind = i+1;
137 						current_optind = optind;
138 					}
139 				}
140 			}
141 			no_arg = 0;
142 			i = current_optind;
143 			c = getopt(argc, argv, "()-p:c:osN:X:V(version)");
144 
145 				/*
146 				 * This takes care of options given after
147 				 * file names.
148 				 */
149 			if (c == EOF) {
150 			    if (optind < argc) {
151 				/* if it's due to -- then break; */
152 				if (argv[i][0] == '-' &&
153 				    argv[i][1] == '-') {
154 					argv[i] = 0;
155 					break;
156 				}
157 				optind++;
158 				current_optind = optind;
159 				continue;
160 			    } else {
161 				break;
162 			    }
163 			}
164 			p = optarg;
165 			testmore = 0;
166 			switch (c) {
167 
168 			case 'p':
169 				if (!p[0]) {
170 					argv[i] = 0;
171 					continue;
172 				}
173 				chksid(sid_ab(p, &sid), &sid);
174 				break;
175 			case 'c':
176 				clist = p;
177 				break;
178 			case 'o':
179 				testmore++;
180 				break;
181 			case 's':
182 				testmore++;
183 				break;
184 
185 			case 'N':	/* Bulk names */
186 				initN(&N);
187 				if (optarg == argv[i+1]) {
188 					no_arg = 1;
189 					break;
190 				}
191 				N.n_parm = p;
192 				break;
193 
194 			case 'X':	/* -Xtended options */
195 				X.x_parm = optarg;
196 				X.x_flags = XO_NULLPATH;
197 				if (!parseX(&X))
198 					goto err;
199 				had[NLOWER+c-'A'] = 0;	/* Allow mult -X */
200 				break;
201 
202 			case 'V':		/* version */
203 				printf(gettext(
204 				"comb %s-SCCS version %s %s (%s-%s-%s)\n"),
205 					PROVIDER,
206 					VERSION,
207 					VDATE,
208 					HOST_CPU, HOST_VENDOR, HOST_OS);
209 				exit(EX_OK);
210 
211 			default:
212 			err:
213 				fatal(gettext(
214 				"Usage: comb [ -os ][ -c sid-list ] [ -p SID ][ -N[bulk-spec]][ -Xxopts ] s.filename ..."));
215 			}
216 
217 			if (testmore) {
218 				testmore = 0;
219 				if (p) {
220 				    if (*p) {
221 					sprintf(SccsError,
222 						gettext("value after %c arg (cm7)"),
223 						c);
224 					fatal(SccsError);
225 				    }
226 				}
227 			}
228 
229 			/*
230 			 * Make sure that we only collect option letters from
231 			 * the range 'a'..'z' and 'A'..'Z'.
232 			 */
233 			if (ALPHA(c) &&
234 			    (had[LOWER(c)? c-'a' : NLOWER+c-'A']++)) {
235 				if (c != 'X')
236 					fatal(gettext("key letter twice (cm2)"));
237 			}
238 	}
239 
240 	for (i = 1; i < argc; i++) {
241 		if (argv[i]) {
242 			num_files++;
243 		}
244 	}
245 	if (num_files == 0)
246 		fatal(gettext("missing file arg (cm3)"));
247 	if (HADP && HADC)
248 		fatal(gettext("can't have both -p and -c (cb2)"));
249 
250 	setsig();
251 	xsethome(NULL);
252 	if (HADUCN) {					/* Parse -N args  */
253 		parseN(&N);
254 
255 		if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
256 			fatal(gettext("-Ns. not supported in off-tree project mode"));
257 	}
258 
259 	Fflags &= ~FTLEXIT;
260 	Fflags |= FTLJMP;
261 	iop = stdout;
262 	for (i = 1; i < argc; i++)
263 		if ((p = argv[i]) != NULL)
264 			do_file(p, comb, 1, N.n_sdot, &X);
265 	fclose(iop);
266 	iop = NULL;
267 
268 	return (Fcnt ? 1 : 0);
269 }
270 
271 static void
comb(file)272 comb(file)
273 char *file;
274 {
275 	register int i, n;
276 	register struct idel *rdp;
277 	char *p;
278 	char	*dir_name = NULL;
279 	char rarg[SID_STRSIZE];
280 	int succnt;
281 	struct sid *sp;
282 	extern char had_dir, had_standinp;
283 	struct stats stats;
284 static	int	idx;
285 
286 	if (setjmp(Fjmp))
287 		return;
288 	if (HADUCN) {
289 #ifdef	__needed__
290 		char	*ofile = file;
291 #endif
292 
293 		file = bulkprepare(&N, file);
294 		if (file == NULL) {
295 #ifdef	__needed__
296 			if (N.n_ifile)
297 				ofile = N.n_ifile;
298 #endif
299 			/*
300 			 * The error is typically
301 			 * "directory specified as s-file (cm14)"
302 			 */
303 			fatal(gettext(bulkerror(&N)));
304 		}
305 		if (sid.s_rel == 0 && N.n_sid.s_rel != 0) {
306 			sid.s_rel = N.n_sid.s_rel;
307 			sid.s_lev = N.n_sid.s_lev;
308 			sid.s_br  = N.n_sid.s_br;
309 			sid.s_seq = N.n_sid.s_seq;
310 		}
311 		dir_name = N.n_dir_name;
312 	}
313 
314 	if (strchr(file, '\'') || strchr(file, '\\'))
315 		fatal(gettext("dangerous filename (cb5)"));
316 	sinit(&gpkt, file, SI_OPEN);
317 	gpkt.p_verbose = -1;
318 	gpkt.p_stdout = stderr;
319 	gpkt.p_enter = enter;
320 	if (gpkt.p_verbose && (num_files > 1 || had_dir || had_standinp))
321 		fprintf(gpkt.p_stdout, "\n%s:\n", gpkt.p_file);
322 	if (exists(auxf(gpkt.p_file, 'p')))
323 		fatal(gettext("p-file exists (cb1)"));
324 
325 	if (dodelt(&gpkt, &stats, (struct sid *) 0, 0) == 0)
326 		fmterr(&gpkt);
327 
328 	Cvec = (int *)
329 		fmalloc((unsigned) (n = ((maxser(&gpkt)+1) * sizeof (*Cvec))));
330 	zero((char *) Cvec, n);
331 	Cnt = 0;
332 
333 	if (HADP) {
334 		rdp = gpkt.p_idel;
335 		if (!(n = sidtoser(&sid, &gpkt)))
336 			fatal(gettext("sid doesn't exist (cb3)"));
337 		while (n <= maxser(&gpkt)) {
338 			if (rdp[n].i_sid.s_rel == 0 &&
339 			    rdp[n].i_sid.s_lev == 0 &&
340 			    rdp[n].i_sid.s_br == 0 &&
341 			    rdp[n].i_sid.s_seq == 0) {
342 				n++;
343 				continue;
344 			}
345 			Cvec[Cnt++] = n++;
346 		}
347 	} else if (HADC) {
348 		dolist(&gpkt, clist, 0);
349 	} else {
350 		rdp = gpkt.p_idel;
351 		for (i = 1; i <= maxser(&gpkt); i++) {
352 			succnt = 0;
353 			if (rdp[i].i_sid.s_rel == 0 &&
354 			    rdp[i].i_sid.s_lev == 0 &&
355 			    rdp[i].i_sid.s_br == 0 &&
356 			    rdp[i].i_sid.s_seq == 0)
357 				continue;
358 			for (n = i + 1; n <= maxser(&gpkt); n++)
359 				if (rdp[n].i_pred == i)
360 					succnt++;
361 			if (succnt != 1)
362 				Cvec[Cnt++] = i;
363 		}
364 	}
365 	finduser(&gpkt);
366 	doflags(&gpkt);
367 	donamedflags(&gpkt);
368 	dometa(&gpkt);
369 	sclose(&gpkt);
370 	sfree(&gpkt);
371 	if (!Cnt)
372 		fatal(gettext("nothing to do (cb4)"));
373 	rdp = gpkt.p_idel;
374 	Do_prs = 0;
375 	if (idx++ == 0) {
376 #if defined(INS_BASE)
377 #ifdef	PROTOTYPES
378 		fprintf(iop, "PATH=%s/" SCCS_BIN_PRE "bin:$PATH\n", INS_BASE);
379 #else
380 		fprintf(iop, "PATH=%s/ccs/bin:$PATH\n", INS_BASE);
381 #endif
382 		fprintf(iop, "export PATH\n");
383 #endif
384 		fprintf(iop, "trap \"rm -f COMB$$ comb$$ s.COMB$$; exit 2\" 1 2 3 15\n");
385 		fprintf(iop, "set -e\n");
386 		fprintf(iop, "wdir=`pwd`\n");
387 	}
388 	fprintf(iop, "\n");
389 	if (dir_name)
390 		fprintf(iop, "cd '%s'\n", dir_name);
391 	sp = prtget(rdp, Cvec[0], iop, gpkt.p_file);
392 	sid_ba(sp, rarg);
393 	if ((Val_ptr = gpkt.p_sflags[VALFLAG - 'a']) == NULL)
394 		Val_ptr = Blank;
395 	fprintf(iop, "v=`prs -r%s -d:MR: '%s'`\n", rarg, gpkt.p_file);
396 	fprintf(iop, "vs=`val -v '%s'`\n", gpkt.p_file);
397 	fprintf(iop, "V6=\n");
398 	fprintf(iop, "ip=\n");
399 	fprintf(iop, "ur=\n");
400 	fprintf(iop, "case \"$vs\" in\n");
401 	fprintf(iop, "SCCS\\ V6*)\n");
402 	fprintf(iop, "\tV6=-V6\n");
403 	fprintf(iop, "\tip=-XGp=`prs -d:Gp: '%s'`\n", gpkt.p_file);
404 	fprintf(iop, "\tur=-XGr=`prs -d:Gr: '%s'`\n", gpkt.p_file);
405 	fprintf(iop, "\t;;\n");
406 	fprintf(iop, "esac\n");
407 	fprintf(iop, "if test \"$v\"\n");
408 	fprintf(iop, "then\n");
409 	fprintf(iop,
410 	"admin $V6 $ip $ur -iCOMB$$ -r%s -fv%s -m\"$v\" -y'This was COMBined' s.COMB$$\n",
411 		rarg, Val_ptr);
412 	fprintf(iop, "else\n");
413 	fprintf(iop, "admin $V6 $ip $ur -iCOMB$$ -r%s -y'This was COMBined' s.COMB$$\n",
414 		rarg);
415 	fprintf(iop, "fi\n");
416 	Do_prs = 1;
417 	fprintf(iop, "rm -f COMB$$\n");
418 #if defined(BUG_1205145) || defined(GMT_TIME)
419 	fprintf(iop, "TZ=GMT\n");
420 	fprintf(iop, "export TZ\n");
421 #endif
422 	for (i = 1; i < Cnt; i++) {
423 		n = getpred(rdp, Cvec, i);
424 		if (HADO)
425 			fprintf(iop, "get -s -r%d -g -e -t s.COMB$$\n",
426 				rdp[Cvec[i]].i_sid.s_rel);
427 		else
428 			fprintf(iop, "get -s -a%d -r%d -g -e s.COMB$$\n",
429 				n + 1, rdp[Cvec[i]].i_sid.s_rel);
430 		prtget(rdp, Cvec[i], iop, gpkt.p_file);
431 		fprintf(iop, "if test \"$b\"\n");
432 		fprintf(iop, "then\n");
433 		fprintf(iop, "delta -s -m\"$b\" -y\"$a\" s.COMB$$\n");
434 		fprintf(iop, "admin -fv s.COMB$$\n");
435 		fprintf(iop, "else\n");
436 		fprintf(iop, "delta -s -y\"$a\" s.COMB$$ </dev/null\n");
437 		fprintf(iop, "fi\n");
438 		fprintf(iop, "c1=`prs -d:D: s.COMB$$`\n");
439 		fprintf(iop, "d1=`prs -d:T: s.COMB$$`\n");
440 		fprintf(iop, "e1=`prs -d:P: s.COMB$$`\n");
441 		fprintf(iop, "s=`prs -d:I: s.COMB$$`\n");
442 		fprintf(iop, "sed '/d D '$s'/s|'$c1'|'$c'|' s.COMB$$ >comb$$\n");
443 		fprintf(iop, "rm -f s.COMB$$\n");
444 		fprintf(iop, "sed '/d D '$s'/s|'$d1'|'$d'|' comb$$ >COMB$$\n");
445 		fprintf(iop, "sed '/d D '$s'/s|'$e1'|'$e'|' COMB$$ >s.COMB$$\n");
446 		fprintf(iop, "if test \"$v\"\n");
447 		fprintf(iop, "then\n");
448 		fprintf(iop, "admin -z -fv s.COMB$$\n");
449 		fprintf(iop, "else\n");
450 		fprintf(iop, "admin -z s.COMB$$\n");
451 		fprintf(iop, "fi\n");
452 	}
453 	fprintf(iop, "sed -n '/^%c%c$/,/^%c%c$/p' '%s' >comb$$\n",
454 		CTLCHAR, BUSERTXT, CTLCHAR, EUSERTXT, gpkt.p_file);
455 	fprintf(iop, "ed - comb$$ <<\\!\n");
456 	fprintf(iop, "1d\n");
457 	fprintf(iop, "$c\n");
458 	fprintf(iop, "*** DELTA TABLE PRIOR TO COMBINE ***\n");
459 	fprintf(iop, ".\n");
460 	fprintf(iop, "w\n");
461 	fprintf(iop, "q\n");
462 	fprintf(iop, "!\n");
463 	fprintf(iop, "prs -e '%s' >>comb$$\n", gpkt.p_file);
464 	fprintf(iop, "admin -tcomb$$ s.COMB$$\\\n");
465 	for (i = 0; i < NFLAGS; i++)
466 		if ((p = gpkt.p_sflags[i]) != NULL)
467 		    if (i != (ENCODEFLAG-'a'))
468 			fprintf(iop, " -f%c%s\\\n", i + 'a', p);
469 	fprintf(iop, "\n");
470 	fprintf(iop, "sed -n '/^%c%c$/,/^%c%c$/p' '%s' >comb$$\n",
471 		CTLCHAR, BUSERNAM, CTLCHAR, EUSERNAM, gpkt.p_file);
472 	fprintf(iop, "ed - comb$$ <<\\!\n");
473 	fprintf(iop, "v/^%c/s/.*/ -a& \\\\/\n", CTLCHAR);
474 	fprintf(iop, "1c\n");
475 	fprintf(iop, "admin s.COMB$$\\\n");
476 	fprintf(iop, ".\n");
477 	fprintf(iop, "$c\n");
478 	fprintf(iop, "\n");
479 	fprintf(iop, ".\n");
480 	fprintf(iop, "w\n");
481 	fprintf(iop, "q\n");
482 	fprintf(iop, "!\n");
483 	fprintf(iop, ". comb$$\n");
484 	fprintf(iop, "rm comb$$\n");
485 	if (!HADS) {
486 		fprintf(iop, "rm -f '%s'\n", gpkt.p_file);
487 		fprintf(iop, "mv s.COMB$$ '%s'\n", gpkt.p_file);
488 		if (!gpkt.p_sflags[VALFLAG - 'a'])
489 			fprintf(iop, "admin -dv '%s'\n", gpkt.p_file);
490 	} else {
491 		fprintf(iop, "set `ls -st s.COMB$$ '%s'`\n", gpkt.p_file);
492 		fprintf(iop, "c=`expr 100 - 100 '*' $1 / $3`\n");
493 		fprintf(iop, "echo '%s\t' ${c}'%%\t' $1/$3\n", gpkt.p_file);
494 		fprintf(iop, "rm -f s.COMB$$\n");
495 	}
496 	if (dir_name)
497 		fprintf(iop, "cd \"$wdir\"\n");
498 
499 	ffreeall();
500 }
501 
502 /*ARGSUSED*/
503 static void
enter(pkt,ch,n,sidp)504 enter(pkt, ch, n, sidp)
505 	struct packet	*pkt;
506 	char		ch;
507 	int		n;
508 	struct sid	*sidp;
509 {
510 	Cvec[Cnt++] = n;
511 }
512 
513 
514 static struct sid *
prtget(idp,ser,fptr,file)515 prtget(idp, ser, fptr, file)
516 struct idel *idp;
517 int ser;
518 FILE *fptr;
519 char *file;
520 {
521 	char buf[SID_STRSIZE];
522 	struct sid *sp;
523 
524 	sid_ba(sp = &idp[ser].i_sid, buf);
525 	fprintf(fptr, "get -s -k -r%s -p '%s' > COMB$$\n", buf, file);
526 	if (Do_prs) {
527 		fprintf(fptr, "a=`prs -r%s -d:C: '%s'`\n", buf, file);
528 		fprintf(fptr, "b=`prs -r%s -d:MR: '%s'`\n", buf, file);
529 		fprintf(fptr, "c=`prs -r%s -d:D: '%s'`\n", buf, file);
530 		fprintf(fptr, "d=`prs -r%s -d:T: '%s'`\n", buf, file);
531 		fprintf(fptr, "e=`prs -r%s -d:P: '%s'`\n", buf, file);
532 	}
533 	return (sp);
534 }
535 
536 
537 static int
getpred(idp,vec,i)538 getpred(idp, vec, i)
539 struct idel *idp;
540 int *vec;
541 int i;
542 {
543 	int ser, pred, acpred;
544 
545 	ser = vec[i];
546 	while (--i) {
547 		pred = vec[i];
548 		for (acpred = idp[ser].i_pred;
549 		    acpred; acpred = idp[acpred].i_pred)
550 			if (pred == acpred)
551 				break;
552 		if (pred == acpred)
553 			break;
554 	}
555 	return (i);
556 }
557 
558 static void
clean_up()559 clean_up()
560 {
561 	sclose(&gpkt);
562 	sfree(&gpkt);
563 	ffreeall();
564 }
565