1 /* @(#)sccscvt.c	1.34 20/08/23 Copyright 2011-2020 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)sccscvt.c	1.34 20/08/23 Copyright 2011-2020 J. Schilling";
6 #endif
7 /*
8  *	Convert a SCCS v4 history file to a SCCS v6 file and vice versa.
9  *	When converting from v6 to v4, the extra meta data from the delta table
10  *	is kept in special degenerated comment at the beginning of the comment
11  *	block.
12  *
13  *	Copyright (c) 2011-2020 J. Schilling
14  */
15 /*
16  * The contents of this file are subject to the terms of the
17  * Common Development and Distribution License, Version 1.0 only
18  * (the "License").  You may not use this file except in compliance
19  * with the License.
20  *
21  * See the file CDDL.Schily.txt in this distribution for details.
22  *
23  * When distributing Covered Code, include this CDDL HEADER in each
24  * file and include the License file CDDL.Schily.txt from this distribution.
25  */
26 
27 #define	SCCS_MAIN		/* define global vars */
28 #include <defines.h>
29 #include <version.h>
30 #include <had.h>		/* Do we need this? We don't use getopt() */
31 #include <i18n.h>
32 #include <schily/getargs.h>
33 #include <schily/utsname.h>
34 
35 LOCAL	void	usage		__PR((int exitcode));
36 EXPORT	int	main		__PR((int ac, char *av[]));
37 LOCAL	void	dodir		__PR((char *name));
38 LOCAL	void	convert		__PR((char *file));
39 LOCAL	void	cvtdelt2v4	__PR((struct packet *pkt));
40 LOCAL	void	cvtdelt2v6	__PR((struct packet *pkt));
41 LOCAL	void	get_setup	__PR((char *file));
42 LOCAL	int	get_hash	__PR((int ser));
43 LOCAL	void	clean_up	__PR((void));
44 LOCAL	int	getN		__PR((const char *, void *));
45 LOCAL	int	getX		__PR((const char *, void *));
46 
47 LOCAL	struct utsname	un;
48 LOCAL	char		*uuname;
49 LOCAL	struct packet	gpkt;
50 LOCAL	char		Zhold[MAXPATHLEN];	/* temporary z-file name	*/
51 LOCAL	Nparms		N;			/* Keep -N parameters		*/
52 LOCAL	Xparms		X;			/* Keep -X parameters		*/
53 
54 LOCAL	BOOL	dov6	= -1;
55 LOCAL	BOOL	keepold;
56 LOCAL	BOOL	discardv6;
57 LOCAL	int	olddate;
58 
59 LOCAL void
usage(exitcode)60 usage(exitcode)
61 	int	exitcode;
62 {
63 	fprintf(stderr, _("Usage: sccscvt [options] s.file1 .. s.filen\n"));
64 	fprintf(stderr, _("	-help	Print this help.\n"));
65 	fprintf(stderr, _("	-version Print version number.\n"));
66 	fprintf(stderr, _("	-V4	Convert history files to SCCS v4.\n"));
67 	fprintf(stderr, _("	-V6	Convert history files to SCCS v6.\n"));
68 	fprintf(stderr, _("	-d	Discard SCCS v6 meta data.\n"));
69 	fprintf(stderr, _("	-keep,-k Keep original history file as o.file.\n"));
70 	fprintf(stderr, _("	-o	Keep original time stamp.\n"));
71 	fprintf(stderr, _("	-oo	Use original time stamp + 1ns.\n"));
72 	fprintf(stderr, _("	-ooo	Use original time stamp + 1us.\n"));
73 	fprintf(stderr, _("	-oooo	Use original time stamp + 1s.\n"));
74 	fprintf(stderr, _("	-Nbulk-spec Processes a bulk of SCCS history files.\n"));
75 	fprintf(stderr, _("	-Xxopts	Processes SCCS extended files.\n"));
76 	exit(exitcode);
77 }
78 
79 EXPORT int
main(ac,av)80 main(ac, av)
81 	int	ac;
82 	char	*av[];
83 {
84 	int	cac;
85 	char	* const *cav;
86 	char	*opts = "help,V,version,V4%0,V6,d,k,keep,o+,N&_,X&_";
87 	BOOL	help = FALSE;
88 	BOOL	pversion = FALSE;
89 	int	nargs = 0;
90 
91 	save_args(ac, av);
92 
93 	/*
94 	 * Set locale for all categories.
95 	 */
96 	setlocale(LC_ALL, "");
97 
98 	sccs_setinsbase(INS_BASE);
99 
100 	/*
101 	 * Set directory to search for general l10n SCCS messages.
102 	 */
103 #ifdef	PROTOTYPES
104 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
105 	    NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
106 #else
107 	(void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
108 	    NOGETTEXT("/usr/ccs/lib/locale/"));
109 #endif
110 
111 	(void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
112 
113 	tzset();	/* Set up timezome related vars */
114 
115 	set_clean_up(clean_up);
116 	Fflags = FTLEXIT | FTLMSG | FTLCLN;
117 #ifdef	SCCS_FATALHELP
118 	Fflags |= FTLFUNC;
119 	Ffunc = sccsfatalhelp;
120 #endif
121 
122 	cac = --ac;
123 	cav = ++av;
124 
125 	if (getallargs(&cac, &cav, opts,
126 			&help, &pversion, &pversion,
127 			&dov6, &dov6,
128 			&discardv6,
129 			&keepold, &keepold,
130 			&olddate,
131 			getN, &N,
132 			getX, &X) < 0) {
133 		errmsgno(EX_BAD, _("Bad flag: %s.\n"), cav[0]);
134 		usage(EX_BAD);
135 	}
136 	if (help)
137 		usage(0);
138 	if (pversion) {
139 		printf(
140 	_("sccscvt %s-SCCS version %s %s (%s-%s-%s) Copyright (C) 2011-2020 %s\n"),
141 			PROVIDER,
142 			VERSION,
143 			VDATE,
144 			HOST_CPU, HOST_VENDOR, HOST_OS,
145 			_("J�rg Schilling"));
146 		exit(0);
147 	}
148 	if (dov6 < 0) {
149 		errmsgno(EX_BAD, _("Need to specify -V4 or -V6.\n"));
150 		usage(EX_BAD);
151 	}
152 	if (N.n_parm) {					/* Parse -N args  */
153 		parseN(&N);
154 	}
155 
156 	xsethome(NULL);
157 	if (N.n_parm && N.n_sdot && (sethomestat & SETHOME_OFFTREE))
158 		fatal(gettext("-Ns. not supported in off-tree project mode"));
159 
160 	/*
161 	 * Get the name of our machine to be used for the lockfile.
162 	 */
163 	uname(&un);
164 	uuname = un.nodename;
165 
166 	/*
167 	 * Set up a project global lock on the changeset file.
168 	 * Since we set FTLJMP, we do not need to unlockchset() from clean_up().
169 	 */
170 	if (SETHOME_CHSET())
171 		lockchset(getppid(), getpid(), uuname);
172 	timerchsetlock();
173 
174 	Fflags &= ~FTLEXIT;
175 	Fflags |= FTLJMP;
176 
177 	cac = ac;
178 	cav = av;
179 
180 	while (getfiles(&cac, &cav, opts) > 0) {
181 		struct stat	sb;
182 
183 		if (cav[0][0] == '-' && cav[0][1] == '\0')
184 			do_file("-", convert, 0, N.n_sdot, &X);
185 		else if (stat(cav[0], &sb) >= 0 && S_ISDIR(sb.st_mode))
186 			dodir(cav[0]);
187 		else
188 			convert(cav[0]);
189 		cac--;
190 		cav++;
191 		nargs++;
192 	}
193 
194 	/*
195 	 * Only remove the global lock it it was created by us and not by
196 	 * our parent.
197 	 */
198 	if (SETHOME_CHSET()) {
199 		if (N.n_parm)
200 			bulkchdir(&N);
201 		unlockchset(getpid(), uuname);
202 	}
203 
204 	if (nargs == 0) {
205 		errmsgno(EX_BAD, _("Missing arg.\n"));
206 		usage(EX_BAD);
207 	}
208 	return (0);
209 }
210 
211 LOCAL void
dodir(name)212 dodir(name)
213 	char	*name;
214 {
215 	DIR		*dp;
216 	struct dirent	*d;
217 	char		*np;
218 	char		fname[MAXPATHNAME+1];
219 	char		*base;
220 	int		len;
221 	BOOL		newmode = FALSE;
222 
223 	if (SETHOME_CHSET() && N.n_parm && N.n_sdot == 0) {
224 		newmode = TRUE;
225 		N.n_pflags =  NP_NOCHDIR|NP_DIR;
226 		np = bulkprepare(&N, name);
227 		if (np == NULL)
228 			fatal(gettext(bulkerror(&N)));
229 		N.n_pflags =  0;
230 		dp = opendir(np);
231 	} else {
232 		dp = opendir(name);
233 	}
234 	if (dp == NULL) {
235 		errmsg(_("Cannot open directory '%s'\n"), name);
236 		return;
237 	}
238 
239 	if ((len = resolvenpath(name, fname, sizeof (fname))) == -1) {
240 		N.n_error = BULK_EPATHCONV;
241 		closedir(dp);
242 		efatal(gettext(bulkerror(&N)));
243 		return;
244 	} else if (len >= sizeof (fname)) {
245 		N.n_error = BULK_ETOOLONG;
246 		closedir(dp);
247 		fatal(gettext(bulkerror(&N)));
248 		return;
249 	}
250 	fname[len] = '\0';
251 	if (fname[0] == '.' && len == 1) {
252 		fname[--len] = '\0';
253 		base = fname;
254 	} else {
255 		base = &fname[len-1];
256 		if (*base != '/')
257 			*++base = '/';
258 		*++base = '\0';
259 	}
260 	len = sizeof (fname) - strlen(fname);
261 	while ((d = readdir(dp)) != NULL) {
262 		np = d->d_name;
263 
264 		if (np[0] != 's' || np[1] != '.' || np[2] == '\0')
265 			continue;
266 
267 		if (newmode)
268 			np += 2;
269 		strlcpy(base, np, len);
270 		convert(fname);
271 	}
272 	closedir(dp);
273 }
274 
275 LOCAL void
convert(file)276 convert(file)
277 	char	*file;
278 {
279 	struct stat sbuf;
280 	struct timespec ts[2];
281 	char	hash[32];
282 	char	*xf;
283 	char	*dir_name = "";
284 
285 	/*
286 	 * Set up exception handling for fatal().
287 	 */
288 	if (setjmp(Fjmp))
289 		return;
290 
291 	/*
292 	 * In order to make the global lock with a potentially long duration
293 	 * not look as if it was expired, we refresh it for every file in our
294 	 * task list. This is needed since another SCCS instance on a different
295 	 * NFS machine cannot use kill() to check for a still active process.
296 	 */
297 	if (SETHOME_CHSET()) {
298 		if (N.n_parm)
299 			bulkchdir(&N);	/* Done by bulkprepare() anyway */
300 		refreshchsetlock();
301 	}
302 
303 	if (N.n_parm) {
304 #ifdef	__needed__
305 		char	*ofile = file;
306 #endif
307 
308 		file = bulkprepare(&N, file);
309 		if (file == NULL) {
310 #ifdef	__needed__
311 			if (N.n_ifile)
312 				ofile = N.n_ifile;
313 #endif
314 			/*
315 			 * The error is typically
316 			 * "directory specified as s-file (cm14)"
317 			 */
318 			fatal(gettext(bulkerror(&N)));
319 		}
320 		dir_name = N.n_dir_name;
321 	}
322 
323 	if (!sccsfile(file)) {
324 		errmsgno(EX_BAD, _("%s: not an SCCS file (co1).\n"), file);
325 		return;
326 	}
327 
328 	/*
329 	 * Init and check for validity of file name but do not open the file.
330 	 * This prevents us from potentially damaging files with lockit().
331 	 */
332 	sinit(&gpkt, file, SI_INIT);
333 
334 	/*
335 	 * Obtain a lock on the SCCS history file.
336 	 */
337 	if (!islockchset(copy(auxf(gpkt.p_file, 'z'), Zhold)) &&
338 	    lockit(Zhold, SCCS_LOCK_ATTEMPTS, getpid(), uuname)) {
339 		lockfatal(Zhold, getpid(), uuname);
340 	} else {
341 		timersetlockfile(Zhold);
342 	}
343 
344 	/*
345 	 * Open s. file.
346 	 */
347 	sinit(&gpkt, file, SI_OPEN);
348 	if (dov6) {
349 		if (gpkt.p_flags & PF_V6) {
350 			errmsgno(EX_BAD, _("%s: already in SCCS v6 format.\n"),
351 				file);
352 			clean_up();
353 			sclose(&gpkt);
354 			sfree(&gpkt);
355 			return;
356 		}
357 		gpkt.p_flags |= PF_V6;
358 	} else {
359 		if ((gpkt.p_flags & PF_V6) == 0) {
360 			errmsgno(EX_BAD, _("%s: already in SCCS v4 format.\n"),
361 				file);
362 			clean_up();
363 			sclose(&gpkt);
364 			sfree(&gpkt);
365 			return;
366 		}
367 		gpkt.p_flags &= ~PF_V6;	/* Make sure initial chksum is V4 */
368 	}
369 
370 	/*
371 	 * Write a magic in the expected new format.
372 	 */
373 	gpkt.p_upd = 1;
374 	putmagic(&gpkt, "00000");
375 	gpkt.p_wrttn = 1;
376 
377 	get_setup(file);		/* Read delta table for get_hash() */
378 
379 	/*
380 	 * The main conversion work happens here.
381 	 * Convert the delta table.
382 	 * The conversion stops at BUSERNAM (the beginning of the user names).
383 	 */
384 	if (dov6)
385 		cvtdelt2v6(&gpkt);
386 	else
387 		cvtdelt2v4(&gpkt);
388 
389 	/*
390 	 * The next sections in the history file are:
391 	 *
392 	 * -	usernames	mandatory BUSERNAM .. EUSERNAM
393 	 * -	v4 flags	optional
394 	 * -	v6 flags	optional
395 	 * -	v6 meta data	optional
396 	 * -	future extens.	optional
397 	 * -	comments	mandatory BUSERTXT .. EUSERTXT
398 	 *
399 	 * First copy user names...
400 	 */
401 	flushto(&gpkt, EUSERNAM, FLUSH_COPY);
402 
403 	/*
404 	 * gpkt.p_line now points to EUSERNAM and this line has been written.
405 	 *
406 	 * Now copy SCCS v4 flags in case they are present.
407 	 * Using the flag parser is a bit slower, but warns about unknown flags.
408 	 */
409 	doflags(&gpkt);
410 
411 	/*
412 	 * gpkt.p_line now points to the first line past the SCCS v4 flags and
413 	 * this line has not yet been written.
414 	 *
415 	 * Now copy SCCS v6 flags in case they are present.
416 	 * Using the flag parser is a bit slower, but warns about unknown flags.
417 	 */
418 	donamedflags(&gpkt);
419 
420 	/*
421 	 * gpkt.p_line now points to the first line past the SCCS v6 flags and
422 	 * this line has not yet been written.
423 	 *
424 	 * Now copy SCCS v6 meta data extensions in case they are present.
425 	 * Using the meta data parser is needed since we need to know whether
426 	 * we already have SCCS v6 initial path and SCCS v6 unified random.
427 	 */
428 	dometa(&gpkt);
429 
430 	/*
431 	 * gpkt.p_line now points to the first line past the SCCS v6 meta data
432 	 * and this line has not yet been written.
433 	 *
434 	 * Check whether we need to add SCCS v6 initial path and SCCS v6 urand.
435 	 */
436 	if (dov6) {
437 		unsigned	mflags = 0;
438 
439 		if (gpkt.p_init_path == NULL && N.n_parm) {
440 			set_init_path(&gpkt, N.n_ifile, dir_name);
441 			mflags |= M_INIT_PATH;
442 		}
443 		if (!urand_valid(&gpkt.p_rand)) {
444 			urandom(&gpkt.p_rand);
445 			mflags |= M_URAND;
446 		}
447 		if (mflags) {
448 			putmeta(&gpkt, mflags);
449 		}
450 	}
451 
452 	/*
453 	 * gpkt.p_line still points to the first line past the SCCS v6 meta data
454 	 * and this line has still not yet been written.
455 	 *
456 	 * Copy user names, extensions and descriptive user text.
457 	 * Before doing that, the unwritten line is flushed.
458 	 */
459 	flushto(&gpkt, EUSERTXT, FLUSH_COPY);
460 
461 	/*
462 	 * Copy interleaved delta block.
463 	 */
464 	gpkt.p_chkeof = 1;		/* set EOF is ok */
465 	while (getline(&gpkt))
466 		;
467 	putline(&gpkt, (char *)0);	/* flush final line */
468 
469 	/*
470 	 * Write checksum.
471 	 */
472 	if (!dov6)
473 		gpkt.p_flags &= ~PF_V6;
474 	sprintf(hash, "%5.5d", gpkt.p_nhash&0xFFFF);
475 	putmagic(&gpkt, hash);
476 
477 	/*
478 	 * Make sure the data is stable in the file on disk.
479 	 */
480 	xf = auxf(gpkt.p_file, 'x');
481 	if (fflush(gpkt.p_xiop) == EOF)
482 		xmsg(xf, NOGETTEXT("convert"));
483 
484 	/*
485 	 * Lots of paranoia here, to try to catch
486 	 * delayed failure information from NFS.
487 	 */
488 #ifdef	HAVE_FSYNC
489 	if (fsync(fileno(gpkt.p_xiop)) < 0)
490 		xmsg(xf, NOGETTEXT("convert"));
491 #endif
492 	if (fclose(gpkt.p_xiop) == EOF)
493 		xmsg(xf, NOGETTEXT("flushline"));
494 	gpkt.p_xiop = NULL;
495 
496 	stat(gpkt.p_file, &sbuf);
497 	if (keepold)
498 		rename(gpkt.p_file, auxf(gpkt.p_file, 'o'));
499 
500 	rename(auxf(gpkt.p_file, 'x'), gpkt.p_file);
501 	chmod(gpkt.p_file, (unsigned int)sbuf.st_mode);
502 
503 	chown(gpkt.p_file, (unsigned int)sbuf.st_uid,
504 			(unsigned int)sbuf.st_gid);
505 
506 	if (olddate) {
507 		ts[0].tv_sec = sbuf.st_atime;
508 		ts[0].tv_nsec = stat_ansecs(&sbuf);
509 		ts[1].tv_sec = sbuf.st_mtime;
510 		ts[1].tv_nsec = stat_mnsecs(&sbuf);
511 
512 		if (olddate > 3)
513 			ts[1].tv_sec += 1;
514 		else if (olddate > 2)
515 			ts[1].tv_nsec += 1000;
516 		else if (olddate > 1)
517 			ts[1].tv_nsec += 1;
518 		if (ts[1].tv_nsec >= 1000000000) {
519 			ts[1].tv_nsec -= 1000000000;
520 			ts[1].tv_sec += 1;
521 		}
522 
523 		utimensat(AT_FDCWD, gpkt.p_file, ts, 0);
524 	}
525 
526 	clean_up();
527 }
528 
529 LOCAL char	xcomment[] = { CTLCHAR, COMMENTS, '_', '\0' };	/* "^Ac_" */
530 
531 /*
532  * Convert the delta table from a SCCS v6 history file to SCCS v4
533  * The conversion stops at BUSERNAM (the beginning of the user names).
534  */
535 LOCAL void
cvtdelt2v4(pkt)536 cvtdelt2v4(pkt)
537 	register struct packet *pkt;
538 {
539 	char		line[BUFSIZ];
540 	struct deltab	dt;
541 
542 	/*
543 	 * We need to permit to read and evaluate SCCS v6 time stamps.
544 	 */
545 	pkt->p_flags |= PF_V6;
546 
547 	line[0] = '\0';
548 	while (getline(pkt) != NULL) {
549 		if (pkt->p_line[0] != CTLCHAR)
550 			fmterr(pkt);
551 		switch (pkt->p_line[1]) {
552 
553 		case BDELTAB:
554 			/*
555 			 * Convert SCCS v6 to SCCS v4 delta line and save the
556 			 * old line as wrapped degenerated comment.
557 			 */
558 			del_ab(pkt->p_line, &dt, pkt);
559 			del_ba(&dt, line, pkt->p_flags & ~PF_V6);
560 			putline(pkt, line);
561 			line[0] = '\0';
562 			pkt->p_wrttn = 1;
563 			if (discardv6)
564 				continue;
565 			if (dt.d_dtime.dt_zone != DT_NO_ZONE) {
566 				strcpy(line, xcomment);
567 				strlcpy(&line[3], &pkt->p_line[1],
568 							sizeof (line) - 3);
569 			}
570 			continue;
571 
572 		case STATS:
573 		case INCLUDE:
574 		case EXCLUDE:
575 		case IGNORE:
576 		case MRNUM:
577 			continue;
578 
579 		case SIDEXTENS:
580 			if (discardv6) {
581 				pkt->p_wrttn = 1;
582 				continue;
583 			}
584 			/*
585 			 * Keep SCCS v6 extensions as degenerated comment.
586 			 *
587 			 * If not yet flushed, flush delta line.
588 			 */
589 			if (line[0] != '\0')
590 				putline(pkt, line);
591 			line[0] = '\0';
592 			putline(pkt, xcomment);
593 			putline(pkt, &pkt->p_line[1]);
594 			pkt->p_wrttn = 1;
595 			continue;
596 
597 		case COMMENTS:
598 			/*
599 			 * If not yet flushed, flush delta line.
600 			 */
601 			if (line[0] != '\0')
602 				putline(pkt, line);
603 			line[0] = '\0';
604 			continue;
605 
606 		case EDELTAB:
607 			continue;
608 
609 		case BUSERNAM:
610 			return;
611 
612 		default:
613 			fmterr(pkt);
614 		}
615 	}
616 }
617 
618 /*
619  * Convert the delta table from a SCCS v4 history file to SCCS v6
620  * The conversion stops at BUSERNAM (the beginning of the user names).
621  */
622 LOCAL void
cvtdelt2v6(pkt)623 cvtdelt2v6(pkt)
624 	register struct packet *pkt;
625 {
626 	char		line[BUFSIZ];
627 #define	MAX_DELT_LINES	1024
628 	char		*lines[MAX_DELT_LINES];
629 	int		nlines = 0;
630 	int		i;
631 	int		commentstate = 0;
632 	BOOL		incomment = FALSE;
633 	BOOL		needthis = FALSE;
634 	struct deltab	dt;
635 	struct deltab	dt2;
636 
637 	while (getline(pkt) != NULL) {
638 		if (pkt->p_line[0] != CTLCHAR)
639 			fmterr(pkt);
640 		switch (pkt->p_line[1]) {
641 
642 		case BDELTAB:
643 			/*
644 			 * Converting a delta line from SCCS v4 to SCCS v6
645 			 * is harder as we need to save all following lines and
646 			 * look for old saved SCCS v6 content in degenerated
647 			 * comment first.
648 			 */
649 			commentstate = 0;
650 			incomment = FALSE;
651 			del_ab(pkt->p_line, &dt, pkt);
652 			if (dt.d_dtime.dt_zone != DT_NO_ZONE)
653 				fmterr(pkt);
654 
655 			pkt->p_wrttn = 1;
656 			while (getline(pkt) != NULL) {
657 				if (pkt->p_line[0] != CTLCHAR)
658 					fmterr(pkt);
659 				/*
660 				 * Stop on first comment line or at the end
661 				 * of this delta table block.
662 				 */
663 				if (pkt->p_line[1] == COMMENTS)
664 					break;
665 				if (pkt->p_line[1] == EDELTAB)
666 					break;
667 				if (nlines >= MAX_DELT_LINES)
668 					fatal(_("OUT OF SPACE (ut9)"));
669 				lines[nlines] = strdup(pkt->p_line);
670 				if (lines[nlines++] == NULL)
671 					fatal(_("OUT OF SPACE (ut9)"));
672 				pkt->p_wrttn = 1;
673 			}
674 			/*
675 			 * If _we_ previously created a degenerated comment,
676 			 * then the first line is a saved v6 delta line.
677 			 * The first degenerated comment must be of type 'd'
678 			 * and the second degenerated comment must be of
679 			 * type 'S s'.
680 			 */
681 			if ((pkt->p_line[1] == COMMENTS) &&
682 			    (pkt->p_line[2] == '_') &&
683 			    (pkt->p_line[3] == BDELTAB) &&
684 			    (pkt->p_line[4] == ' ')) {
685 				pkt->p_line[2] = CTLCHAR;
686 				del_ab(&pkt->p_line[2], &dt2, pkt);
687 				if (dt2.d_dtime.dt_zone != DT_NO_ZONE) {
688 					dt.d_dtime.dt_zone =
689 						dt2.d_dtime.dt_zone;
690 					dt.d_dtime.dt_nsec =
691 						dt2.d_dtime.dt_nsec;
692 				} else {
693 					dt.d_dtime.dt_zone =
694 						gmtoff(dt.d_dtime.dt_sec);
695 				}
696 				commentstate = 1;
697 				needthis = FALSE;
698 			} else {
699 				dt.d_dtime.dt_zone =
700 					gmtoff(dt.d_dtime.dt_sec);
701 				needthis = TRUE;
702 			}
703 			/*
704 			 * Regenerate or generate a v6 delta line and write
705 			 * the remembered other lines.
706 			 */
707 			del_ba(&dt, line, pkt->p_flags);
708 			putline(pkt, line);
709 			for (i = 0; i < nlines; i++) {
710 				putline(pkt, lines[i]);
711 				free(lines[i]);
712 			}
713 			nlines = 0;
714 
715 			if (commentstate == 1) {
716 				pkt->p_wrttn = 1;
717 				getline(pkt);
718 				if ((pkt->p_line[1] == COMMENTS) &&
719 				    (pkt->p_line[2] == '_') &&
720 				    (pkt->p_line[3] == SIDEXTENS) &&
721 				    (pkt->p_line[4] == ' ') &&
722 				    (pkt->p_line[5] == 's')) {
723 					commentstate = 2;
724 				} else if ((pkt->p_line[1] == COMMENTS) &&
725 				    (pkt->p_line[2] == '_')) {
726 					commentstate = 3;
727 				} else {
728 					needthis = TRUE;
729 				}
730 			}
731 			if (commentstate != 2 && dt.d_type == 'D') {
732 				pkt->p_ghash = get_hash(dt.d_serial);
733 				sidext_ba(pkt, &dt);
734 			}
735 			if (commentstate >= 2)
736 				goto dcomment;
737 
738 			/*
739 			 * If the first delta comment was not a degenerated
740 			 * comment from us, write it back unmodified.
741 			 */
742 			if (needthis)
743 				putline(pkt, (char *)0);
744 			pkt->p_wrttn = 1;
745 			continue;
746 
747 		case COMMENTS:
748 			if (incomment)
749 				continue;
750 			/*
751 			 * Convert v6 extensions saved as degenerated comment
752 			 * back to v6 extensions.
753 			 */
754 		dcomment:
755 			if ((pkt->p_line[1] == COMMENTS) &&
756 			    (pkt->p_line[2] == '_') &&
757 			    (pkt->p_line[3] == SIDEXTENS)) {
758 				putctl(pkt);
759 				putline(pkt, &pkt->p_line[3]);
760 				pkt->p_wrttn = 1;
761 				continue;
762 			}
763 			incomment = TRUE;
764 			continue;
765 
766 		case STATS:
767 		case INCLUDE:
768 		case EXCLUDE:
769 		case IGNORE:
770 		case MRNUM:
771 			continue;
772 
773 		case SIDEXTENS:
774 			fmterr(pkt);
775 			continue;
776 
777 		case EDELTAB:
778 			incomment = FALSE;
779 			continue;
780 
781 		case BUSERNAM:
782 			return;
783 
784 		default:
785 			fmterr(pkt);
786 		}
787 	}
788 }
789 
790 LOCAL struct packet pk2;
791 LOCAL off_t	get_off;
792 LOCAL int	slnno;
793 
794 LOCAL void
get_setup(file)795 get_setup(file)
796 	char	*file;
797 {
798 	struct stats stats;
799 
800 	sinit(&pk2, file, SI_OPEN);
801 
802 	pk2.p_stdout = stderr;
803 	pk2.p_reopen = 1;
804 	pk2.p_cutoff = MAX_TIME;
805 
806 	if ((pk2.p_flags & PF_V6) == 0)
807 		pk2.p_flags |= PF_GMT;
808 
809 	if (dodelt(&pk2, &stats, (struct sid *) 0, 0) == 0)
810 		fmterr(&pk2);
811 	flushto(&pk2, EUSERTXT, FLUSH_NOCOPY);
812 	get_off = stell(&pk2);
813 	slnno = pk2.p_slnno;
814 }
815 
816 LOCAL int
get_hash(ser)817 get_hash(ser)
818 	int	ser;
819 {
820 	int	max_ser = maxser(&pk2);
821 
822 	if (ser > max_ser)
823 		return (-1);
824 
825 	sseek(&pk2, get_off, SEEK_SET);
826 	pk2.p_slnno = slnno;
827 
828 	pk2.p_reopen = 1;
829 	pk2.p_chkeof = 1;
830 	pk2.p_gotsid = pk2.p_idel[ser].i_sid;
831 	pk2.p_reqsid = pk2.p_gotsid;
832 
833 	zero((char *) pk2.p_apply, (max_ser+1)*sizeof(*pk2.p_apply));
834 	setup(&pk2, ser);
835 
836 	pk2.p_ghash = 0;
837 	while (readmod(&pk2))
838 		;
839 	return (pk2.p_ghash & 0xFFFF);
840 }
841 
842 
843 
844 LOCAL void
clean_up()845 clean_up()
846 {
847 	uname(&un);
848 	uuname = un.nodename;
849 	if (mylock(auxf(gpkt.p_file, 'z'), getpid(), uuname)) {
850 		sclose(&gpkt);
851 		sfree(&gpkt);
852 		if (gpkt.p_xiop) {
853 			fclose(gpkt.p_xiop);
854 			gpkt.p_xiop = NULL;
855 			unlink(auxf(gpkt.p_file, 'x'));
856 		}
857 		sclose(&pk2);
858 		sfree(&pk2);
859 		xrm(&gpkt);
860 		ffreeall();
861 		timersetlockfile(NULL);
862 		if (!islockchset(Zhold))
863 			unlockit(Zhold, getpid(), uuname);
864 	}
865 }
866 
867 LOCAL int
getN(argp,valp)868 getN(argp, valp)
869 	const char	*argp;
870 	void		*valp;
871 {
872 	initN(&N);
873 	N.n_parm = (char *)argp;
874 	return (TRUE);
875 }
876 
877 LOCAL int
getX(argp,valp)878 getX(argp, valp)
879 	const char	*argp;
880 	void		*valp;
881 {
882 	X.x_parm = (char *)argp;
883 	X.x_flags = XO_NULLPATH;
884 	if (!parseX(&X))
885 		return (BADFLAG);
886 	return (TRUE);
887 }
888