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  * @(#)admin.c	1.148 20/09/16 J. Schilling
33  */
34 #if defined(sun)
35 #pragma ident "@(#)admin.c 1.148 20/09/16 J. Schilling"
36 #endif
37 /*
38  * @(#)admin.c 1.39 06/12/12
39  */
40 
41 #if defined(sun)
42 #pragma ident	"@(#)admin.c"
43 #pragma ident	"@(#)sccs:cmd/admin.c"
44 #endif
45 
46 # define	NEED_PRINTF_J		/* Need defines for js_snprintf()? */
47 # define	SCCS_MAIN		/* define global vars */
48 # include	<defines.h>
49 # include	<version.h>
50 # include	<had.h>
51 # include	<i18n.h>
52 # include	<schily/dirent.h>
53 # include	<schily/setjmp.h>
54 # include	<schily/utsname.h>
55 # include	<schily/wait.h>
56 # include	<schily/sysexits.h>
57 # include	<schily/maxpath.h>
58 # define	VMS_VFORK_OK
59 # include	<schily/vfork.h>
60 
61 /*
62 	Program to create new SCCS files and change parameters
63 	of existing ones. Arguments to the program may appear in
64 	any order and consist of keyletters, which begin with '-',
65 	and named files. Named files which do not exist are created
66 	and their parameters are initialized according to the given
67 	keyletter arguments, or are given default values if the
68 	corresponding keyletters were not supplied. Named files which
69 	do exist have those parameters corresponding to given key-letter
70 	arguments changed and other parameters are left as is.
71 
72 	If a directory is given as an argument, each SCCS file within
73 	the directory is processed as if it had been specifically named.
74 	If a name of '-' is given, the standard input is read for a list
75 	of names of SCCS files to be processed.
76 	Non-SCCS files are ignored.
77 
78 	Files created are given mode 444.
79 */
80 
81 /*
82 TRANSLATION_NOTE
83 For all message strings the double quotation marks at the beginning
84 and end of the string MUST be included in the translation.  Formatting
85 characters, e.g. "%s" "%c" "%d" "\n" "\t" must appear in the
86 translated string exactly as they do in the msgid string.  Spaces
87 and/or tabs around these formats must be maintained.
88 
89 The following are examples of text that should not be translated, but
90 should appear exactly as they do in the msgid string:
91 
92 - Any SCCS error code, which will be one or two letters followed by one
93   or two numbers all in parenthesis, e.g. "(ad3)",
94 
95 - All descriptions of SCCS option flags, e.g. "-r" or " 'e'" , or "f",
96   or "-fz"
97 
98 - ".FRED", "sid", "SID", "MRs", "CMR", "p-file", "CASSI",  "cassi",
99   function names, e.g. "getcwd()",
100 */
101 
102 # define MAXNAMES 9
103 
104 static FILE	*Cs;			/* The changeset file		*/
105 static char	stdin_file_buf [20];	/* For "/tmp/admin.XXXXXX"	*/
106 static char	*ifile;			/* -i argument			*/
107 static char	*tfile;			/* -t argument			*/
108 static char	*dir_name;		/* directory for -N		*/
109 static struct timespec	ifile_mtime;	/* Timestamp for -i -o		*/
110 static Nparms	N;			/* Keep -N parameters		*/
111 static Xparms	X;			/* Keep -X parameters		*/
112 static char	*CMFAPPL;		/* CMF MODS			*/
113 static char	*z;			/* for validation program name	*/
114 static char	had_flag[NFLAGS];	/* -f seen list			*/
115 static char	rm_flag[NFLAGS];	/* -d seen list			*/
116 #if	defined(PROTOTYPES) && defined(INS_BASE)
117 static char	Valpgmp[]	=	NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "bin/" "val");
118 #endif
119 static char	Valpgm[]	=	NOGETTEXT("val");
120 static int	fexists;		/* Current file exists		*/
121 static int	num_files;		/* Number of file args		*/
122 static int	VFLAG  =  0;		/* -v option seen		*/
123 static int	versflag = -1;		/* history vers for new files	*/
124 static struct sid	new_sid;	/* -r argument			*/
125 static char	*anames[MAXNAMES];	/* -a arguments			*/
126 static char	*enames[MAXNAMES];	/* -e arguments			*/
127 static char	*unlockarg;		/* -dl argument			*/
128 static char	*locks;			/* 'l' flag value in file	*/
129 static char	*flag_p[NFLAGS];	/* -f arguments			*/
130 static int	asub;			/* Index for anames[]		*/
131 static int	esub;			/* Index for enames[]		*/
132 static int	check_id;		/* To check for Keywds with -i	*/
133 static struct	utsname	un;		/* uname for lockit()		*/
134 static char	*uuname;		/* un.nodename			*/
135 static int 	Encoded = EF_TEXT;	/* Default encoding is '0'	*/
136 static off_t	Encodeflag_offset;	/* offset in file where encoded flag is stored */
137 static off_t	Flagdummy_offset;	/* offset in file where dummy flag is stored */
138 static off_t	Checksum_offset;	/* offset in file where g-file hash is stored */
139 
140 	int	main __PR((int argc, char **argv));
141 static	void	admin __PR((char *afile));
142 static	int	fgetchk __PR((FILE *inptr, char *file, struct packet *pkt, int fflag));
143 static	void	warnctl __PR((char *file, off_t nline));
144 static	void	warnnull __PR((char *file, off_t nline));
145 static	void	clean_up __PR((void));
146 static	void	cmt_ba __PR((register struct deltab *dt, char *str, int flags));
147 static	void	putmrs __PR((struct packet *pkt));
148 static	char *	adjust __PR((char *line));
149 static	char *	getval __PR((register char *sourcep, register char *destp));
150 static	int	val_list __PR((register char *list));
151 static	int	pos_ser __PR((char *s1, char *s2));
152 static	int	range __PR((register char *line));
153 static	FILE *	code __PR((FILE *iptr, char *afile, off_t offset, int thash, struct packet *pktp));
154 static	void	direrror __PR((char *dir, int keylet));
155 
156 int
main(argc,argv)157 main(argc,argv)
158 int argc;
159 char *argv[];
160 {
161 	register int j;
162 	register char *p;
163 	char  f;
164 	int i, testklt,c;
165 	extern int Fcnt;
166 	struct sid sid;
167 	int no_arg=0;
168 	int current_optind;
169 
170 	/*
171 	 * Set locale for all categories.
172 	 */
173 	setlocale(LC_ALL, NOGETTEXT(""));
174 
175 	sccs_setinsbase(INS_BASE);
176 
177 	/*
178 	 * Set directory to search for general l10n SCCS messages.
179 	 */
180 #ifdef	PROTOTYPES
181 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
182 	   NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
183 #else
184 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
185 	   NOGETTEXT("/usr/ccs/lib/locale/"));
186 #endif
187 
188 	(void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
189 
190 	tzset();	/* Set up timezome related vars */
191 
192 #ifdef	SCHILY_BUILD
193 	save_args(argc, argv);
194 #endif
195 	/*
196 	Set flags for 'fatal' to issue message, call clean-up
197 	routine and terminate processing.
198 	*/
199 	set_clean_up(clean_up);
200 	Fflags = FTLMSG | FTLCLN | FTLEXIT;
201 #ifdef	SCCS_FATALHELP
202 	Fflags |= FTLFUNC;
203 	Ffunc = sccsfatalhelp;
204 #endif
205 
206 	testklt = 1;
207 
208 	/*
209 	The following loop processes keyletters and arguments.
210 	Note that these are processed only once for each
211 	invocation of 'main'.
212 	*/
213 
214 	current_optind = 1;
215 	optind = 1;
216 	opterr = 0;
217 	j = 1;
218 	/*CONSTCOND*/
219 	while (1) {
220 			if(current_optind < optind) {
221 			   current_optind = optind;
222 			   argv[j] = 0;
223 			   if (optind > j+1 ) {
224 			      if((argv[j+1][0] != '-') && !no_arg){
225 			        argv[j+1] = NULL;
226 			      }
227 			      else {
228 				optind = j+1;
229 			        current_optind = optind;
230 			      }
231 			   }
232 			}
233 			no_arg = 0;
234 			j = current_optind;
235 		        c = getopt(argc, argv, "()-i:t:m:y:d:f:r:nN:hzboqkw:a:e:X:V:(version)");
236 				/*
237 				*  this takes care of options given after
238 				*  file names.
239 				*/
240 			if (c == EOF) {
241 			   if (optind < argc) {
242 				/* if it's due to -- then break; */
243 			       if(argv[j][0] == '-' &&
244 				      argv[j][1] == '-') {
245 			          argv[j] = 0;
246 			          break;
247 			       }
248 			       optind++;
249 			       current_optind = optind;
250 			       continue;
251 			   } else {
252 			       break;
253 			   }
254 			}
255 			p = optarg;
256 			switch (c) {
257 
258 			case 'i':	/* name of file of body */
259 				if (optarg == argv[j+1]) {
260 				   no_arg = 1;
261 				   ifile = "";
262 				   break;
263 				}
264 				/*
265 				 * Use -i. to tell admin to retrieve the ifile
266 				 * name from the g-file name with -N
267 				 * We cannot check for "." here as -N may have
268 				 * been specified later on the cmd line.
269 				 */
270 				ifile = p;
271 				if (*ifile &&
272 				    !(ifile[0] == '.' && ifile[1] == '\0') &&
273 				    exists(ifile)) {
274 				   if ((Statbuf.st_mode & S_IFMT) == S_IFDIR) {
275 					direrror(ifile, c);
276 				   } else {
277 					ifile_mtime.tv_sec = Statbuf.st_mtime;
278 					ifile_mtime.tv_nsec = stat_mnsecs(&Statbuf);
279 				   }
280 				}
281 				break;
282 			case 't':	/* name of file of descriptive text */
283 				if (optarg == argv[j+1]) {
284 				   no_arg = 1;
285 				   tfile = NULL;
286 				   break;
287 				}
288 				tfile = p;
289 				if (*tfile && exists(tfile))
290 				   if ((Statbuf.st_mode & S_IFMT) == S_IFDIR)
291 					direrror(tfile, c);
292 				break;
293 			case 'm':	/* mr flag */
294 				/*
295 				 * Former sccs versions did allow to call
296 				 * admin -fv -m -i... to specify "no mr".
297 				 * With getopt, this only works with '-m '
298 				 * or -m ''.
299 				 */
300 				if (*p == '-')
301 					fatal(gettext("bad m argument (ad34)"));
302 				Mrs = p;
303 				repl(Mrs,'\n',' ');
304 				break;
305 			case 'y':	/* comments flag for entry */
306 				if (optarg == argv[j+1]) {
307 				   no_arg = 1;
308 				   Comments = "";
309 				} else {
310 				   Comments = p;
311 				}
312 				break;
313 			case 'd':	/* flags to be deleted */
314 				testklt = 0;
315 				if ((f = *p) == '\0')
316 				     fatal(gettext("d has no argument (ad1)"));
317 				p = p+1;
318 
319 				switch (f) {
320 
321 				case IDFLAG:	/* see 'f' keyletter */
322 				case BRCHFLAG:	/* for meanings of flags */
323 				case VALFLAG:
324 				case TYPEFLAG:
325 				case MODFLAG:
326 				case QSECTFLAG:
327 				case NULLFLAG:
328 				case FLORFLAG:
329 				case CEILFLAG:
330 				case DEFTFLAG:
331 				case JOINTFLAG:
332 				case SCANFLAG:
333 				case EXTENSFLAG:
334 				case EXPANDFLAG:
335 				case CMFFLAG:	/* option installed by CMF */
336 					if (*p) {
337 						sprintf(SccsError,
338 						gettext("value after %c flag (ad12)"),
339 							f);
340 						fatal(SccsError);
341 					}
342 					break;
343 				case LOCKFLAG:
344 					if (*p == '\0') {
345 						fatal(gettext("bad list format (ad27)"));
346 					}
347 					if (*p) {
348 						/*
349 						set pointer to releases
350 						to be unlocked
351 						*/
352 						repl(p,',',' ');
353 						if (!val_list(p))
354 							fatal(gettext("bad list format (ad27)"));
355 						if (!range(p))
356 							fatal(gettext("element in list out of range (ad28)"));
357 						if (*p != 'a')
358 							unlockarg = p;
359 					}
360 					break;
361 
362 				default:
363 					fatal(gettext("unknown flag (ad3)"));
364 				}
365 
366 				if (rm_flag[f - 'a']++)
367 					fatal(gettext("flag twice (ad4)"));
368 				break;
369 
370 			case 'f':	/* flags to be added */
371 				testklt = 0;
372 				if ((f = *p) == '\0')
373 					fatal(gettext("f has no argument (ad5)"));
374 				p = p+1;
375 
376 				switch (f) {
377 
378 				case BRCHFLAG:	/* branch */
379 				case NULLFLAG:	/* null deltas */
380 				case JOINTFLAG:	/* joint edit flag */
381 					if (*p) {
382 						sprintf(SccsError,
383 						gettext("value after %c flag (ad13)"),
384 							f);
385 						fatal(SccsError);
386 					}
387 					break;
388 
389 				case IDFLAG:	/* id-kwd message (err/warn) */
390 					break;
391 
392 				case VALFLAG:	/* mr validation */
393 					VFLAG++;
394 					if (*p)
395 						z = p;
396 					break;
397 
398 				case CMFFLAG: /* CMFNET MODS */
399 					if (*p)	/* CMFNET MODS */
400 						CMFAPPL = p; /* CMFNET MODS */
401 					else /* CMFNET MODS */
402 						fatal (gettext("No application with application flag."));
403 					if (gf (CMFAPPL) == (char*) NULL)
404 						fatal (gettext("No .FRED file exists for this application."));
405 					break; /* END CMFNET MODS */
406 
407 				case FLORFLAG:	/* floor */
408 					if ((i = patoi(p)) == -1)
409 						fatal(gettext("floor not numeric (ad22)"));
410 					if (((int) size(p) > 5) || (i < MINR) ||
411 							(i > MAXR))
412 						fatal(gettext("floor out of range (ad23)"));
413 					break;
414 
415 				case CEILFLAG:	/* ceiling */
416 					if ((i = patoi(p)) == -1)
417 						fatal(gettext("ceiling not numeric (ad24)"));
418 					if (((int) size(p) > 5) || (i < MINR) ||
419 							(i > MAXR))
420 						fatal(gettext("ceiling out of range (ad25)"));
421 					break;
422 
423 				case DEFTFLAG:	/* default sid */
424 					if (!(*p))
425 						fatal(gettext("no default sid (ad14)"));
426 					chksid(sid_ab(p,&sid),&sid);
427 					break;
428 
429 				case TYPEFLAG:	/* type */
430 				case MODFLAG:	/* module name */
431 				case QSECTFLAG:	/* csect name */
432 					if (!(*p)) {
433 						sprintf(SccsError,
434 						gettext("flag %c has no value (ad2)"),
435 							f);
436 						fatal(SccsError);
437 					}
438 					break;
439 				case LOCKFLAG:	/* release lock */
440 					if (!(*p))
441 						/*
442 						lock all releases
443 						*/
444 						p = NOGETTEXT("a");
445 					/*
446 					replace all commas with
447 					blanks in SCCS file
448 					*/
449 					repl(p,',',' ');
450 					if (!val_list(p))
451 						fatal(gettext("bad list format (ad27)"));
452 					if (!range(p))
453 						fatal(gettext("element in list out of range (ad28)"));
454 					break;
455 				case SCANFLAG:	/* the number of lines that are scanned to expand of SCCS KeyWords. */
456 					if ((i = patoi(p)) == -1)
457 						fatal(gettext("line not numeric (ad33)"));
458 					break;
459 				case EXTENSFLAG:	/* exable SCCS extensions */
460 					p = "SCHILY";	/* we use SCHILY type ext */
461 					break;
462 				case EXPANDFLAG:	/* expand the SCCS KeyWords */
463 					/* replace all commas with blanks in SCCS file */
464 					repl(p,',',' ');
465 					break;
466 
467 				default:
468 					fatal(gettext("unknown flag (ad3)"));
469 				}
470 
471 				if (had_flag[f - 'a']++)
472 					fatal(gettext("flag twice (ad4)"));
473 				flag_p[f - 'a'] = p;
474 				break;
475 
476 			case 'r':	/* initial release number supplied */
477 				 chksid(sid_ab(p,&new_sid),&new_sid);
478 				 if ((new_sid.s_rel < MINR) ||
479 				     (new_sid.s_rel > MAXR))
480 					fatal(gettext("r out of range (ad7)"));
481 				 break;
482 
483 			case 'N':	/* creating new SCCS files */
484 				initN(&N);
485 				if (optarg == argv[j+1]) {
486 				   no_arg = 1;
487 				   break;
488 				}
489 				N.n_parm = p;
490 				break;
491 			case 'n':	/* creating new SCCS file */
492 			case 'h':	/* only check hash of file */
493 			case 'k':	/* get(1) without keyword expand */
494 			case 'z':	/* zero the input hash */
495 			case 'b':	/* force file to be encoded (binary) */
496 			case 'o':	/* use original file date */
497 				break;
498 
499                         case 'q':       /* activate NSE features */
500 				if(p) {
501                                   if (*p) {
502                                         nsedelim = p;
503 				  }
504                                 } else {
505                                         nsedelim = (char *) 0;
506                                 }
507                                 break;
508 
509 			case 'w':
510 				if (p)
511 					whatsetup(p);
512 				break;
513 
514 			case 'a':	/* user-name allowed to make deltas */
515 				testklt = 0;
516 				if (!(*p) || *p == '-')
517 					fatal(gettext("bad a argument (ad8)"));
518 				if (asub > MAXNAMES) {
519 					fatal(gettext("too many 'a' keyletters (ad9)"));
520 					/* NOTREACHED */
521 				}
522 				anames[asub++] = sccs_user(p);
523 				break;
524 
525 			case 'e':	/* user-name to be removed */
526 				testklt = 0;
527 				if (!(*p) || *p == '-')
528 					fatal(gettext("bad e argument (ad10)"));
529 				if (esub > MAXNAMES) {
530 					fatal(gettext("too many 'e' keyletters (ad11)"));
531 					/* NOTREACHED */
532 				}
533 				enames[esub++] = sccs_user(p);
534 				break;
535 
536 			case 'X':
537 				X.x_parm = optarg;
538 				X.x_flags = XO_INIT_PATH|XO_URAND|\
539 					XO_UNLINK|XO_MAIL|XO_USER|XO_DATE|\
540 					XO_NULLPATH|XO_NOBULK|XO_G_PATH;
541 				if (!parseX(&X))
542 					goto err;
543 				had[NLOWER+c-'A'] = 0;	/* Allow mult -X */
544 				break;
545 
546 			case 'V':		/* version */
547 				if (optarg == argv[j+1]) {
548 				doversion:
549 					printf(gettext(
550 					    "admin %s-SCCS version %s %s (%s-%s-%s)\n"),
551 						PROVIDER,
552 						VERSION,
553 						VDATE,
554 						HOST_CPU, HOST_VENDOR, HOST_OS);
555 					exit(EX_OK);
556 				}
557 				if (p[1] == '\0') {
558 					if (p[0] == '4') {
559 						versflag = 4;
560 						break;
561 					} else if (p[0] == '6') {
562 						versflag = 6;
563 						break;
564 					}
565 				}
566 
567 			default:
568 			err:
569 				/*
570 				 * Check whether "-V" was last arg...
571 				 */
572 				if (optind == argc &&
573 				    ((argv[argc-1][0] == '-' &&
574 				    argv[argc-1][1] == 'V' &&
575 				    argv[argc-1][2] == '\0') ||
576 				    strcmp(argv[argc-1], "-version") == 0 ||
577 				    strcmp(argv[argc-1], "--version") == 0))
578 					goto doversion;
579 				fatal(gettext("Usage: admin [ -bhknoz ][ -ausername|groupid ]\n\t[ -dflag ][ -eusername|groupid ]\n\t[ -fflag [value]][ -i [filename]]\n\t[ -m mr-list][ -r release ][ -t [description-file]]\n\t[ -N[bulk-spec]][ -Xxopts ] [ -y[comment]] s.filename ..."));
580 			}
581 			/*
582 			 * Make sure that we only collect option letters from
583 			 * the range 'a'..'z' and 'A'..'Z'.
584 			 */
585 			if (ALPHA(c) &&
586 			    (had[LOWER(c)? c-'a' : NLOWER+c-'A']++ && testklt++)) {
587 				if (c != 'X')
588 					fatal(gettext("key letter twice (cm2)"));
589 			}
590 	}
591 
592 	for (i = 1; i < argc; i++){
593 		if (argv[i]) {
594 		       num_files++;
595 		}
596 	}
597 
598 	if (num_files == 0 && !HADUCN)
599 		fatal(gettext("missing file arg (cm3)"));
600 	if (num_files > 1 && (X.x_opts & (XO_INIT_PATH|XO_URAND)))
601 		fatal(gettext("too many file args (cm18)"));
602 
603 	setsig();
604 	xsethome(NULL);
605 	if (HADUCN) {					/* Parse -N args  */
606 		/*
607 		 * initN() was already called while parsing options.
608 		 */
609 		if (HADI)
610 			N.n_flags |= N_IFILE;
611 		if (HADI && ifile[0] == '.' && ifile[1] == '\0')
612 			N.n_flags |= N_IDOT;
613 		if (HADN)
614 			N.n_flags |= N_NFILE;
615 		parseN(&N);
616 		if (N.n_get)
617 			N.n_flags |= N_GETI;
618 
619 		if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
620 			fatal(gettext("-Ns. not supported in off-tree project mode"));
621 
622 		/*
623 		 * If someone previously successfully called "sccs init" and
624 		 * "admin" is called with the option -N, we default to -V6.
625 		 */
626 		if (versflag < 0 && SETHOME_INIT()) {
627 			versflag = 6;
628 		}
629 
630 	} else if (HADI && ifile[0] == '.' && ifile[1] == '\0') {
631 		direrror(ifile, 'i');
632 	}
633 
634 #ifdef	SCCS_V6_ENV
635 	if (versflag < 0) {		/* Not explicitly selected, check ENV */
636 		if (getenv("SCCS_V6"))
637 			versflag = 6;
638 	}
639 #endif
640 	if (versflag < 0)
641 		versflag = 4;		/* This is the historic default */
642 
643 	if ((HADY || HADM) && ! (HADI || HADN))
644 		fatal(gettext("illegal use of 'y' or 'm' keyletter (ad30)"));
645 	if (HADI && !HADUCN && num_files > 1) /* only one file allowed with `i' */
646 		fatal(gettext("more than one file (ad15)"));
647 	if ((HADI || HADN) && ! logname())
648 		fatal(gettext("USER ID not in password file (cm9)"));
649 
650 	/*
651 	 * Get the name of our machine to be used for the lockfile.
652 	 */
653 	uname(&un);
654 	uuname = un.nodename;
655 
656 	/*
657 	 * Set up a project global lock on the changeset file.
658 	 * Since we set FTLJMP, we do not need to unlockchset() from clean_up().
659 	 */
660 	if (!HADH && SETHOME_CHSET()) {
661 		lockchset(getppid(), getpid(), uuname);
662 		if (HADUCN && exists(changesetgfile)) {
663 			/*
664 			 * Should we open/create the file even if it does not
665 			 * yet exist? This may change with the final concept.
666 			 */
667 			Cs = xfopen(changesetgfile, O_WRONLY|O_APPEND|O_BINARY);
668 		}
669 	}
670 	timerchsetlock();
671 
672 	/*
673 	Change flags for 'fatal' so that it will return to this
674 	routine (main) instead of terminating processing.
675 	*/
676 	Fflags &= ~FTLEXIT;
677 	Fflags |= FTLJMP;
678 
679 	/*
680 	Call 'admin' routine for each file argument.
681 	*/
682 	for (j=1; j<argc; j++)
683 		if ((p = argv[j]) != NULL)
684 			do_file(p, admin, HADN|HADI ? 0 : 1, N.n_sdot, &X);
685 
686 	if (num_files == 0 && HADUCN)
687 		do_file("-", admin, 0, N.n_sdot, &X);
688 
689 	/*
690 	 * Only remove the global lock it it was created by us and not by
691 	 * our parent.
692 	 */
693 	if (!HADH && SETHOME_CHSET()) {
694 		if (HADUCN) {
695 			bulkchdir(&N);
696 			if (Cs)
697 				fclose(Cs);
698 		}
699 		unlockchset(getpid(), uuname);
700 	}
701 
702 	return (Fcnt ? 1 : 0);
703 }
704 
705 /*
706 	Routine that actually does admin's work on SCCS files.
707 	Existing s-files are copied, with changes being made, to a
708 	temporary file (x-file). The name of the x-file is the same as the
709 	name of the s-file, with the 's.' replaced by 'x.'.
710 	s-files which are to be created are processed in a similar
711 	manner, except that a dummy s-file is first created with
712 	mode 444.
713 	At end of processing, the x-file is renamed with the name of s-file
714 	and the old s-file is removed.
715 */
716 
717 static struct packet gpkt;	/* see file defines.h */
718 static char	Zhold[MAXPATHLEN];	/* temporary z-file name */
719 
720 static void
admin(afile)721 admin(afile)
722 char	*afile;
723 {
724 	struct deltab dt;	/* see file defines.h */
725 	struct stats stats;	/* see file defines.h */
726 	struct stat sbuf;
727 	FILE	*iptr;
728 	register int k;
729 	register char *cp;
730 	register signed char *q;
731 	char	*in_f;		/* ptr holder for lockflag vals in SCCS file */
732 	char	nline[MAXLINE];
733 	char	*p_lval, *tval;
734 	char	*lval;
735 	char	f;		/* character holder for flag character */
736 	char	line[MAXLINE];
737 	int	ck_it;		/* used for lockflag checking */
738 	off_t	offset;
739 	int     thash;
740 	int	status;		/* used for status return from fork */
741 	int	from_stdin;	/* used for ifile */
742 	extern	char had_dir;
743 
744 	if (setjmp(Fjmp))	/* set up to return here from 'fatal' */
745 		return;		/* and return to caller of admin */
746 
747 	/*
748 	 * Initialize here to avoid setjmp() clobbering warnings
749 	 */
750 	iptr = NULL;
751 	lval = NULL;
752 	ck_it = 0;
753 	from_stdin = 0;
754 	dir_name = "";
755  	Encoded = EF_TEXT;	/* Default encoding is '0' */
756 
757 	zero((char *) &stats,sizeof(stats));
758 
759 	/*
760 	 * In order to make the global lock with a potentially long duration
761 	 * not look as if it was expired, we refresh it for every file in our
762 	 * task list. This is needed since another SCCS instance on a different
763 	 * NFS machine cannot use kill() to check for a still active process.
764 	 */
765 	if (!HADH && SETHOME_CHSET()) {
766 		if (HADUCN)
767 			bulkchdir(&N);	/* Done by bulkprepare() anyway */
768 		refreshchsetlock();
769 	}
770 
771 	if (HADUCN && !(X.x_opts & XO_NOBULK)) {
772 		char	*oafile = afile;
773 
774 		afile = bulkprepare(&N, afile);
775 		if (N.n_error != 0 && N.n_error != BULK_EISDIR) {
776 			fatal(gettext(bulkerror(&N)));
777 		} else if (afile == NULL) {
778 			if (N.n_ifile)
779 				direrror(N.n_ifile, 'i');
780 			else
781 				direrror(oafile, 'i');
782 		}
783 		if (N.n_mtime.tv_sec != 0) {
784 			ifile_mtime.tv_sec = N.n_mtime.tv_sec;
785 			ifile_mtime.tv_nsec = N.n_mtime.tv_nsec;
786 		}
787 		had_dir = 0;
788 		dir_name = N.n_dir_name;
789 		if (dir_name == NULL)
790 			dir_name = "";
791 		if (N.n_flags & N_IDOT)
792 			ifile = N.n_ifile;
793 	}
794 	if (X.x_opts & XO_G_PATH)
795 		ifile = X.x_gpath;
796 
797 	if (HADI && had_dir) /* directory not allowed with `i' keyletter */
798 		fatal(gettext("directory named with `i' keyletter (ad26)"));
799 
800 	fexists = exists(afile);
801 
802 	if (HADI)
803 		HADN = 1;
804 	if (HADI || HADN) {
805 		if (VFLAG && had_flag[CMFFLAG - 'a'])
806 			fatal(gettext("Can't have two verification routines."));
807 
808 		if (HADM && !VFLAG && !had_flag[CMFFLAG - 'a'])
809 			fatal(gettext("MRs not allowed (de8)"));
810 
811 		if (VFLAG && !HADM)
812 			fatal(gettext("MRs required (de10)"));
813 
814 	}
815 
816 	if (!(HADI||HADN) && HADR)
817 		fatal(gettext("r only allowed with i or n (ad16)"));
818 
819 	if (HADN && HADT && !tfile)
820 		fatal(gettext("t has no argument (ad17)"));
821 
822 	if (HADN && HADD)
823 		fatal(gettext("d not allowed with n (ad18)"));
824 
825 	if (HADN && fexists) {
826 		sprintf(SccsError, gettext("file %s exists (ad19)"),
827 			afile);
828 		fatal(SccsError);
829 	}
830 
831 	if (!HADN && !fexists) {
832 		sprintf(SccsError, gettext("file %s does not exist (ad20)"),
833 			afile);
834 		fatal(SccsError);
835 	}
836 	if (HADH) {
837 		pid_t	pid;
838 
839 		/*
840 		   fork here so 'admin' can execute 'val' to
841 		   check for a corrupted file.
842 		*/
843 		if ((pid = vfork()) < 0)
844 			efatal(gettext("cannot fork, try again"));
845 		if (pid == 0) {		/* child */
846 			/*
847 			   perform 'val' with appropriate keyletters
848 			*/
849 #if	defined(PROTOTYPES) && defined(INS_BASE)
850 			execlp(Valpgmp, Valpgm, "-s", afile, (char *)0);
851 #endif
852 			execlp(Valpgm, Valpgm, "-s", afile, (char *)0);
853 			sprintf(SccsError, gettext("cannot execute '%s'"),
854 				Valpgm);
855 #ifdef	HAVE_VFORK
856 			Fflags |= FTLVFORK;
857 #endif
858 			efatal(SccsError);
859 		}
860 		else {
861 			wait(&status);	   /* wait on status from 'execlp' */
862 			if (status)
863 				fatal(gettext("corrupted file (co6)"));
864 			return;		/* return to caller of 'admin' */
865 		}
866 	}
867 
868 	/*
869 	 * Lock out any other user who may be trying to process
870 	 * the same file.
871 	 */
872 	if (!HADH && !islockchset(copy(auxf(afile, 'z'), Zhold))) {
873 		if (lockit(Zhold, SCCS_LOCK_ATTEMPTS, getpid(), uuname)) {
874 			lockfatal(Zhold, getpid(), uuname);
875 		}
876 		timersetlockfile(Zhold);
877 	}
878 
879 	if (fexists) { /* modifying */
880 		int	cklen = 8;
881 
882 		sinit(&gpkt, afile, SI_OPEN);	/* init pkt & open s-file */
883 
884 		if (gpkt.p_flags & PF_V6)
885 			cklen = 15;
886 
887 		/* Modify checksum if corrupted */
888 
889 		if ((int) strlen(gpkt.p_line) > cklen &&
890 		    gpkt.p_line[0] == '\001' &&
891 		    gpkt.p_line[1] == '\150') {
892 			gpkt.p_line[cklen-1] = '\012';
893 			gpkt.p_line[cklen]   = '\000';
894 		}
895 	}
896 	else {
897 		if ((int) strlen(sname(afile)) > MAXNAMLEN) {
898 			sprintf(SccsError, gettext("file name is greater than %d characters"),
899 				MAXNAMLEN);
900 			fatal(SccsError);
901 		}
902 		if (sccsfile(afile)) {
903 			FILE	*xf;
904 
905 			/*
906 			 * create dummy s-file
907 			 *
908 			 * Closing is needed on Cygwin to avoid an EPERM in
909 			 * rename()
910 			 */
911 			Statbuf.st_mode = 0;
912 			if (HADI && *ifile)
913 				(void) exists(ifile);
914 			else
915 				(void )exists(auxf(afile,'g'));
916 			if (S_IEXEC & Statbuf.st_mode) {
917 				xf = xfcreat(afile, 0555);
918 			} else {
919 				xf = xfcreat(afile, 0444);
920 			}
921 			if (xf)
922 				fclose(xf);
923 		}
924 
925 		sinit(&gpkt, afile, SI_INIT);	/* and init pkt */
926 
927 		/*
928 		 * Initialize global meta data
929 		 */
930 		if (versflag == 6) {
931 			if (X.x_opts & XO_INIT_PATH) {
932 				gpkt.p_init_path = X.x_init_path;
933 			} else if (HADUCN && !(X.x_opts & XO_NOBULK)) {
934 				/*
935 				 * Only if we have been called with -N..., we
936 				 * know the real g-file name. We cannot derive
937 				 * the g-file name from the s.file name
938 				 * otherwise, since we cannot know about sub
939 				 * dirs like "SCCS" in the other cases.
940 				 */
941 				set_init_path(&gpkt, N.n_ifile, dir_name);
942 			}
943 
944 			if (X.x_opts & XO_URAND)
945 				gpkt.p_rand = X.x_rand;
946 			else
947 				urandom(&gpkt.p_rand);
948 		}
949 	}
950 
951 	if (!HADH)
952 		/*
953 		   set the flag for 'putline' routine to open
954 		   the 'x-file' and allow writing on it.
955 		*/
956 		gpkt.p_upd = 1;
957 
958 	if (HADZ) {
959 		gpkt.do_chksum = 0;	/* ignore checksum processing */
960 		gpkt.p_ihash = 0;
961 	}
962 
963 	/*
964 	Get statistics of latest delta in old file.
965 	*/
966 	if (!HADN) {
967 		stats_ab(&gpkt,&stats);
968 		gpkt.p_wrttn++;
969 		newstats(&gpkt,line,"0");
970 	}
971 
972 	if (HADN) {		/*   N E W   F I L E   */
973 
974 		if (versflag == 6)
975 			gpkt.p_flags |= PF_V6;
976 
977 		/*
978 		Beginning of SCCS file.
979 		*/
980 		putmagic(&gpkt, "00000");
981 
982 		/*
983 		Statistics.
984 		*/
985 		newstats(&gpkt,line,"0");
986 
987 		dt.d_type = 'D';	/* type of delta */
988 		if (X.x_opts & XO_UNLINK)
989 			dt.d_type = 'U';
990 
991 		/*
992 		Set initial release, level, branch and
993 		sequence values.
994 		*/
995 		if (HADR)
996 			{
997 			 dt.d_sid.s_rel = new_sid.s_rel;
998 			 dt.d_sid.s_lev = new_sid.s_lev;
999 			 dt.d_sid.s_br  = new_sid.s_br ;
1000 			 dt.d_sid.s_seq = new_sid.s_seq;
1001 			 if (dt.d_sid.s_lev == 0) dt.d_sid.s_lev = 1;
1002 			 if ((dt.d_sid.s_br) && ( ! dt.d_sid.s_seq))
1003 				dt.d_sid.s_seq = 1;
1004 			}
1005 		else
1006 			{
1007 			 dt.d_sid.s_rel = 1;
1008 			 dt.d_sid.s_lev = 1;
1009 			if (X.x_opts & XO_UNLINK)
1010 				dt.d_sid.s_lev = 0;
1011 			 dt.d_sid.s_br = dt.d_sid.s_seq = 0;
1012 			}
1013 		dtime(&dt.d_dtime);		/* get time and date */
1014                 if (HADN && HADI && (HADO || HADQ) &&
1015 		    (ifile_mtime.tv_sec != 0)) {
1016                         /*
1017 			 * When specifying -o (original date) and
1018 			 * for NSE when putting existing file under sccs the
1019                          * delta time is the mtime of the clear file.
1020                          */
1021 			time2dt(&dt.d_dtime,
1022 				ifile_mtime.tv_sec, ifile_mtime.tv_nsec);
1023                 }
1024 		if (HADN && (X.x_opts & XO_DATE)) {
1025 			dt.d_dtime = X.x_dtime;
1026 		}
1027 
1028 		strlcpy(dt.d_pgmr, logname(), LOGSIZE);	/* get user's name */
1029 		if (X.x_user)				/* from -Xuser=	*/
1030 			strlcpy(dt.d_pgmr, X.x_user, LOGSIZE);
1031 
1032 		dt.d_serial = 1;
1033 		dt.d_pred = 0;
1034 
1035 		gpkt.p_reqsid = dt.d_sid;	/* set sid for changelog */
1036 
1037 		del_ba(&dt,line, gpkt.p_flags);	/* form and write */
1038 		putline(&gpkt,line);	/* delta-table entry */
1039 
1040 		/*
1041 		If -m flag, enter MR numbers
1042 		*/
1043 
1044 		if (Mrs) {
1045 			if (had_flag[CMFFLAG - 'a']) {	/* CMF check routine */
1046 				if (cmrcheck (Mrs, CMFAPPL) != 0) {	/* check them */
1047 					fatal (gettext("Bad CMR number(s)."));
1048 					}
1049 				}
1050 			mrfixup();
1051 			if (z && valmrs(&gpkt,z))
1052 				fatal(gettext("invalid MRs (de9)"));
1053 			putmrs(&gpkt);
1054 		}
1055 		if (gpkt.p_flags & PF_V6) {
1056 			Checksum_offset = ftell(gpkt.p_xiop);
1057 			gpkt.p_mail = X.x_mail;
1058 			sidext_ba(&gpkt, &dt);	/* Will not write "dt" entry. */
1059 			gpkt.p_mail = NULL;
1060 		}
1061 
1062 		/*
1063 		Enter comment line for `chghist'
1064 		*/
1065 
1066 		if (HADY) {
1067 		        char *comment = savecmt(Comments);
1068 			sprintf(line,"%c%c ",CTLCHAR,COMMENTS);
1069 			putline(&gpkt,line);
1070 			putline(&gpkt,comment);
1071 			putline(&gpkt,"\n");
1072 		}
1073 		else {
1074 			/*
1075 			insert date/time and pgmr into comment line
1076 			*/
1077 			cmt_ba(&dt, line, gpkt.p_flags);
1078 			putline(&gpkt,line);
1079 		}
1080 		/*
1081 		End of delta-table.
1082 		*/
1083 		sprintf(line,CTLSTR,CTLCHAR,EDELTAB);
1084 		putline(&gpkt,line);
1085 
1086 		/*
1087 		Beginning of user-name section.
1088 		*/
1089 		sprintf(line,CTLSTR,CTLCHAR,BUSERNAM);
1090 		putline(&gpkt,line);
1091 	}
1092 	else
1093 		/*
1094 		For old file, copy to x-file until user-name section
1095 		is found.
1096 		*/
1097 		flushto(&gpkt, BUSERNAM, FLUSH_COPY);
1098 
1099 	/*
1100 	Write user-names to be added to list of those
1101 	allowed to make deltas.
1102 	*/
1103 	if (HADA)
1104 		for (k = 0; k < asub; k++) {
1105 			sprintf(line,"%s\n",anames[k]);
1106 			putline(&gpkt,line);
1107 		}
1108 
1109 	/*
1110 	Do not copy those user-names which are to be erased.
1111 	*/
1112 	if (HADE && !HADN)
1113 		while (((cp = getline(&gpkt)) != NULL) &&
1114 				!(*cp++ == CTLCHAR && *cp == EUSERNAM)) {
1115 			for (k = 0; k < esub; k++) {
1116 				cp = gpkt.p_line;
1117 				while (*cp)	/* find and */
1118 					cp++;	/* zero newline */
1119 				*--cp = '\0';	/* character */
1120 
1121 				if (equal(enames[k],gpkt.p_line)) {
1122 					/*
1123 					Tell getline not to output
1124 					previously read line.
1125 					*/
1126 					gpkt.p_wrttn = 1;
1127 					break;
1128 				}
1129 				else
1130 					*cp = '\n';	/* restore newline */
1131 			}
1132 		}
1133 
1134 	if (HADN) {		/*   N E W  F I L E   */
1135 
1136 		/*
1137 		End of user-name section.
1138 		*/
1139 		sprintf(line,CTLSTR,CTLCHAR,EUSERNAM);
1140 		putline(&gpkt,line);
1141 	} else {
1142 		/*
1143 		For old file, copy to x-file until end of
1144 		user-names section is found.
1145 		*/
1146 		if (!HADE)
1147 			flushto(&gpkt, EUSERNAM, FLUSH_COPY);
1148 	}
1149 
1150 	/*
1151 	For old file, read flags and their values (if any), and
1152 	store them. Check to see if the flag read is one that
1153 	should be deleted.
1154 	*/
1155 	if (!HADN) {
1156 		while (((cp = getline(&gpkt)) != NULL) &&
1157 				(*cp++ == CTLCHAR && *cp++ == FLAG)) {
1158 
1159 			gpkt.p_wrttn = 1;	/* don't write previous line */
1160 
1161 			NONBLANK(cp);	/* point to flag character */
1162 			k = *cp - 'a';
1163 			if (k < 0 || k >= NFLAGS) {
1164 				fprintf(stderr,
1165 				gettext(
1166 				"WARNING [%s]: unsupported flag at line %d\n"),
1167 					gpkt.p_file,
1168 					gpkt.p_slnno);
1169 				/*
1170 				 * Better to abort then to silently remove flags
1171 				 * as previous versions did.
1172 				 */
1173 				fatal("unsupported flag (ad35)");
1174 				continue;
1175 			}
1176 			f = *cp++;
1177 			NONBLANK(cp);
1178 			if (f == LOCKFLAG) {
1179 				p_lval = cp;
1180 				tval = fmalloc(size(gpkt.p_line)- (unsigned)5);
1181 				copy(++p_lval,tval);
1182 				lval = tval;
1183 				while(*tval)
1184 					++tval;
1185 				*--tval = '\0';
1186 			}
1187 
1188 			if (!had_flag[k] && !rm_flag[k]) {
1189 				had_flag[k] = 2;	/* indicate flag is */
1190 							/* from file, not */
1191 							/* from arg list */
1192 
1193 				if (*cp != '\n') {	/* get flag value */
1194 					q = (signed char *) fmalloc(size(gpkt.p_line) - (unsigned)5);
1195 					copy(cp, (char *)q);
1196 					flag_p[k] = (char*) q;
1197 					while (*q)	/* find and */
1198 						q++;	/* zero newline */
1199 					*--q = '\0';	/* character */
1200 					if (k == ENCODEFLAG - 'a') {
1201 						int	i;
1202 
1203 						NONBLANK(cp);
1204 						cp = satoi(cp, &i);
1205 						if (*cp == '\n')
1206 							gpkt.p_encoding = i;
1207 					}
1208 				}
1209 			}
1210 			if (rm_flag[k]) {
1211 				if (f == LOCKFLAG) {
1212 					if (unlockarg) {
1213 						in_f = lval;
1214 						if (((lval = adjust(in_f)) != NULL) &&
1215 							!had_flag[k])
1216 							ck_it = had_flag[k] = 1;
1217 					}
1218 					else had_flag[k] = 0;
1219 				}
1220 				else had_flag[k] = 0;
1221 			}
1222 		}
1223 	}
1224 
1225 	/*
1226 	Write out flags.
1227 	*/
1228 	/* test to see if the CMFFLAG is safe */
1229 	if (had_flag[CMFFLAG - 'a']) {
1230 		if (had_flag[VALFLAG - 'a'] && !rm_flag[VALFLAG - 'a'])
1231 			fatal (gettext("Can't use -fz with -fv."));
1232 	}
1233 	for (k = 0; k < NFLAGS; k++) {
1234 		if (had_flag[k]) {
1235 			int	i;		/* for flag string cleanup */
1236 
1237 			if (flag_p[k] || lval ) {
1238 				if (('a' + k) == LOCKFLAG && had_flag[k] == 1) {
1239 					if ((flag_p[k] && *flag_p[k] == 'a') || (lval && *lval == 'a'))
1240 						locks = NOGETTEXT("a");
1241 					else if (lval && flag_p[k]) {
1242 						nline[0] = '\0';
1243 						i = strlcatl(nline, sizeof (nline),
1244 							lval, " ", flag_p[k], (char *)0);
1245 						if (i >= sizeof (nline))
1246 							fatal(gettext("line too long (co31)"));
1247 						locks = nline;
1248 					} else if (lval) {
1249 						locks = lval;
1250 					} else {
1251 						locks = flag_p[k];
1252 					}
1253 					sprintf(line,"%c%c %c %s\n",
1254 						CTLCHAR,FLAG,'a' + k,locks);
1255 					locks = 0;
1256 					if (lval) {
1257 						ffree(lval);
1258 						tval = lval = 0;
1259 					}
1260 					if (ck_it)
1261 						had_flag[k] = ck_it = 0;
1262 				}
1263 				else if (flag_p[k])
1264 					sprintf(line,"%c%c %c %s\n",
1265 					 CTLCHAR,FLAG,'a'+k,flag_p[k]);
1266 				     else
1267 					sprintf(line,"%c%c %c\n",
1268 					 CTLCHAR,FLAG,'a'+k);
1269 			}
1270 			else
1271 				sprintf(line,"%c%c %c\n",
1272 					CTLCHAR,FLAG,'a'+k);
1273 
1274 			/* flush imbeded newlines from flag value */
1275 			i = 4;
1276 			if (line[i] == ' ')
1277 				for (i++; line[i+1]; i++)
1278 					if (line[i] == '\n')
1279 						line[i] = ' ';
1280 			putline(&gpkt,line);
1281 
1282 			if (had_flag[k] == 2) {	/* flag was taken from file */
1283 				had_flag[k] = 0;
1284 				if (flag_p[k]) {
1285 					ffree(flag_p[k]);
1286 					flag_p[k] = 0;
1287 				}
1288 			}
1289 		}
1290 	}
1291 
1292 	if (HADN) {
1293 		if (HADI || HADB) {
1294 			/*
1295 			If the "encoded" flag was not present, put it in
1296 			with a value of 0; this acts as a place holder,
1297 			so that if we later discover that the file contains
1298 			non-ASCII characters we can flag it as encoded
1299 			by setting the value to 1.
1300 			*/
1301 			Encodeflag_offset = ftell(gpkt.p_xiop);
1302 			sprintf(line,"%c%c %c %d\n",
1303 				CTLCHAR, FLAG, ENCODEFLAG, Encoded);
1304 			putline(&gpkt,line);
1305 
1306 			if (HADI && !HADB && (gpkt.p_flags & PF_V6) &&
1307 			    (had_flag[EXPANDFLAG - 'a'] == 0)) {
1308 				Flagdummy_offset = ftell(gpkt.p_xiop);
1309 				sprintf(line,"%c%c   \n",
1310 					CTLCHAR, NAMEDFLAG);
1311 				putline(&gpkt,line);
1312 			}
1313 		}
1314 		/*
1315 		 * Writing out SCCS v6 flags belongs here.
1316 		 */
1317 
1318 		/*
1319 		 * Since we are creating a new history file, everything is from
1320 		 * us and every meta data needs to be written.
1321 		 */
1322 		putmeta(&gpkt, M_ALL);
1323 
1324 		/*
1325 		Beginning of descriptive (user) text.
1326 		*/
1327 		sprintf(line,CTLSTR,CTLCHAR,BUSERTXT);
1328 		putline(&gpkt,line);
1329 	} else {
1330 		/*
1331 		 * Check where the above loop that processes flags stopped:
1332 		 */
1333 		gpkt.p_wrttn = 0;
1334 		/*
1335 		 * Copy over SCCS v6 flags.
1336 		 */
1337 		while (gpkt.p_line_length > 1 &&
1338 			    gpkt.p_line[0] == CTLCHAR &&
1339 			    gpkt.p_line[1] == NAMEDFLAG) {
1340 			if (gpkt.p_line[1] == NAMEDFLAG) {
1341 				q = (signed char *)&gpkt.p_line[2];
1342 				NONBLANK(q);
1343 				if (*q == '\n') {
1344 					/*
1345 					 * Skip dummy flag, it is only needed
1346 					 * as a placeholder by "admin -i...".
1347 					 */
1348 					gpkt.p_wrttn = 1;
1349 					getline(&gpkt);
1350 					continue;
1351 				}
1352 			}
1353 			getline(&gpkt);
1354 
1355 		}
1356 
1357 		/*
1358 		 * Copy over SCCS v6 global metadata.
1359 		 */
1360 		while (gpkt.p_line_length > 1 &&
1361 			    gpkt.p_line[0] == CTLCHAR &&
1362 			    gpkt.p_line[1] == GLOBALEXTENS) {
1363 			getline(&gpkt);
1364 		}
1365 
1366 		/*
1367 		 * Write out everything until the BUSERTXT record.
1368 		 * This includes possible future extensions.
1369 		 */
1370 		if (gpkt.p_line[0] == CTLCHAR && gpkt.p_line[1] == BUSERTXT)
1371 			putline(&gpkt,(char *) 0);
1372 		else
1373 			flushto(&gpkt, BUSERTXT, FLUSH_COPY);
1374 	}
1375 
1376 	/*
1377 	Get user description, copy to x-file.
1378 	*/
1379 	if (HADT) {
1380 		if (tfile) {
1381 		   if (*tfile) {
1382 			iptr = xfopen(tfile, O_RDONLY|O_BINARY);
1383 #ifdef	USE_SETVBUF
1384 			setvbuf(iptr, NULL, _IOFBF, VBUF_SIZE);
1385 #endif
1386 			(void)fgetchk(iptr, tfile, &gpkt, 0);
1387 			fclose(iptr);
1388 			iptr = NULL;
1389 			/*
1390 			 * fgetchk() did set p_ghash and in case that the
1391 			 * file has zero size or -n is used, p_ghash may never
1392 			 * be set up again. So we need to clear it here.
1393 			 */
1394 			gpkt.p_ghash = 0;
1395 		   }
1396 		}
1397 
1398 		/*
1399 		If old file, ignore any previously supplied
1400 		commentary. (i.e., don't copy it to x-file.)
1401 		*/
1402 		if (!HADN)
1403 			flushto(&gpkt, EUSERTXT, FLUSH_NOCOPY);
1404 	}
1405 
1406 	if (HADN) {		/*   N E W  F I L E   */
1407 
1408 		/*
1409 		End of user description.
1410 		*/
1411 		sprintf(line,CTLSTR,CTLCHAR,EUSERTXT);
1412 		putline(&gpkt,line);
1413 
1414 		/*
1415 		Beginning of body (text) of first delta.
1416 		*/
1417 		sprintf(line,"%c%c %d\n",CTLCHAR,INS,1);
1418 		putline(&gpkt,line);
1419 
1420 		if (HADB)
1421 			Encoded |= EF_UUENCODE;
1422 		if (HADI) {		/* get body */
1423 
1424 			/*
1425 			Set indicator to check lines of body of file for
1426 			keyword definitions.
1427 			If no keywords are found, a warning
1428 			will be produced.
1429 			*/
1430 			check_id = 1;
1431 			/*
1432 			Set indicator that tells whether there
1433 			were any keywords to 'no'.
1434 			*/
1435 			gpkt.p_did_id = 0;
1436 			if (ifile) {
1437 			   if (*ifile) {
1438 				/* from a file */
1439 				from_stdin = 0;
1440 			   } else {
1441 				/* from standard input */
1442 				int    err = 0, cnt;
1443 				char   buf[MAXLINE];
1444 				FILE * out;
1445 				mode_t cur_umask;
1446 
1447 				from_stdin = 1;
1448 				ifile	   = stdin_file_buf;
1449 				strlcpy(stdin_file_buf, "/tmp/admin.XXXXXX", sizeof (stdin_file_buf));
1450 				cur_umask = umask((mode_t)((S_IRWXU|S_IRWXG|S_IRWXO)&~(S_IRUSR|S_IWUSR)));
1451 #ifdef	HAVE_MKSTEMP
1452 				if ((out = fdopen(mkstemp(ifile), "wb")) == NULL) {
1453 					xmsg(ifile, NOGETTEXT("admin"));
1454 				}
1455 #else
1456 				mktemp(stdin_file_buf);
1457 				if ((out = fopen(ifile, "wb")) == NULL) {
1458 					xmsg(ifile, NOGETTEXT("admin"));
1459 				}
1460 #endif
1461 				setmode(fileno(out), O_BINARY);
1462 				(void)umask(cur_umask);
1463 				/*CONSTCOND*/
1464 				while (1) {
1465 					if ((cnt = fread(buf, 1, sizeof (buf), stdin))) {
1466 						if (fwrite(buf, 1, cnt, out) == cnt) {
1467 							continue;
1468 						}
1469 						err = 1;
1470 						break;
1471 					} else {
1472 						if (!feof(stdin)) {
1473 							err = 1;
1474 						}
1475 						break;
1476 					}
1477 				}
1478 				if (err) {
1479 					unlink(ifile);
1480 					xmsg(ifile, NOGETTEXT("admin"));
1481 				}
1482 				fclose(out);
1483 			   }
1484 			   iptr = xfopen(ifile, O_RDONLY|O_BINARY);
1485 #ifdef	USE_SETVBUF
1486 			   setvbuf(iptr, NULL, _IOFBF, VBUF_SIZE);
1487 #endif
1488 			}
1489 
1490 			/* save an offset to x-file in case need to encode
1491                            file.  Then won't have to start all over.  Also
1492                            save the hash value up to this point.
1493 			 */
1494 			offset = ftell(gpkt.p_xiop);
1495 			thash = gpkt.p_nhash;
1496 
1497 			/*
1498 			If we haven't already been told that the file
1499 			should be encoded, read and copy to x-file,
1500 			while checking for control characters (octal 1),
1501 			and also check if file ends in newline.  If
1502 			control char or no newline, the file needs to
1503 			be encoded.
1504 			Also, count lines read, and set statistics'
1505 			structure appropriately.
1506 			The 'fgetchk' routine will check for keywords.
1507 			*/
1508 			if (!HADB) {
1509 			   stats.s_ins = fgetchk(iptr, ifile, &gpkt, 1);
1510 			   if (stats.s_ins == -1 ) {
1511 				Encoded |= EF_UUENCODE;
1512 			   } else {
1513 				Encoded &= ~EF_UUENCODE;	/* Keep EF_GZIP */
1514 			   }
1515 			} else {
1516 				Encoded |= EF_UUENCODE;
1517 			}
1518 			if (Encoded & EF_UUENCODE) {
1519 			   /* non-ascii characters in file, encode them */
1520 			   iptr = code(iptr, afile, offset, thash, &gpkt);
1521 			   stats.s_ins = fgetchk(iptr, ifile, &gpkt, 0);
1522 			}
1523 			if (iptr) {
1524 				fclose(iptr);
1525 				iptr = NULL;
1526 			}
1527 			stats.s_del = stats.s_unc = 0;
1528 
1529 			/*
1530 			 * If no keywords were found, issue warning unless in
1531 			 * NSE mode...or warnings have been disabled via an
1532 			 * empty 'y' flag value.
1533 			 */
1534 			if (!gpkt.p_did_id && !HADQ &&
1535 			    (!Flagdummy_offset || !(gpkt.p_props & CK_NULL)) &&
1536 			    (!flag_p[EXPANDFLAG - 'a'] ||
1537 			    *(flag_p[EXPANDFLAG - 'a']))) {
1538 				if (had_flag[IDFLAG - 'a']) {
1539 					if(!(flag_p[IDFLAG -'a']))
1540 						fatal(gettext("no id keywords (cm6)"));
1541 					else
1542 						fatal(gettext("invalid id keywords (cm10)"));
1543 				} else {
1544 					fprintf(stderr, gettext("No id keywords (cm7)\n"));
1545 					(void) sccsfatalhelp("(cm7)");
1546 				}
1547 			}
1548 
1549 			check_id = 0;
1550 			gpkt.p_did_id = 0;
1551 		}
1552 
1553 		/*
1554 		End of body of first delta.
1555 		*/
1556 		sprintf(line,"%c%c %d\n",CTLCHAR,END,1);
1557 		putline(&gpkt,line);
1558 		if (gpkt.p_flags & PF_V6 && gpkt.p_ghash != 0) {
1559 			fseek(gpkt.p_xiop, Checksum_offset, SEEK_SET);
1560 			fprintf(gpkt.p_xiop, "%c%c s %5.5d\n",
1561 				CTLCHAR, SIDEXTENS, gpkt.p_ghash);
1562 			gpkt.p_nhash -= 5 * '0';
1563 			sprintf(line, "%5.5d", gpkt.p_ghash);
1564 			q = (signed char *) line;
1565 			while (*q)
1566 				gpkt.p_nhash += *q++;
1567 		}
1568 	} else {
1569 		/*
1570 		Indicate that EOF at this point is ok, and
1571 		flush rest of (old) s-file to x-file.
1572 		*/
1573 		gpkt.p_chkeof = 1;
1574 		while (getline(&gpkt)) ;
1575 	}
1576 
1577 	/* If encoded file, put change "fe" flag and recalculate
1578 	   the hash value
1579 	 */
1580 
1581 	if (Encoded & EF_UUENCODE)
1582 	{
1583 		strcpy(line,"0");
1584 		q = (signed char *) line;
1585 		while (*q)
1586 			gpkt.p_nhash -= *q++;
1587 		strcpy(line,"1");
1588 		q = (signed char *) line;
1589 		while (*q)
1590 			gpkt.p_nhash += *q++;
1591 		fseek(gpkt.p_xiop, Encodeflag_offset, SEEK_SET);
1592 		fprintf(gpkt.p_xiop,"%c%c %c %d\n",
1593 			CTLCHAR, FLAG, ENCODEFLAG, Encoded);
1594 	} else if (Flagdummy_offset && (gpkt.p_props & CK_NULL)) {
1595 		strcpy(line,"F   ");
1596 		q = (signed char *) line;
1597 		while (*q)
1598 			gpkt.p_nhash -= *q++;
1599 		strcpy(line,"f y ");
1600 		q = (signed char *) line;
1601 		while (*q)
1602 			gpkt.p_nhash += *q++;
1603 		fseek(gpkt.p_xiop, Flagdummy_offset, SEEK_SET);
1604 		fprintf(gpkt.p_xiop,"%c%c %c \n",
1605 			CTLCHAR, FLAG, EXPANDFLAG);
1606 	}
1607 
1608 	/*
1609 	Flush the buffer, take care of rewinding to insert
1610 	checksum and statistics in file, and close.
1611 	*/
1612 	flushline(&gpkt,&stats);
1613 
1614 	/*
1615 	Change x-file name to s-file, and delete old file.
1616 	Unlock file before returning.
1617 	*/
1618 	if (!HADH) {
1619 		if (!HADN) stat(gpkt.p_file,&sbuf);
1620 		rename(auxf(gpkt.p_file,'x'), gpkt.p_file);
1621 		if (!HADN) {
1622 			chmod(gpkt.p_file, sbuf.st_mode);
1623 			chown(gpkt.p_file,sbuf.st_uid, sbuf.st_gid);
1624 		}
1625 		if (HADO) {
1626 			struct timespec	ts[2];
1627 			extern dtime_t	Timenow;
1628 
1629 			ts[0].tv_sec = Timenow.dt_sec;
1630 			ts[0].tv_nsec = Timenow.dt_nsec;
1631 			ts[1].tv_sec = ifile_mtime.tv_sec;
1632 			ts[1].tv_nsec = ifile_mtime.tv_nsec;
1633 
1634 			/*
1635 			 * As SunPro make and gmake call sccs get when the time
1636 			 * if s.file equals the time stamp of the g-file, make
1637 			 * sure the s.file is a bit older.
1638 			 */
1639 			if (!(gpkt.p_flags & PF_V6)) {
1640 				struct timespec	tn;
1641 
1642 				getnstimeofday(&tn);
1643 				ts[1].tv_nsec = tn.tv_nsec;
1644 			}
1645 			if (ts[1].tv_nsec > 500000000)
1646 				ts[1].tv_nsec -= 500000000;
1647 
1648 			utimensat(AT_FDCWD, gpkt.p_file, ts, 0);
1649 		}
1650 		if (HADI && *ifile) {
1651 			if (N.n_comma) {
1652 				char cfile[FILESIZE];
1653 				register char *snp;
1654 
1655 				snp = sname(ifile);
1656 
1657 				strlcpy(cfile, ifile, sizeof (cfile));
1658 				cfile[snp-ifile] = '\0';
1659 				strlcat(cfile, ",", sizeof (cfile));
1660 				if (strlcat(cfile, snp, sizeof (cfile)) <
1661 				    sizeof (cfile)) {
1662 					rename(ifile, cfile);
1663 				}
1664 			} else if(N.n_unlink) {
1665 				unlink(ifile);
1666 			} else if (N.n_get) {
1667 				unlink(ifile);
1668 				doget(gpkt.p_file, ifile, 1);
1669 				if (HADO)
1670 					dogtime(&gpkt, ifile, &ifile_mtime);
1671 			}
1672 		}
1673 		xrm(&gpkt);
1674 		timersetlockfile(NULL);
1675 		if (!islockchset(Zhold))
1676 			unlockit(Zhold, getpid(), uuname);
1677 
1678 		if ((gpkt.p_flags & PF_V6) && Cs && gpkt.p_init_path) {
1679 			char	cbuf[2*MAXPATHLEN];
1680 
1681 			change_ba(&gpkt, cbuf, sizeof (cbuf));
1682 			fprintf(Cs, "%s\n", cbuf);
1683 		}
1684 	}
1685 
1686 	if (HADI)
1687 		unlink(auxf(gpkt.p_file,'e'));
1688 	if (from_stdin) {
1689 		unlink(stdin_file_buf);
1690 		stdin_file_buf[0] = '\0';
1691 	}
1692 	if (gpkt.p_init_path) {
1693 		if (gpkt.p_init_path != X.x_init_path)
1694 			ffree(gpkt.p_init_path);
1695 		gpkt.p_init_path = NULL;
1696 	}
1697 	sclose(&gpkt);
1698 	sfree(&gpkt);
1699 	ffreeall();
1700 }
1701 
1702 static int
fgetchk(inptr,file,pkt,fflag)1703 fgetchk(inptr, file, pkt, fflag)
1704 	FILE	*inptr;		/* File pointer to read from	*/
1705 	char	*file;		/* File name to read from	*/
1706 struct	packet	*pkt;		/* struct paket for output	*/
1707 	int	fflag;		/* 0 = abort, 1 == flag		*/
1708 {
1709 	off_t	nline;
1710 	int	idx = 0;
1711 	int	warned = 0;
1712 	int	nwarned = 0;
1713 	char	chkflags = 0;
1714 	char	lastchar;
1715 #ifndef	RECORD_IO
1716 	char	*p = NULL;	/* Intialize to make gcc quiet */
1717 	char	*pn =  NULL;	/* Pointer to nul character */
1718 	char	line[VBUF_SIZE+1];
1719 	char	*lastline = line; /* Init to make GCC quiet */
1720 #else
1721 	int	search_on = 0;
1722 	int	llen;
1723 	char	line[256+1];	/* Avoid a too long buffer for speed */
1724 #endif
1725 	off_t	ibase = 0;	/* Ifile off from last read operation	*/
1726 	off_t	ioff = 0;	/* Ifile offset past last newline	*/
1727 	off_t	soff = ftell(pkt->p_xiop); /* Ofile (s. file) base offset */
1728 	off_t	coff = 0;	/* Offset from additional ^A escapes	*/
1729 	unsigned int sum = 0;
1730 	int	pktv6 = pkt->p_flags & PF_V6;
1731 
1732 	/*
1733 	 * This gives the illusion that a zero-length file ends
1734 	 * in a newline so that it won't be mistaken for a
1735 	 * binary file.
1736 	 */
1737 	lastchar = '\n';
1738 
1739 	nline = 0;
1740 	(void)memset(line, '\377', sizeof (line));
1741 #ifndef	RECORD_IO
1742 	/*
1743 	 * In most cases (non record oriented I/O), we can optimize the way we
1744 	 * scan files for '\0' bytes, line-ends '\n' and ^A '\1'. The optimized
1745 	 * algorithm allows to avoid to do a reverse scan for '\0' from the end
1746 	 * of the buffer.
1747 	 */
1748 	while ((idx = fread(line, 1, sizeof (line) - 1, inptr)) > 0) {
1749 		sum += usum(line, idx);
1750 		lastline = line;
1751 		if (lastchar == '\n' && line[0] == CTLCHAR) {
1752 			chkflags |= CK_CTLCHAR;
1753 			if (fflag && pktv6) {
1754 				if (!warned) {
1755 					warnctl(file, nline+1);
1756 					warned = 1;
1757 				}
1758 				putctl(pkt);
1759 				coff++;
1760 			} else {
1761 				goto err;
1762 			}
1763 		}
1764 		lastchar = line[idx-1];
1765 		p = findbytes(line, idx, '\0');
1766 		if (p != NULL) {
1767 			chkflags |= CK_NULL;
1768 			pn = p;
1769 		}
1770 		for (p = line;
1771 		    (p = findbytes(p, idx - (p-line), '\n')) != NULL; p++) {
1772 			ioff = ibase + (p - line) + 1;
1773 			if (pn && p > pn) {		/* '\0' before '\n' */
1774 				if (pktv6) {
1775 					if (!nwarned) {
1776 						warnnull(file, nline+1);
1777 						nwarned = 1;
1778 					}
1779 				} else {
1780 					goto err;
1781 				}
1782 			}
1783 			nline++;
1784 			if ((p - line) >= (idx-1))	/* '\n' last in buf */
1785 				break;
1786 
1787 			if (p[1] == CTLCHAR) {
1788 				chkflags |= CK_CTLCHAR;
1789 				if (fflag && pktv6 &&
1790 				    (p[1] == CTLCHAR)) {
1791 					if (!warned) {
1792 						warnctl(file, nline+1);
1793 						warned = 1;
1794 					}
1795 					p[1] = '\0';
1796 					putlline(pkt, lastline, &p[1] - lastline);
1797 					p[1] = CTLCHAR;
1798 					lastline = &p[1];
1799 					putctl(pkt);
1800 					coff++;
1801 					continue;
1802 				}
1803 	err:
1804 				if (fflag) {
1805 					return(-1);
1806 				} else {
1807 					sprintf(SccsError,
1808 					gettext(
1809 			  "file '%s' contains illegal data on line %jd (ad21)"),
1810 					file, (Intmax_t)++nline);
1811 					fatal(SccsError);
1812 				}
1813 			}
1814 		}
1815 		line[idx] = '\0';
1816 		putlline(pkt, lastline, &line[idx] - lastline);
1817 
1818 		if (check_id && pkt->p_did_id == 0) {
1819 			pkt->p_did_id =
1820 				chkid(line, flag_p[IDFLAG - 'a'], flag_p);
1821 		}
1822 		ibase += idx;
1823 	}
1824 #else	/* !RECORD_IO */
1825 	/*
1826 	 * We support nul bytes with fgets() by pre-filling the buffer.
1827 	 */
1828 	while (fgets(line, sizeof (line), inptr) != NULL) {
1829 	   if (lastchar == '\n' && line[0] == CTLCHAR) {
1830 		chkflags |= CK_CTLCHAR;
1831 		if (fflag && pktv6) {
1832 			if (!warned) {
1833 				warnctl(file, nline+1);
1834 				warned = 1;
1835 			}
1836 			putctl(pkt);
1837 			coff++;
1838 		} else {
1839 			nline++;
1840 			goto err;
1841 		}
1842 	   }
1843 	   search_on = 0;
1844 	   for (idx = sizeof (line)-1; idx >= 0; idx--) {
1845 	      if (search_on > 0) {
1846 		 if (line[idx] == '\0') {
1847 		    chkflags |= CK_NULL;
1848 	err:
1849 		    if (fflag) {
1850 			if (pktv6) {
1851 				if ((chkflags & CK_NULL) && !nwarned) {
1852 					warnnull(file, nline);
1853 					nwarned = 1;
1854 				}
1855 				continue;
1856 			}
1857 		       return(-1);
1858 		    } else {
1859 		       sprintf(SccsError,
1860 			 gettext("file '%s' contains illegal data on line %jd (ad21)"),
1861 			 file, (Intmax_t)nline);
1862 		       fatal(SccsError);
1863 		    }
1864 		 }
1865 	      } else {
1866 		 if (line[idx] == '\0') {
1867 		    sum += usum(line, idx);
1868 		    search_on = 1;
1869 		    lastchar = line[idx-1];
1870 		    if (lastchar == '\n') {
1871 		       nline++;
1872 		       ioff = ibase + idx;
1873 
1874 		    }
1875 		    ibase += idx;
1876 		    llen = idx;
1877 		 }
1878 	      }
1879 	   }
1880 	   if (check_id && pkt->p_did_id == 0) {
1881 		pkt->p_did_id =
1882 			chkid(line, flag_p[IDFLAG - 'a'], flag_p);
1883 	   }
1884 	   putlline(pkt, line, llen);
1885 	   (void)memset(line, '\377', sizeof (line));
1886 	}
1887 #endif	/* !RECORD_IO */
1888 	pkt->p_ghash = sum & 0xFFFF;
1889 	if (lastchar != '\n')
1890 		chkflags |= CK_NONL;
1891 	pkt->p_props |= chkflags;
1892 
1893 	if (chkflags & CK_NONL) {
1894 #ifndef	RECORD_IO
1895 		if (!pktv6)
1896 		if (pn && nline == 0)		/* Found null byte but no newline past null */
1897 			goto err;
1898 #endif
1899 		if (fflag && pktv6) {
1900 			int	first = 1;
1901 
1902 			fseek(inptr, ioff, SEEK_SET);
1903 			fseek(pkt->p_xiop, ioff + soff + coff, SEEK_SET);
1904 
1905 			/*
1906 			 * Write again already written text without recomputing
1907 			 * the checksum for this part of the text.
1908 			 */
1909 			while ((idx =
1910 			    fread(line, 1, sizeof (line) - 1, inptr)) > 0) {
1911 				if (first) {
1912 					first = 0;
1913 					if (line[0] == CTLCHAR) {
1914 						/*
1915 						 * ^A escape is already present
1916 						 */
1917 						putchr(pkt, NONL);
1918 					} else {
1919 						putctlnnl(pkt);
1920 					}
1921 				}
1922 				if (fwrite(line, 1, idx, pkt->p_xiop) <= 0)
1923 					FAILPUT;
1924 			}
1925 			putchr(pkt, '\n');
1926 			fprintf(stderr, gettext(
1927 			    "WARNING [%s%s%s]: No newline at end of file (ad31)\n"),
1928 				dir_name,
1929 				*dir_name?"/":"",
1930 				file);
1931 			return (nline);
1932 		}
1933 
1934 	   if (fflag) {
1935 	      return(-1);
1936 	   } else {
1937 	      sprintf(SccsError,
1938 		gettext("No newline at end of file '%s' (ad31)"),
1939 		file);
1940 	      fatal(SccsError);
1941 	   }
1942 	}
1943 	return(nline);
1944 }
1945 
1946 static void
warnctl(file,nline)1947 warnctl(file, nline)
1948 	char	*file;
1949 	off_t	nline;
1950 {
1951 	fprintf(stderr,
1952 		gettext(
1953 		"WARNING [%s%s%s]: line %jd begins with ^A\n"),
1954 		dir_name,
1955 		*dir_name?"/":"",
1956 		file, (Intmax_t)nline);
1957 }
1958 
1959 static void
warnnull(file,nline)1960 warnnull(file, nline)
1961 	char	*file;
1962 	off_t	nline;
1963 {
1964 	fprintf(stderr,
1965 		gettext(
1966 		"WARNING [%s]: line %jd contains a '\\000'\n"),
1967 		file, (Intmax_t)nline);
1968 }
1969 
1970 
1971 static void
clean_up()1972 clean_up()
1973 {
1974 	xrm(&gpkt);
1975 	if(gpkt.p_file[0]) {
1976 		unlink(auxf(gpkt.p_file,'x'));
1977 		if (HADI)
1978 			unlink(auxf(gpkt.p_file,'e'));
1979 		if (HADN)
1980 			unlink(gpkt.p_file);
1981 	}
1982 	if (gpkt.p_init_path) {
1983 		if (gpkt.p_init_path != X.x_init_path)
1984 			ffree(gpkt.p_init_path);
1985 		gpkt.p_init_path = NULL;
1986 	}
1987 	if (!HADH) {
1988 		uname(&un);
1989 		uuname = un.nodename;
1990 		timersetlockfile(NULL);
1991 		if (!islockchset(Zhold))
1992 			unlockit(Zhold, getpid(), uuname);
1993 	}
1994 	sclose(&gpkt);
1995 	sfree(&gpkt);
1996 	ffreeall();
1997 }
1998 
1999 static void
cmt_ba(dt,str,flags)2000 cmt_ba(dt,str, flags)
2001 register struct deltab *dt;
2002 char *str;
2003 int flags;
2004 {
2005 	register char *p;
2006 
2007 	p = str;
2008 	*p++ = CTLCHAR;
2009 	*p++ = COMMENTS;
2010 	*p++ = ' ';
2011 	copy(NOGETTEXT("date and time created"),p);
2012 	while (*p++)
2013 		;
2014 	--p;
2015 	*p++ = ' ';
2016 	/*
2017 	 * The s-file is not part of the POSIX standard. For this reason, we
2018 	 * are free to switch to a 4-digit year for the initial comment.
2019 	 */
2020 	if ((flags & PF_V6) ||
2021 	    (dt->d_dtime.dt_sec < Y1969) ||
2022 	    (dt->d_dtime.dt_sec >= Y2038))		/* comment only */
2023 		date_bazl(&dt->d_dtime,p, flags);	/* 4 digit year */
2024 	else
2025 		date_ba(&dt->d_dtime.dt_sec,p, flags);	/* 2 digit year */
2026 	while (*p++)
2027 		;
2028 	--p;
2029 	*p++ = ' ';
2030 	copy(NOGETTEXT("by"),p);
2031 	while (*p++)
2032 		;
2033 	--p;
2034 	*p++ = ' ';
2035 	copy(dt->d_pgmr,p);
2036 	while (*p++)
2037 		;
2038 	--p;
2039 	*p++ = '\n';
2040 	*p = 0;
2041 }
2042 
2043 static void
putmrs(pkt)2044 putmrs(pkt)
2045 struct packet *pkt;
2046 {
2047 	register char **argv;
2048 	char str[64];
2049 	extern char **Varg;
2050 
2051 	for (argv = &Varg[VSTART]; *argv; argv++) {
2052 		sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv);
2053 		putline(pkt,str);
2054 	}
2055 }
2056 
2057 
2058 static char*
adjust(line)2059 adjust(line)
2060 char	*line;
2061 {
2062 	register int k;
2063 	register int i;
2064 	char	*t_unlock;
2065 	char	t_line[MAXLINE];
2066 	char	rel[5];
2067 
2068 	t_unlock = unlockarg;
2069 	while(*t_unlock) {
2070 		NONBLANK(t_unlock);
2071 		t_unlock = getval(t_unlock,rel);
2072 		while ((k = pos_ser(line,rel)) != -1) {
2073 			for(i = k; i < ((int) size(rel) + k); i++) {
2074 				line[i] = '+';
2075 				if (line[i++] == ' ')
2076 					line[i] = '+';
2077 				else if (line[i] == '\0')
2078 					break;
2079 				else --i;
2080 			}
2081 			k = 0;
2082 			for(i = 0; i < (int) length(line); i++) {
2083 				if (line[i] == '+')
2084 					continue;
2085 				else if (k == 0 && line[i] == ' ')
2086 					continue;
2087 				else t_line[k++] = line[i];
2088 			}
2089 			t_line[k] = '\0';
2090 			if (k > 0 &&
2091 			    t_line[(int) strlen(t_line) - 1] == ' ')
2092 				t_line[(int) strlen(t_line) - 1] = '\0';
2093 			line = t_line;
2094 		}
2095 	}
2096 	if (length(line) == 0)
2097 		return (0);
2098 	if (line == t_line) {
2099 		t_unlock = fmalloc(size(line));
2100 		copy(line, t_unlock);
2101 		return (t_unlock);
2102 	}
2103 	return (line);
2104 }
2105 
2106 static char*
getval(sourcep,destp)2107 getval(sourcep,destp)
2108 register char	*sourcep;
2109 register char	*destp;
2110 {
2111 	while (*sourcep != ' ' && *sourcep != '\t' && *sourcep != '\0')
2112 		*destp++ = *sourcep++;
2113 	*destp = 0;
2114 	return(sourcep);
2115 }
2116 
2117 static int
val_list(list)2118 val_list(list)
2119 register char *list;
2120 {
2121 	register int i;
2122 
2123 	if (list[0] == 'a')
2124 		return(1);
2125 	else for(i = 0; list[i] != '\0'; i++)
2126 		if (list[i] == ' ' || numeric(list[i]))
2127 			continue;
2128 		else if (list[i] == 'a') {
2129 			list[0] = 'a';
2130 			list[1] = '\0';
2131 			return(1);
2132 		}
2133 		else return(0);
2134 	return(1);
2135 }
2136 
2137 static int
pos_ser(s1,s2)2138 pos_ser(s1,s2)
2139 char	*s1;
2140 char	*s2;
2141 {
2142 	register int offset;
2143 	register char *p;
2144 	char	num[5];
2145 
2146 	p = s1;
2147 	offset = 0;
2148 
2149 	while(*p) {
2150 		NONBLANK(p);
2151 		p = getval(p,num);
2152 		if (equal(num,s2)) {
2153 			return(offset);
2154 		}
2155 		offset = offset + (int) size(num);
2156 	}
2157 	return(-1);
2158 }
2159 
2160 static int
range(line)2161 range(line)
2162 register char *line;
2163 {
2164 	register char *p;
2165 	char	rel[MAXLINE];
2166 
2167 	p = line;
2168 	while(*p) {
2169 		NONBLANK(p);
2170 		p = getval(p,rel);
2171 		if ((int) size(rel) > 5)
2172 			return(0);
2173 	}
2174 	return(1);
2175 }
2176 
2177 static FILE *
code(iptr,afile,offset,thash,pktp)2178 code(iptr,afile,offset,thash,pktp)
2179 FILE *iptr;
2180 char *afile;
2181 off_t offset;
2182 int thash;
2183 struct packet *pktp;
2184 {
2185 	FILE *eptr;
2186 
2187 
2188 	/* issue a warning that file is non-text */
2189 	if (!HADB) {
2190 		fprintf(stderr, gettext("WARNING [%s%s%s]: %s"),
2191 			dir_name,
2192 			*dir_name?"/":"",
2193 			ifile,
2194 			gettext("Not a text file (ad32)\n"));
2195 			(void) sccsfatalhelp("(ad32)");
2196 	}
2197 	rewind(iptr);
2198 	eptr = fopen(auxf(afile,'e'), "wb");
2199 
2200 	encode(iptr,eptr);
2201 	fclose(eptr);
2202 	fclose(iptr);
2203 	iptr = fopen(auxf(afile,'e'), "rb");
2204 	/* close the stream to xfile and reopen it at offset.  Offset is
2205 	 * the end of sccs header info and before gfile contents
2206 	 */
2207 	putline(pktp,0);
2208 	fseek(pktp->p_xiop, offset, SEEK_SET);
2209 	pktp->p_nhash = thash;
2210 
2211 	return (iptr);
2212 }
2213 
2214 static void
direrror(dir,keylet)2215 direrror(dir, keylet)
2216 	char	*dir;
2217 	int	keylet;
2218 {
2219 	sprintf(SccsError,
2220 	gettext("directory `%s' specified as `%c' keyletter value (ad29)"),
2221 		dir, (char)keylet);
2222 	fatal(SccsError);
2223 }
2224