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  * @(#)get.c	1.97 20/09/07 J. Schilling
33  */
34 #if defined(sun)
35 #pragma ident "@(#)get.c 1.97 20/09/07 J. Schilling"
36 #endif
37 /*
38  * @(#)get.c 1.59 06/12/12
39  */
40 
41 #if defined(sun)
42 #pragma ident	"@(#)get.c"
43 #pragma ident	"@(#)sccs:cmd/get.c"
44 #endif
45 
46 #define		SCCS_MAIN			/* define global vars */
47 #include	<defines.h>
48 #include	<version.h>
49 #include	<had.h>
50 #include	<i18n.h>
51 #include	<schily/utsname.h>
52 #include	<ccstypes.h>
53 #include	<schily/limits.h>
54 #include	<schily/sysexits.h>
55 
56 #define	DATELEN	12
57 
58 #if defined(__STDC__) || defined(PROTOTYPES)
59 #define	INCPATH		INS_BASE "/ccs/include"	/* place to find binaries */
60 #else
61 /*
62  * XXX With a K&R compiler, you need to edit the following string in case
63  * XXX you like to change the install path.
64  */
65 #define	INCPATH		"/usr/ccs/include"	/* place to find binaries */
66 #endif
67 
68 
69 
70 /*
71  * Get is now much more careful than before about checking
72  * for write errors when creating the g- l- p- and z-files.
73  * However, it remains deliberately unconcerned about failures
74  * in writing statistical information (e.g., number of lines
75  * retrieved).
76  */
77 
78 static struct packet gpkt;		/* libcomobj data for get()	*/
79 static char	Zhold[MAXPATHLEN];	/* temporary z-file name */
80 
81 static Nparms	N;			/* Keep -N parameters		*/
82 static Xparms	X;			/* Keep -X parameters		*/
83 static struct sid sid;			/* To hold -r parameter		*/
84 static unsigned	Ser;			/* To hold -a parameter		*/
85 static char	*ilist;			/* To hold -i parameter		*/
86 static char	*elist;			/* To hold -x parameter		*/
87 static char	*lfile;			/* To hold -l parameter		*/
88 static char	*cutoffstr;		/* To hold -c parameter		*/
89 static char	Gfile[PATH_MAX];	/* To hold -G (g. + parameter)	*/
90 static char	gfile[PATH_MAX];	/* To hold -G parameter		*/
91 static char	*Cwd = "";		/* To hold -C parameter		*/
92 
93 /* Beginning of modifications made for CMF system. */
94 #define	CMRLIMIT 128
95 static char	cmr[CMRLIMIT];		/* To hold -z parameter		*/
96 /* End of insertion */
97 
98 static int	num_files;		/* arg counter for main()/get()	*/
99 static char	Pfilename[FILESIZE];	/* p.filename used in get()	*/
100 static time_t	cutoff = MAX_TIME;	/* cutoff time for main()/get()	*/
101 
102 static struct	utsname	un;		/* uname for lockit()		*/
103 static char	*uuname;		/* un.nodename			*/
104 
105 
106 static	void    clean_up __PR((void));
107 static	void	enter	__PR((struct packet *pkt, int ch, int n, struct sid *sidp));
108 
109 	int	main __PR((int argc, char **argv));
110 static void	do_get __PR((char *file));
111 static void	get __PR((struct packet *pkt, char *file));
112 static void	gen_lfile __PR((struct packet *pkt));
113 static void	prfx __PR((struct packet *pkt));
114 static void	annotate __PR((struct packet *pkt));
115 static void	wrtpfile __PR((struct packet *pkt, char *inc, char *exc));
116 static int	cmrinsert __PR((struct packet *pkt));
117 
118 int
main(argc,argv)119 main(argc,argv)
120 int argc;
121 register char *argv[];
122 {
123 	register int i;
124 	register char *p;
125 	int  c;
126 	extern int Fcnt;
127 	int current_optind;
128 	int no_arg;
129 	int	cmri = 0;	/* CMF index for option parsing */
130 
131 	/*
132 	 * Set locale for all categories.
133 	 */
134 	setlocale(LC_ALL, NOGETTEXT(""));
135 
136 	sccs_setinsbase(INS_BASE);
137 
138 	/*
139 	 * Set directory to search for general l10n SCCS messages.
140 	 */
141 #ifdef	PROTOTYPES
142 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
143 	   NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
144 #else
145 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
146 	   NOGETTEXT("/usr/ccs/lib/locale/"));
147 #endif
148 	(void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
149 
150 	tzset();	/* Set up timezome related vars */
151 
152 #ifdef	SCHILY_BUILD
153 	save_args(argc, argv);
154 #endif
155 	set_clean_up(clean_up);
156 	Fflags = FTLEXIT | FTLMSG | FTLCLN;
157 #ifdef	SCCS_FATALHELP
158 	Fflags |= FTLFUNC;
159 	Ffunc = sccsfatalhelp;
160 #endif
161 #ifdef	XPG4
162 	sccs_xpg4(TRUE);
163 #endif
164 	current_optind = 1;
165 	optind = 1;
166 	opterr = 0;
167 	no_arg = 0;
168 	i = 1;
169 	/*CONSTCOND*/
170 	while (1) {
171 			if (current_optind < optind) {
172 			   current_optind = optind;
173 			   argv[i] = 0;
174 			   if (optind > i+1) {
175 			      if ((argv[i+1][0] != '-') && (no_arg == 0)) {
176 			         argv[i+1] = NULL;
177 			      } else {
178 				 optind = i+1;
179 			         current_optind = optind;
180 			      }
181 			   }
182 			}
183 			no_arg = 0;
184 			i = current_optind;
185 		        c = getopt(argc, argv, "()-r:c:ebi:x:kl:Lpsmnogta:G:w:zqdC:AFN:X:V(version)");
186 				/* this takes care of options given after
187 				** file names.
188 				*/
189 			if (c == EOF) {
190 			   if (optind < argc) {
191 			      /* if it's due to -- then break; */
192 			      if (argv[i][0] == '-' &&
193 				  argv[i][1] == '-') {
194 			         argv[i] = 0;
195 			         break;
196 			      }
197 			      optind++;
198 			      current_optind = optind;
199 			      continue;
200 			   } else {
201 			      break;
202 			   }
203 			}
204 			p = optarg;
205 			switch (c) {
206     			case CMFFLAG:		/* 'z' */
207 				/* Concatenate the rest of this argument with
208 				 * the existing CMR list. */
209 				if (p) {
210 				   while (*p) {
211 				      if (cmri == CMRLIMIT)
212 					 fatal(gettext("CMR list is too long."));
213 					 cmr[cmri++] = *p++;
214 				   }
215 				}
216 				cmr[cmri] = '\0';
217 				break;
218 			case 'a':
219 				if (*p == 0) continue;
220 				Ser = patoi(p);
221 				break;
222 			case 'r':
223 				if (argv[i][2] == '\0') {
224 				   if (*(omit_sid(p)) != '\0') {
225 				      no_arg = 1;
226 				      continue;
227 				   }
228 				}
229 				chksid(sid_ab(p, &sid), &sid);
230 				if ((sid.s_rel < MINR) || (sid.s_rel > MAXR)) {
231 				   fatal(gettext("r out of range (ge22)"));
232 				}
233 				break;
234 			case 'w':
235 				if (p) whatsetup(p);
236 				break;
237 			case 'c':
238 				if (*p == 0) continue;
239 				cutoffstr = p;
240 #if defined(BUG_1205145) || defined(GMT_TIME)
241 				if (parse_date(p,&cutoff, 0))
242 #else
243 				if (parse_date(p,&cutoff, PF_GMT))
244 #endif
245 				   fatal(gettext("bad date/time (cm5)"));
246 				break;
247 			case 'L':
248 				/* treat this as if -lp was given */
249 				lfile = NOGETTEXT("stdout");
250 				c = 'l';
251 				break;
252 			case 'l':
253 				if (argv[i][2] != '\0') {
254 				   if ((*p == 'p') && (strlen(p) == 1)) {
255 				      p = NOGETTEXT("stdout");
256 				   }
257 				   lfile = p;
258 				} else {
259 				   no_arg = 1;
260 				   lfile = NULL;
261 				}
262 				break;
263 			case 'i':
264 				if (*p == 0) continue;
265 				ilist = p;
266 				break;
267 			case 'x':
268 				if (*p == 0) continue;
269 				elist = p;
270 				break;
271 			case 'G':
272 				{
273 				char *cp;
274 
275 				if (*p == 0) continue;
276 				copy(p, gfile);
277  				cp = auxf(p, 'G');
278 				copy(cp, Gfile);
279 				break;
280 				}
281 			case 'b':
282 			case 'g':
283 			case 'e':
284 			case 'p':
285 			case 'k':
286 			case 'm':
287 			case 'n':
288 			case 'o':
289 			case 's':
290 			case 't':
291 			case 'd':
292 			case 'A':
293 			case 'F':
294 				if (p) {
295 				   sprintf(SccsError,
296 				     gettext("value after %c arg (cm8)"),
297 				     c);
298 				   fatal(SccsError);
299 				}
300 				break;
301                         case 'q': /* enable NSE mode */
302 				if (p) {
303                                    if (*p) {
304                                       nsedelim = p;
305 				   }
306                                 } else {
307                                    nsedelim = (char *) 0;
308                                 }
309                                 break;
310 			case 'C':
311 				Cwd = p;
312 				break;
313 
314 			case 'N':	/* Bulk names */
315 				initN(&N);
316 				if (optarg == argv[i+1]) {
317 				   no_arg = 1;
318 				   break;
319 				}
320 				N.n_parm = p;
321 				break;
322 
323 			case 'X':	/* -Xtended options */
324 				X.x_parm = optarg;
325 				X.x_flags = XO_NULLPATH;
326 				if (!parseX(&X))
327 					goto err;
328 				had[NLOWER+c-'A'] = 0;	/* Allow mult -X */
329 				break;
330 
331 			case 'V':		/* version */
332 				printf(gettext(
333 				    "get %s-SCCS version %s %s (%s-%s-%s)\n"),
334 					PROVIDER,
335 					VERSION,
336 					VDATE,
337 					HOST_CPU, HOST_VENDOR, HOST_OS);
338 				exit(EX_OK);
339 
340 			default:
341 			err:
342 			   fatal(gettext("Usage: get [-AbeFgkmLopst] [-l[p]] [-asequence] [-cdate-time] [-Gg-file]\n\t[-isid-list] [-rsid] [-xsid-list][ -N[bulk-spec]][ -Xxopts ] s.filename ..."));
343 			}
344 
345 			/*
346 			 * Make sure that we only collect option letters from
347 			 * the range 'a'..'z' and 'A'..'Z'.
348 			 */
349 			if (ALPHA(c) &&
350 			    (had[LOWER(c)? c-'a' : NLOWER+c-'A']++)) {
351 				if (c != 'X')
352 					fatal(gettext("key letter twice (cm2)"));
353 			}
354 	}
355 	for (i=1; i<argc; i++) {
356 		if (argv[i]) {
357 		       num_files++;
358 		}
359 	}
360 	if(num_files == 0)
361 		fatal(gettext("missing file arg (cm3)"));
362 	if (HADE && HADM)
363 		fatal(gettext("e not allowed with m (ge3)"));
364 	if (HADE)
365 		HADK = 1;
366 	if (HADE && !logname())
367 		fatal(gettext("User ID not in password file (cm9)"));
368 
369 	setsig();
370 	xsethome(NULL);
371 	if (HADUCN) {					/* Parse -N args  */
372 		/*
373 		 * initN() was already called while parsing options.
374 		 */
375 		if (!HADP && !HADG && !HADUCG)		/* Create g-file, so */
376 			N.n_flags |= N_GETI;		/* create subdirs    */
377 
378 		parseN(&N);
379 
380 		if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
381 			fatal(gettext("-Ns. not supported in off-tree project mode"));
382 	}
383 
384 	/*
385 	 * Get the name of our machine to be used for the lockfile.
386 	 */
387 	uname(&un);
388 	uuname = un.nodename;
389 
390 	/*
391 	 * Set up a project global lock on the changeset file.
392 	 * Since we set FTLJMP, we do not need to unlockchset() from clean_up().
393 	 */
394 	if (HADE && SETHOME_CHSET())
395 		lockchset(getppid(), getpid(), uuname);
396 	timerchsetlock();
397 
398 	Fflags &= ~FTLEXIT;
399 	Fflags |= FTLJMP;
400 	for (i=1; i<argc; i++)
401 		if ((p=argv[i]) != NULL)
402 			do_file(p, do_get, 1, N.n_sdot, &X);
403 
404 	/*
405 	 * Only remove the global lock it it was created by us and not by
406 	 * our parent.
407 	 */
408 	if (HADE && SETHOME_CHSET()) {
409 		if (HADUCN)
410 			bulkchdir(&N);
411 		unlockchset(getpid(), uuname);
412 	}
413 
414 	return (Fcnt ? 1 : 0);
415 }
416 
417 static void
do_get(file)418 do_get(file)
419 	char		*file;
420 {
421 	get(&gpkt, file);
422 }
423 
424 static void
get(pkt,file)425 get(pkt, file)
426 	struct packet	*pkt;
427 	char		*file;
428 {
429 	register char *p;
430 	register unsigned ser;
431 	extern char had_dir, had_standinp;
432 	struct stats stats;
433 	char	str[SID_STRSIZE];		/* Must fit a SID string */
434 #ifdef	PROTOTYPES
435 	char template[] = NOGETTEXT("/get.XXXXXX");
436 #else
437 	char *template = NOGETTEXT("/get.XXXXXX");
438 #endif
439 	char *ifile;
440 	char buf1[PATH_MAX];
441 	uid_t holduid;
442 	gid_t holdgid;
443 	static int first = 1;
444 
445 	if (setjmp(Fjmp))
446 		return;
447 
448 	/*
449 	 * In order to make the global lock with a potentially long duration
450 	 * not look as if it was expired, we refresh it for every file in our
451 	 * task list. This is needed since another SCCS instance on a different
452 	 * NFS machine cannot use kill() to check for a still active process.
453 	 */
454 	if (HADE && SETHOME_CHSET()) {
455 		if (HADUCN)
456 			bulkchdir(&N);	/* Done by bulkprepare() anyway */
457 		refreshchsetlock();
458 	}
459 
460 	if (HADUCN) {
461 #ifdef	__needed__
462 		char	*ofile = file;
463 #endif
464 
465 		file = bulkprepare(&N, file);
466 		if (file == NULL) {
467 #ifdef	__needed__
468 			if (N.n_ifile)
469 				ofile = N.n_ifile;
470 #endif
471 			/*
472 			 * The error is typically
473 			 * "directory specified as s-file (cm14)"
474 			 */
475 			fatal(gettext(bulkerror(&N)));
476 		}
477 		ifile = N.n_ifile;
478 		if (sid.s_rel == 0 && N.n_sid.s_rel != 0) {
479 			sid.s_rel = N.n_sid.s_rel;
480 			sid.s_lev = N.n_sid.s_lev;
481 			sid.s_br  = N.n_sid.s_br;
482 			sid.s_seq = N.n_sid.s_seq;
483 		}
484 	} else {
485 		ifile = NULL;
486 	}
487 
488 	if (HADE) {
489 
490 		/*
491 		call `sinit' to check if file is an SCCS file
492 		but don't open the SCCS file.
493 		If it is, then create lock file.
494 		pass both the PID and machine name to lockit
495 		*/
496 		sinit(pkt, file, SI_INIT);
497 
498 		/*
499 		 * Lock out any other user who may be trying to process
500 		 * the same file.
501 		 */
502 		if (!islockchset(copy(auxf(file, 'z'), Zhold)) &&
503 		    lockit(Zhold, SCCS_LOCK_ATTEMPTS, getpid(), uuname)) {
504 			lockfatal(Zhold, getpid(), uuname);
505 		} else {
506 			timersetlockfile(Zhold);
507 		}
508 	}
509 	/*
510 	Open SCCS file and initialize packet
511 	*/
512 	sinit(pkt, file, SI_OPEN);
513 	pkt->p_ixuser = (HADI | HADX);
514 	pkt->p_reqsid.s_rel = sid.s_rel;
515 	pkt->p_reqsid.s_lev = sid.s_lev;
516 	pkt->p_reqsid.s_br = sid.s_br;
517 	pkt->p_reqsid.s_seq = sid.s_seq;
518 	pkt->p_verbose = (HADS) ? 0 : 1;
519 	pkt->p_stdout  = (HADP||lfile) ? stderr : stdout;
520 	pkt->p_cutoff = cutoff;
521 	pkt->p_lfile = lfile;
522 	pkt->p_enter = enter;
523 #if defined(BUG_1205145) || defined(GMT_TIME)
524 #else
525 	if ((pkt->p_flags & PF_V6) == 0) {
526 		pkt->p_flags |= PF_GMT;
527 	} else if (cutoffstr != NULL) {
528 		if (parse_date(cutoffstr, &cutoff, 0))
529 			fatal(gettext("bad date/time (cm5)"));
530 		pkt->p_cutoff = cutoff;
531 	}
532 #endif
533 	if (HADUCA)
534 		pkt->p_pgmrs = (char **)Null;
535 	if (Gfile[0] == 0 || !first) {
536 		gfile[0] = '\0';
537 		strlcatl(gfile, sizeof (gfile),
538 			Cwd,
539 			ifile ? ifile :
540 				auxf(pkt->p_file, 'g'),
541 				(char *)0);
542 		Gfile[0] = '\0';
543 		strlcatl(Gfile, sizeof (Gfile),
544 			Cwd,
545 			ifile ? auxf(ifile, 'G') :
546 				auxf(pkt->p_file, 'A'),
547 			(char *)0);
548 	}
549 	strlcpy(buf1, dname(Gfile), sizeof (buf1));
550 	strlcat(buf1, template, sizeof (buf1));
551 	Gfile[0] = '\0';		/* File not yet created */
552 	first = 0;
553 
554 	if (pkt->p_verbose && (num_files > 1 || had_dir || had_standinp))
555 		fprintf(pkt->p_stdout, "\n%s:\n", pkt->p_file);
556 	if (dodelt(pkt, &stats, (struct sid *) 0, 0) == 0)
557 		fmterr(pkt);
558 	finduser(pkt);
559 	doflags(pkt);
560 	donamedflags(pkt);
561 	dometa(pkt);
562 
563 	if (!HADA) {
564 		ser = getser(pkt);
565 	} else {
566 		if ((ser = Ser) > maxser(pkt))
567 			fatal(gettext("serial number too large (ge19)"));
568 		pkt->p_gotsid = pkt->p_idel[ser].i_sid;
569 		if (HADR && sid.s_rel != pkt->p_gotsid.s_rel) {
570 			zero((char *) &pkt->p_reqsid, sizeof (pkt->p_reqsid));
571 			pkt->p_reqsid.s_rel = sid.s_rel;
572 		} else {
573 			pkt->p_reqsid = pkt->p_gotsid;
574 		}
575 	}
576 	doie(pkt, ilist, elist, (char *) 0);
577 	setup(pkt, (int) ser);
578 	if (!(HADP || HADG)) {
579 		if (exists(gfile) && (S_IWRITE & Statbuf.st_mode)) {
580 			sprintf(SccsError, gettext("writable `%s' exists (ge4)"),
581 				gfile);
582 			fatal(SccsError);
583 		}
584 	}
585 	if (pkt->p_verbose) {
586 		sid_ba(&pkt->p_gotsid, str);
587 		fprintf(pkt->p_stdout, "%s\n", str);
588 	}
589 	if (HADE) {
590 		if (HADC || pkt->p_reqsid.s_rel == 0)
591 			pkt->p_reqsid = pkt->p_gotsid;
592 		newsid(pkt, pkt->p_sflags[BRCHFLAG - 'a'] && HADB);
593 		permiss(pkt);
594 		wrtpfile(pkt, ilist, elist);
595 	}
596 	if (!HADG || HADL) {
597 		if (pkt->p_stdout) {
598 			fflush(pkt->p_stdout);
599 			fflush(stderr);
600 		}
601 		holduid=geteuid();
602 		holdgid=getegid();
603 		setuid(getuid());
604 		setgid(getgid());
605 		if (HADL)
606 			gen_lfile(pkt);
607 		if (HADG)
608 			goto unlock;
609 
610 		if (pkt->p_idel[ser].i_dtype == 'U') {	/* unlink delta */
611 			unlink(gfile);
612 			goto unlock;
613 		}
614 
615 		flushto(pkt, EUSERTXT, FLUSH_NOCOPY);
616 		idsetup(pkt, gfile);
617 		pkt->p_chkeof = 1;
618 		pkt->p_did_id = 0;
619 		/*
620 		call `xcreate' which deletes the old `g-file' and
621 		creates a new one except if the `p' keyletter is set in
622 		which case any old `g-file' is not disturbed.
623 		The mod of the file depends on whether or not the `k'
624 		keyletter has been set.
625 		*/
626 		if (pkt->p_gout == 0) {
627 			if (HADP) {
628 				pkt->p_gout = stdout;
629 			} else {
630 #ifdef	HAVE_MKSTEMP
631 				close(mkstemp(buf1));	/* safer than mktemp */
632 #else
633 				mktemp(buf1);
634 #endif
635 				copy(buf1, Gfile);
636 				/*
637 				 * It may be better to use fdopen() and chmod()
638 				 * instead of xfcreat() in order to avoid an
639 				 * unlink()/create() chain.
640 				 */
641 				if ((exists(pkt->p_file) && (S_IEXEC & Statbuf.st_mode)) ||
642 				    (pkt->p_flags & PF_SCOX)) {
643 					pkt->p_gout = xfcreat(Gfile, HADK ?
644 						((mode_t)0755) : ((mode_t)0555));
645 				} else {
646 					pkt->p_gout = xfcreat(Gfile, HADK ?
647 						((mode_t)0644) : ((mode_t)0444));
648 				}
649 #ifdef	USE_SETVBUF
650 				/*
651 				 * Do not call setvbuf() with stdout as this may result
652 				 * in a second illegal call in gen_lfile().
653 				 */
654 				setvbuf(pkt->p_gout, NULL, _IOFBF, VBUF_SIZE);
655 #endif
656 			}
657 		}
658 		pkt->p_ghash = 0;		/* Reset ghash */
659 		if (pkt->p_encoding & EF_UUENCODE) {
660 			while (readmod(pkt)) {
661 				decode(pkt->p_line, pkt->p_gout);
662 			}
663 		} else {
664 			while (readmod(pkt)) {
665 				prfx(pkt);
666 				if (pkt->p_flags & PF_NONL) {
667 					pkt->p_line[--(pkt->p_line_length)] = '\0';
668 					pkt->p_line_length -= 2;	/* ^AN */
669 				}
670 				p = idsubst(pkt, pkt->p_lineptr);
671 				if (p != pkt->p_lineptr) {
672 					if (fputs(p, pkt->p_gout) == EOF)
673 						xmsg(gfile, NOGETTEXT("get"));
674 				} else {
675 					if (fwrite(p, 1, pkt->p_line_length,
676 					    pkt->p_gout) != pkt->p_line_length)
677 						xmsg(gfile, NOGETTEXT("get"));
678 				}
679 				if (pkt->p_flags & PF_NONL) {
680 					pkt->p_line_length += 2;	/* ^AN */
681 					pkt->p_line[(pkt->p_line_length)++] = '\n';
682 				}
683 			}
684 		}
685 		if ((pkt->p_flags & (PF_V6 | PF_V6TAGS)) && pkt->p_hash) {
686 			/*
687 			 * SCCS v6 is able to check against SID specific
688 			 * checksums, but this will not work in case that
689 			 * we use include or exclude lists.
690 			 */
691 			if (pkt->p_hash[ser] != (pkt->p_ghash & 0xFFFF) &&
692 			    elist == NULL && ilist == NULL && !HADUCF)
693 				fatal(gettext("corrupted file version (co27)"));
694 		}
695 
696 		if (pkt->p_gout) {
697 			if (fflush(pkt->p_gout) == EOF)
698 				xmsg(gfile, NOGETTEXT("get"));
699 			fflush (stderr);
700 		}
701 		if (pkt->p_gout && pkt->p_gout != stdout) {
702 			/*
703 			 * Force g-file to disk and verify
704 			 * that it actually got there.
705 			 */
706 #ifdef	HAVE_FSYNC
707 			if (fsync(fileno(pkt->p_gout)) < 0)
708 				xmsg(gfile, NOGETTEXT("get"));
709 #endif
710 			if (fclose(pkt->p_gout) == EOF)
711 				xmsg(gfile, NOGETTEXT("get"));
712 			pkt->p_gout = NULL;
713 		}
714 		if (pkt->p_verbose) {
715 #ifdef XPG4
716 		   fprintf(pkt->p_stdout, NOGETTEXT("%d lines\n"), pkt->p_glnno);
717 #else
718 		   if (HADD == 0)
719 		      fprintf(pkt->p_stdout, gettext("%d lines\n"), pkt->p_glnno);
720 #endif
721 		}
722 		if (!pkt->p_did_id && !HADK && !HADQ &&
723 		    (!pkt->p_sflags[EXPANDFLAG - 'a'] ||
724 		    *(pkt->p_sflags[EXPANDFLAG - 'a']))) {
725 		   if (pkt->p_sflags[IDFLAG - 'a']) {
726 		      if (!(*pkt->p_sflags[IDFLAG - 'a'])) {
727 		         fatal(gettext("no id keywords (cm6)"));
728 		      } else {
729 			 fatal(gettext("invalid id keywords (cm10)"));
730 		      }
731 		   } else {
732 		      if (pkt->p_verbose) {
733 			 fprintf(stderr, gettext("No id keywords (cm7)\n"));
734 		      }
735 		   }
736 		}
737 		if (Gfile[0] != '\0' && exists(Gfile)) {
738 			rename(Gfile, gfile);
739 			if (HADO) {
740 #if defined(BUG_1205145) || defined(GMT_TIME)
741 #else
742 				struct tm	tm;
743 #endif
744 				struct timespec	ts[2];
745 				unsigned int	gser;
746 				extern dtime_t	Timenow;
747 
748 				gser = sidtoser(&pkt->p_gotsid, pkt);
749 
750 				ts[0].tv_sec = Timenow.dt_sec;
751 				ts[0].tv_nsec = Timenow.dt_nsec;
752 				ts[1].tv_sec = pkt->p_idel[gser].i_datetime.tv_sec;
753 				ts[1].tv_nsec = pkt->p_idel[gser].i_datetime.tv_nsec;
754 				/*
755 				 * If we did cheat while scanning the delta
756 				 * table and converted the time stamps assuming
757 				 * GMT. Fix the resulting error here.
758 				 */
759 #if defined(BUG_1205145) || defined(GMT_TIME)
760 #else
761 				if (pkt->p_flags & PF_GMT) {
762 					tm = *gmtime(&ts[1].tv_sec);
763 					tm.tm_isdst = -1;
764 					ts[1].tv_sec = mktime(&tm);
765 				}
766 #endif
767 				/*
768 				 * As SunPro make and gmake call sccs
769 				 * get when the time if s.file equals
770 				 * the time stamp of the g-file, make
771 				 * sure the g-file is a bit younger.
772 				 */
773 				if (!(gpkt.p_flags & PF_V6)) {
774 					struct timespec	tn;
775 
776 					getnstimeofday(&tn);
777 					ts[1].tv_nsec = tn.tv_nsec;
778 				}
779 				if (ts[1].tv_nsec <= 500000000)
780 					ts[1].tv_nsec += 499999999;
781 
782 				utimensat(AT_FDCWD, gfile, ts, 0);
783 			}
784 		}
785 		setuid(holduid);
786 		setgid(holdgid);
787 	}
788 unlock:
789 	if (HADE) {
790 		copy(auxf(pkt->p_file, 'p'), Pfilename);
791 		rename(auxf(pkt->p_file, 'q'), Pfilename);
792 		timersetlockfile(NULL);
793 		if (!islockchset(Zhold))
794 			unlockit(Zhold, getpid(), uuname);
795 	}
796 	sclose(pkt);
797 	sfree(pkt);
798 	ffreeall();
799 	if (HADUCA)				/* ffreeall() killed it	*/
800 		lhash_destroy();		/* need to reset it	*/
801 }
802 
803 static void
enter(pkt,ch,n,sidp)804 enter(pkt,ch,n,sidp)
805 struct packet *pkt;
806 char ch;
807 int n;
808 struct sid *sidp;
809 {
810 	char	str[SID_STRSIZE];		/* Must fit a SID string */
811 	register struct apply *ap;
812 
813 	sid_ba(sidp,str);
814 	if (pkt->p_verbose)
815 		fprintf(pkt->p_stdout,"%s\n",str);
816 	ap = &pkt->p_apply[n];
817 	switch (ap->a_code) {
818 
819 	case SX_EMPTY:
820 		if (ch == INCLUDE)
821 			condset(ap,APPLY,INCLUSER);
822 		else
823 			condset(ap,NOAPPLY,EXCLUSER);
824 		break;
825 	case APPLY:
826 		sid_ba(sidp,str);
827 		sprintf(SccsError, gettext("%s already included (ge9)"), str);
828 		fatal(SccsError);
829 		break;
830 	case NOAPPLY:
831 		sid_ba(sidp,str);
832 		sprintf(SccsError, gettext("%s already excluded (ge10)"), str);
833 		fatal(SccsError);
834 		break;
835 	default:
836 		fatal(gettext("internal error in get/enter() (ge11)"));
837 		break;
838 	}
839 }
840 
841 static void
gen_lfile(pkt)842 gen_lfile(pkt)
843 register struct packet *pkt;
844 {
845 	char *n;
846 	int reason;
847 	char str[max(DT_ZSTRSIZE, SID_STRSIZE)]; /* SCCS v6 date or SID str */
848 	char line[BUFSIZ];
849 	struct deltab dt;
850 	FILE *in;
851 	FILE *out;
852 	char *outname = NOGETTEXT("stdout");
853 
854 #define	OUTPUTC(c) \
855 	if (putc((c), out) == EOF) \
856 		xmsg(outname, NOGETTEXT("gen_lfile"));
857 
858 	in = xfopen(pkt->p_file, O_RDONLY|O_BINARY);
859 	if (pkt->p_lfile) {
860 		/*
861 		 * Do not call setvbuf() with stdout as this may result
862 		 * in a second illegal call in get().
863 		 */
864 		out = stdout;
865 	} else {
866 		outname = auxf(pkt->p_file, 'l');
867 		out = xfcreat(outname,(mode_t)0444);
868 	}
869 	fgets(line,sizeof(line),in);
870 	while (fgets(line,sizeof(line),in) != NULL &&
871 	       line[0] == CTLCHAR && line[1] == STATS) {
872 		fgets(line,sizeof(line),in);
873 		del_ab(line,&dt,pkt);
874 		if (dt.d_type == 'D' || dt.d_type == 'U') {
875 			reason = pkt->p_apply[dt.d_serial].a_reason;
876 			if (pkt->p_apply[dt.d_serial].a_code == APPLY) {
877 				OUTPUTC(' ');
878 				OUTPUTC(' ');
879 			} else {
880 				OUTPUTC('*');
881 				if (reason & IGNR) {
882 					OUTPUTC(' ');
883 				} else {
884 					OUTPUTC('*');
885 				}
886 			}
887 			switch (reason & (INCL | EXCL | CUTOFF)) {
888 
889 			case INCL:
890 				OUTPUTC('I');
891 				break;
892 			case EXCL:
893 				OUTPUTC('X');
894 				break;
895 			case CUTOFF:
896 				OUTPUTC('C');
897 				break;
898 			default:
899 				OUTPUTC(' ');
900 				break;
901 			}
902 			OUTPUTC(' ');
903 			sid_ba(&dt.d_sid,str);
904 			if (fprintf(out, "%s\t", str) == EOF)
905 				xmsg(outname, NOGETTEXT("gen_lfile"));
906 			/*
907 			 * POSIX requires a 2-digit year for the l-file.
908 			 * We use 4-digits before 1969 and past 2068
909 			 * which is outside the year range specified by POSIX.
910 			 */
911 #if SIZEOF_TIME_T == 4
912 			if (dt.d_dtime.dt_sec < Y1969)
913 #else
914 			if ((dt.d_dtime.dt_sec < Y1969) ||
915 			    (dt.d_dtime.dt_sec >= Y2069))
916 #endif
917 				date_bal(&dt.d_dtime.dt_sec,str, pkt->p_flags);	/* 4 digit year */
918 			else
919 				date_ba(&dt.d_dtime.dt_sec,str, pkt->p_flags);	/* 2 digit year */
920 			if (fprintf(out, "%s %s\n", str, dt.d_pgmr) == EOF)
921 				xmsg(outname, NOGETTEXT("gen_lfile"));
922 		}
923 		while ((n = fgets(line,sizeof(line),in)) != NULL) {
924 			if (line[0] != CTLCHAR) {
925 				break;
926 			} else {
927 				switch (line[1]) {
928 
929 				case EDELTAB:
930 					break;
931 				default:
932 					continue;
933 				case MRNUM:
934 				case COMMENTS:
935 					if (dt.d_type == 'D' || dt.d_type == 'U') {
936 					   if (fprintf(out,"\t%s",&line[3]) == EOF)
937 					      xmsg(outname,
938 					         NOGETTEXT("gen_lfile"));
939 					}
940 					continue;
941 				}
942 				break;
943 			}
944 		}
945 		if (n == NULL || line[0] != CTLCHAR)
946 			break;
947 		OUTPUTC('\n');
948 	}
949 	fclose(in);
950 	if (out != stdout) {
951 #ifdef	HAVE_FSYNC
952 		if (fsync(fileno(out)) < 0)
953 			xmsg(outname, NOGETTEXT("gen_lfile"));
954 #endif
955 		if (fclose(out) == EOF)
956 			xmsg(outname, NOGETTEXT("gen_lfile"));
957 	}
958 
959 #undef	OUTPUTC
960 }
961 
962 static void
prfx(pkt)963 prfx(pkt)
964 register struct packet *pkt;
965 {
966 	char str[SID_STRSIZE];		/* Must fit a SID string */
967 
968 	if (HADN)
969 		if (fprintf(pkt->p_gout, "%s\t", getmodname()) == EOF)
970 			xmsg(gfile, NOGETTEXT("prfx"));
971 	if (HADM) {
972 		sid_ba(&pkt->p_inssid,str);
973 		if (fprintf(pkt->p_gout, "%s\t", str) == EOF)
974 			xmsg(gfile, NOGETTEXT("prfx"));
975 	}
976 	if (pkt->p_pgmrs)
977 		annotate(pkt);
978 }
979 
980 static void
annotate(pkt)981 annotate(pkt)
982 	register struct packet *pkt;
983 {
984 	char	str[SID_STRSIZE];	/* Must fit a SID string */
985 	char	*p;
986 
987 	date_ba(&pkt->p_idel[pkt->p_insser].i_datetime.tv_sec,
988 				str, pkt->p_flags);
989 	p = strchr(str, ' ');
990 	if (p)
991 		*p = '\0';
992 	if (fprintf(pkt->p_gout, "%-8s\t%s\t",
993 				pkt->p_pgmrs[pkt->p_insser], str) == EOF)
994 		xmsg(gfile, NOGETTEXT("prfx"));
995 }
996 
997 static void
clean_up()998 clean_up()
999 {
1000 	/*
1001 	clean_up is only called from fatal() upon bad termination.
1002 	*/
1003 	if (gpkt.p_gout) {
1004 		fflush(gpkt.p_gout);
1005 	}
1006 	if (gpkt.p_gout && gpkt.p_gout != stdout) {
1007 		fclose(gpkt.p_gout);
1008 		gpkt.p_gout = NULL;
1009 		unlink(Gfile);
1010 	}
1011 	if (HADE) {
1012 		uname(&un);
1013 		uuname = un.nodename;
1014 		if (mylock(auxf(gpkt.p_file,'z'), getpid(), uuname)) {
1015 			unlink(auxf(gpkt.p_file,'q'));
1016 			timersetlockfile(NULL);
1017 			if (!islockchset(Zhold))
1018 				unlockit(Zhold, getpid(), uuname);
1019 		}
1020 	}
1021 	sclose(&gpkt);
1022 	sfree(&gpkt);
1023 	ffreeall();
1024 	if (HADUCA)				/* ffreeall() killed it	*/
1025 		lhash_destroy();		/* need to reset it	*/
1026 }
1027 
1028 static	char	warn[] = NOGETTEXT("WARNING: being edited: `%s' (ge18)\n");
1029 
1030 static void
wrtpfile(pkt,inc,exc)1031 wrtpfile(pkt,inc,exc)
1032 register struct packet *pkt;
1033 char *inc, *exc;
1034 {
1035 	char line[64];
1036 	char str1[SID_STRSIZE], str2[SID_STRSIZE]; /* Must fit a SID string */
1037 	char *user, *pfile;
1038 	FILE *in, *out;
1039 	struct pfile pf;
1040 	extern dtime_t Timenow;
1041 	int fd;
1042 	char **sflags = pkt->p_sflags;
1043 
1044 	if ((user=logname()) == NULL)
1045 	   fatal(gettext("User ID not in password file (cm9)"));
1046 	if ((fd=open(auxf(pkt->p_file,'q'),O_WRONLY|O_CREAT|O_EXCL|O_BINARY,0444)) < 0) {
1047 	   efatal(gettext("cannot create lock file (cm4)"));
1048 	}
1049 #ifdef	HAVE_FCHMOD
1050 	fchmod(fd, (mode_t)0644);
1051 #else
1052 	chmod(auxf(pkt->p_file,'q'), (mode_t)0644);
1053 #endif
1054 	out = fdfopen(fd, O_WRONLY|O_BINARY);
1055 	if (exists(pfile = auxf(pkt->p_file,'p'))) {
1056 		in = fdfopen(xopen(pfile, O_RDONLY|O_BINARY), O_RDONLY|O_BINARY);
1057 		while (fgets(line,sizeof(line),in) != NULL) {
1058 			if (fputs(line, out) == EOF)
1059 			   xmsg(pfile, NOGETTEXT("wrtpfile"));
1060 			pf_ab(line,&pf,0);
1061 			if (!(sflags[JOINTFLAG - 'a'])) {
1062 				if ((pf.pf_gsid.s_rel == pkt->p_gotsid.s_rel &&
1063      				   pf.pf_gsid.s_lev == pkt->p_gotsid.s_lev &&
1064 				   pf.pf_gsid.s_br == pkt->p_gotsid.s_br &&
1065 				   pf.pf_gsid.s_seq == pkt->p_gotsid.s_seq) ||
1066 				   (pf.pf_nsid.s_rel == pkt->p_reqsid.s_rel &&
1067 				   pf.pf_nsid.s_lev == pkt->p_reqsid.s_lev &&
1068 				   pf.pf_nsid.s_br == pkt->p_reqsid.s_br &&
1069 				   pf.pf_nsid.s_seq == pkt->p_reqsid.s_seq)) {
1070 					fclose(in);
1071 					sprintf(SccsError,
1072 					   gettext("being edited: `%s' (ge17)"),
1073 					   line);
1074 					fatal(SccsError);
1075 				}
1076 				if (!equal(pf.pf_user,user))
1077 					fprintf(stderr,warn,line);
1078 			} else {
1079 			   fprintf(stderr,warn,line);
1080 			}
1081 		}
1082 		fclose(in);
1083 	}
1084 	if (fseek(out, (off_t)0, SEEK_END) == EOF)
1085 		xmsg(pfile, NOGETTEXT("wrtpfile"));
1086 	sid_ba(&pkt->p_gotsid,str1);
1087 	sid_ba(&pkt->p_reqsid,str2);
1088 	/*
1089 	 * POSIX does not explicitly mention a 2-digit year for the p-file but
1090 	 * refers to "date-time" which is most likely expected to have 2-digits.
1091 	 * We use 4-digits before 1969 and past 2068
1092 	 * which is outside the year range specified by POSIX.
1093 	 */
1094 #if SIZEOF_TIME_T == 4
1095 	if (Timenow.dt_sec < Y1969)
1096 #else
1097 	if ((Timenow.dt_sec < Y1969) ||
1098 	    (Timenow.dt_sec >= Y2069))
1099 #endif
1100 		date_bal(&Timenow.dt_sec, line, 0);	/* 4 digit year */
1101 	else
1102 		date_ba(&Timenow.dt_sec, line, 0);	/* 2 digit year */
1103 	if (fprintf(out,"%s %s %s %s",str1,str2,user,line) == EOF)
1104 		xmsg(pfile, NOGETTEXT("wrtpfile"));
1105 	if (inc)
1106 		if (fprintf(out," -i%s",inc) == EOF)
1107 			xmsg(pfile, NOGETTEXT("wrtpfile"));
1108 	if (exc)
1109 		if (fprintf(out," -x%s",exc) == EOF)
1110 			xmsg(pfile, NOGETTEXT("wrtpfile"));
1111 	if (cmrinsert(pkt) > 0)	/* if there are CMRS and they are okay */
1112 		if (fprintf (out, " -z%s", cmr) == EOF)
1113 			xmsg(pfile, NOGETTEXT("wrtpfile"));
1114 	if (fprintf(out, "\n") == EOF)
1115 		xmsg(pfile, NOGETTEXT("wrtpfile"));
1116 	if (fflush(out) == EOF)
1117 		xmsg(pfile, NOGETTEXT("wrtpfile"));
1118 #ifdef	HAVE_FSYNC
1119 	if (fsync(fileno(out)) < 0)
1120 		xmsg(pfile, NOGETTEXT("wrtpfile"));
1121 #endif
1122 	if (fclose(out) == EOF)
1123 		xmsg(pfile, NOGETTEXT("wrtpfile"));
1124 	if (pkt->p_verbose) {
1125                 if (HADQ)
1126                    (void)fprintf(pkt->p_stdout, gettext("new version %s\n"),
1127 				str2);
1128                 else
1129                    (void)fprintf(pkt->p_stdout, gettext("new delta %s\n"),
1130 				str2);
1131 	}
1132 }
1133 
1134 /* cmrinsert -- insert CMR numbers in the p.file. */
1135 
1136 static int
cmrinsert(pkt)1137 cmrinsert(pkt)
1138 register struct packet *pkt;
1139 {
1140 	char holdcmr[CMRLIMIT];
1141 	char tcmr[CMRLIMIT];
1142 	char *p;
1143 	int bad;
1144 	int isvalid;
1145 
1146 	if (pkt->p_sflags[CMFFLAG - 'a'] == 0)	{ /* CMFFLAG was not set. */
1147 		return (0);
1148 	}
1149 	if (HADP && !HADZ) { /* no CMFFLAG and no place to prompt. */
1150 		fatal(gettext("Background CASSI get with no CMRs\n"));
1151 	}
1152 retry:
1153 	if (cmr[0] == '\0') {	/* No CMR list.  Make one. */
1154 		if (HADZ && ((!isatty(0)) || (!isatty(1)))) {
1155 		   fatal(gettext("Background CASSI get with invalid CMR\n"));
1156 		}
1157 		fprintf (stdout,
1158 		   gettext("Input Comma Separated List of CMRs: "));
1159 		fgets(cmr, CMRLIMIT, stdin);
1160 		p=strend(cmr);
1161 		*(--p) = '\0';
1162 		if ((int)(p - cmr) == CMRLIMIT) {
1163 		   fprintf(stdout, gettext("?Too many CMRs.\n"));
1164 		   cmr[0] = '\0';
1165 		   goto retry; /* Entry was too long. */
1166 		}
1167 	}
1168 	/* Now, check the comma separated list of CMRs for accuracy. */
1169 	bad = 0;
1170 	isvalid = 0;
1171 	strlcpy(tcmr, cmr, sizeof (tcmr));
1172 	while ((p=strrchr(tcmr,',')) != NULL) {
1173 		++p;
1174 		if (cmrcheck(p, pkt->p_sflags[CMFFLAG - 'a'])) {
1175 			++bad;
1176 		} else {
1177 			++isvalid;
1178 			strlcat(holdcmr, ",", sizeof (holdcmr));
1179 			strlcat(holdcmr, p, sizeof (holdcmr));
1180 		}
1181 		*(--p) = '\0';
1182 	}
1183 	if (*tcmr) {
1184 		if (cmrcheck(tcmr, pkt->p_sflags[CMFFLAG - 'a'])) {
1185 			++bad;
1186 		} else {
1187 			++isvalid;
1188 			strlcat(holdcmr, ",", sizeof (holdcmr));
1189 			strlcat(holdcmr, tcmr, sizeof (holdcmr));
1190 		}
1191 	}
1192 	if (!bad && holdcmr[1]) {
1193 	   strlcpy(cmr, holdcmr+1, sizeof (cmr));
1194 	   return(1);
1195 	} else {
1196 	   if ((isatty(0)) && (isatty(1))) {
1197 	      if (!isvalid)
1198 		 fprintf(stdout,
1199 		    gettext("Must enter at least one valid CMR.\n"));
1200 	      else
1201 		 fprintf(stdout,
1202 		    gettext("Re-enter invalid CMRs, or press return.\n"));
1203 	   }
1204 	   cmr[0] = '\0';
1205 	   goto retry;
1206 	}
1207 }
1208