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 * @(#)val.c 1.71 20/09/10 J. Schilling
33 */
34 #if defined(sun)
35 #pragma ident "@(#)val.c 1.71 20/09/10 J. Schilling"
36 #endif
37 /*
38 * @(#)val.c 1.22 06/12/12
39 */
40
41 #if defined(sun)
42 #pragma ident "@(#)val.c"
43 #pragma ident "@(#)sccs:cmd/val.c"
44 #endif
45 /************************************************************************/
46 /* */
47 /* val - */
48 /* val [-mname] [-rSID] [-s] [-ytype] file ... */
49 /* */
50 /************************************************************************/
51
52 #define SCCS_MAIN /* define global vars */
53 #include <defines.h>
54 #include <version.h>
55 #include <had.h>
56 #include <i18n.h>
57 #include <ccstypes.h>
58 #include <schily/sysexits.h>
59
60 #define FILARG_ERR 0200 /* no file name given */
61 #define UNKDUP_ERR 0100 /* unknown or duplicate keyletter */
62 #define CORRUPT_ERR 040 /* corrupt file error code */
63 #define FILENAM_ERR 020 /* file name error code */
64 #define INVALSID_ERR 010 /* invalid or ambiguous SID error */
65 #define NONEXSID_ERR 04 /* non-existent SID error code */
66 #define TYPE_ERR 02 /* type arg value error code */
67 #define NAME_ERR 01 /* name arg value error code */
68 #define TRUE 1
69 #define FALSE 0
70 #define BLANK(l) while (!(*l == '\0' || *l == ' ' || *l == '\t')) l++;
71
72 static int silent; /* be silent, report only in exit code */
73 static int debug; /* print debug messages */
74 static int ret_code; /* prime return code from 'main' program */
75 static int inline_err = 0; /* input line error code (from 'process') */
76 static int infile_err = 0; /* file error code (from 'validate') */
77 static int inpstd; /* TRUE = args from standard input */
78
79 static struct packet gpkt;
80 static Nparms N; /* Keep -N parameters */
81 static Xparms X; /* Keep -X parameters */
82
83 static struct deltab dt;
84 static struct deltab odt;
85
86 static char path[FILESIZE]; /* storage for file name value */
87 static char sid[50]; /* storage for sid (-r) value */
88 static char type[50]; /* storage for type (-y) value */
89 static char name[50]; /* storage for name (-m) value */
90 static char *Argv[BUFSIZ];
91 static char line[BUFSIZ];
92 static char save_line[BUFSIZ];
93
94 static struct delent { /* structure for delta table entry */
95 char type; /* Type: D, R or U */
96 char *osid; /* SID from delta */
97 char *datetime; /* Whole datetime string */
98 char *pgmr; /* Programmer name */
99 char *serial; /* Serial number for this delta */
100 char *pred; /* Serial # for predecessor */
101 } del;
102
103 int main __PR((int argc, char **argv));
104 static void process __PR((char *p_line, int argc, char **argv));
105 static void do_validate __PR((char *c_path));
106 static void validate __PR((char *c_path, char *c_sid, char *c_type, char *c_name));
107 static void getdel __PR((struct delent *delp, char *lp, struct packet *pkt));
108 static void read_to __PR((int ch, struct packet *pkt));
109 static void report __PR((int code, char *inp_line, char *file));
110 static char * get_line __PR((struct packet *pkt)); /* function returning ptr to line read */
111 static void s_init __PR((struct packet *pkt, char *file));
112 static int read_mod __PR((struct packet *pkt));
113 static void add_q __PR((struct packet *pkt, int ser, int keep, int iord, int user));
114 static void rem_q __PR((struct packet *pkt, int ser));
115 static void set_keep __PR((struct packet *pkt));
116 static int chk_ix __PR((struct queue *new, struct queue *head));
117 static int do_delt __PR((struct packet *pkt, int goods, char *d_sid));
118 static int getstats __PR((struct packet *pkt));
119 static char * getastat __PR((char *p, int *ip));
120 static void get_setup __PR((char *file));
121 static void get_close __PR((void));
122 static int get_hashtest __PR((int ser));
123
124
125 /* This is the main program that determines whether the command line
126 * comes from the standard input or read off the original command
127 * line. See VAL(I) for more information.
128 */
129
130 int
main(argc,argv)131 main(argc, argv)
132 int argc;
133 char *argv[];
134 {
135 FILE *iop;
136 char *lp;
137
138 /*
139 * Set locale for all categories.
140 */
141 setlocale(LC_ALL, NOGETTEXT(""));
142
143 sccs_setinsbase(INS_BASE);
144
145 /*
146 * Set directory to search for general l10n SCCS messages.
147 */
148 #ifdef PROTOTYPES
149 (void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
150 NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
151 #else
152 (void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
153 NOGETTEXT("/usr/ccs/lib/locale/"));
154 #endif
155
156 (void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
157
158 tzset(); /* Set up timezome related vars */
159
160 #ifdef SCHILY_BUILD
161 save_args(argc, argv);
162 #endif
163 /*
164 Set flags for 'fatal' to issue message, call clean-up
165 routine and terminate processing.
166 */
167 Fflags = FTLMSG | FTLCLN | FTLEXIT;
168 #ifdef SCCS_FATALHELP
169 Fflags |= FTLFUNC;
170 Ffunc = sccsfatalhelp;
171 #endif
172
173 ret_code = 0;
174 if (argc == 2 && argv[1][0] == '-' && !(argv[1][1])) {
175 inpstd = TRUE;
176 iop = stdin; /* read from standard input */
177 while (fgets(line, BUFSIZ, iop) != NULL) {
178 if (line[0] != '\n') {
179 repl(line, '\n', '\0');
180 strlcpy(save_line, line, sizeof (save_line));
181 argv = Argv;
182 *argv++ = "val";
183 lp = save_line;
184 argc = 1;
185 while (*lp != '\0') {
186 while ((*lp == ' ') || (*lp == '\t'))
187 *lp++ = '\0';
188 *argv++ = lp++;
189 argc++;
190 while ((*lp != ' ') && (*lp != '\t') && (*lp != '\0'))
191 lp++;
192 }
193 *argv = NULL;
194 argv = Argv;
195 process(line, argc, argv);
196 ret_code |= inline_err;
197 }
198 }
199 } else {
200 inpstd = FALSE;
201 line[0] = '\0';
202 process(line, argc, argv);
203 ret_code = inline_err;
204 }
205
206 return (ret_code);
207 }
208
209
210 /* This function processes the line sent by the main routine. It
211 * determines which keyletter values are present on the command
212 * line and assigns the values to the correct storage place. It
213 * then calls validate for each file name on the command line
214 * It will return to main if the input line contains an error,
215 * otherwise it returns any error code found by validate.
216 */
217
218 static void
process(p_line,argc,argv)219 process(p_line, argc, argv)
220 char *p_line;
221 int argc;
222 char *argv[];
223 {
224 register int j;
225 register int line_sw;
226 register char *p;
227 register int i;
228
229 int num_files;
230
231 char **filelist = NULL;
232 char *savelinep;
233 int c;
234
235 int current_optind;
236 int no_arg;
237
238 silent = FALSE;
239 path[0] = sid[0] = type[0] = name[0] = 0;
240 num_files = inline_err = 0;
241
242 /*
243 make copy of 'line' for use later
244 */
245 savelinep = p_line;
246 /*
247 clear out had flags for each 'line' processed
248 */
249 for (j = 0; j < HAD_SIZE; j++)
250 had[j] = 0;
251 /*
252 execute loop until all characters in 'line' are checked.
253 */
254
255 current_optind = 1;
256 optind = 1;
257 opterr = 0;
258 no_arg = 0;
259 j = 1;
260 /*CONSTCOND*/
261 while (1) {
262 if (current_optind < optind) {
263 current_optind = optind;
264 argv[j] = 0;
265 if (optind > j+1) {
266 if ((argv[j+1][0] != '-') &&
267 (no_arg == 0)) {
268 argv[j+1] = NULL;
269 } else {
270 optind = j+1;
271 current_optind = optind;
272 }
273 }
274 }
275 no_arg = 0;
276 j = current_optind;
277 c = getopt(argc, argv, "()-r:sm:y:hvTN:X:V(version)");
278
279 /* this takes care of options given after
280 ** file names.
281 */
282 if (c == EOF) {
283 if (optind < argc) {
284 /* if it's due to -- then break; */
285 if (argv[j][0] == '-' &&
286 argv[j][1] == '-') {
287 argv[j] = 0;
288 break;
289 }
290 optind++;
291 current_optind = optind;
292 continue;
293 } else {
294 break;
295 }
296 }
297 p = optarg;
298 switch (c) {
299 case 'h':
300 break;
301 case 's':
302 silent = TRUE;
303 break;
304 case 'r':
305 strlcpy(sid, p, sizeof (sid));
306 break;
307 case 'y':
308 strlcpy(type, p, sizeof (type));
309 break;
310 case 'm':
311 strlcpy(name, p, sizeof (name));
312 break;
313 case 'v':
314 break;
315
316 case 'T':
317 debug = TRUE;
318 silent = FALSE;
319 break;
320
321 case 'N': /* Bulk names */
322 initN(&N);
323 if (optarg == argv[j+1]) {
324 no_arg = 1;
325 break;
326 }
327 N.n_parm = p;
328 break;
329
330 case 'X': /* -Xtended options */
331 X.x_parm = optarg;
332 X.x_flags = XO_NULLPATH;
333 if (!parseX(&X))
334 goto err;
335 had[NLOWER+c-'A'] = 0; /* Allow mult -X */
336 break;
337
338 case 'V': /* version */
339 printf(gettext(
340 "val %s-SCCS version %s %s (%s-%s-%s)\n"),
341 PROVIDER,
342 VERSION,
343 VDATE,
344 HOST_CPU, HOST_VENDOR, HOST_OS);
345 exit(EX_OK);
346
347 default:
348 err:
349 Fflags &= ~FTLEXIT;
350 fatal(gettext("Usage: val [ -h ] [ -s ] [ -m name ] [ -r SID ] [ -T ] [ -v ]\n\t[ -y type ] [ -N[bulk-spec]][ -Xxopts ] s.filename..."));
351 if (debug) {
352 printf(gettext("Uknown option '%c'.\n"),
353 optopt?optopt:c);
354 }
355 inline_err |= UNKDUP_ERR;
356 if (inpstd)
357 report(inline_err, savelinep, "");
358 else
359 report(inline_err, "", "");
360 return;
361 }
362 /*
363 * Make sure that we only collect option letters from
364 * the range 'a'..'z' and 'A'..'Z'.
365 *
366 * use 'had' array and determine if the keyletter
367 * was given twice.
368 */
369 if (ALPHA(c) &&
370 (had[LOWER(c)? c-'a' : NLOWER+c-'A']++)) {
371 if (c != 'X') {
372 if (debug) {
373 printf(gettext(
374 "Duplicate option '%c'.\n"),
375 c);
376 }
377 inline_err |= UNKDUP_ERR;
378 }
379 }
380 }
381
382 for (j = 1; j < argc; j++) {
383 if (argv[j]) {
384 if (filelist == NULL)
385 filelist = &argv[j];
386 num_files++;
387 }
388 }
389 /*
390 check if any files were named as arguments
391 */
392 if (num_files == 0) {
393 if (debug)
394 printf(gettext("Missing file argument.\n"));
395 inline_err |= FILARG_ERR;
396 }
397 /*
398 check for error in command line.
399 */
400 if (inline_err) {
401 if (!silent) {
402 if (inpstd)
403 report(inline_err, savelinep, "");
404 else
405 report(inline_err, "", "");
406 }
407 return; /* return to 'main' routine */
408 }
409 line_sw = 1; /* print command line flag */
410
411 xsethome(NULL);
412 if (HADUCN) { /* Parse -N args */
413 parseN(&N);
414
415 if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
416 fatal(gettext("-Ns. not supported in off-tree project mode"));
417 }
418
419 /*
420 loop through 'validate' for each file on command line.
421 */
422 for (i = 0, j = 0; j < num_files; j++) {
423 while (filelist[i+j] == NULL) {
424 i++;
425 if ((i+j+1) >= argc)
426 break;
427 }
428 if (filelist[i+j] == NULL)
429 break;
430
431 /*
432 read a file from 'filelist' and place into 'path'.
433 */
434 if (size(filelist[i+j]) > FILESIZE) {
435 extern char *Ffile;
436 Ffile = filelist[i+j];
437 fatal(gettext("too long (co7)"));
438 }
439 strlcpy(path, filelist[i+j], sizeof (path));
440 if (!inpstd) {
441 do_file(path, do_validate, 1, N.n_sdot, &X);
442 continue;
443 }
444 validate(path, sid, type, name);
445 inline_err |= infile_err;
446 /*
447 check for error from 'validate' and call 'report'
448 depending on 'silent' flag.
449 */
450 if (infile_err && !silent) {
451 if (line_sw && inpstd) {
452 report(infile_err, savelinep, path);
453 line_sw = 0;
454 } else
455 report(infile_err, "", path);
456 }
457 }
458 /* return to 'main' routine */
459 }
460
461 static void
do_validate(c_path)462 do_validate(c_path)
463 char *c_path;
464 {
465 if (HADUCN) {
466 #ifdef __needed__
467 char *ofile = c_path;
468 #endif
469
470 c_path = bulkprepare(&N, c_path);
471 if (c_path == NULL) {
472 #ifdef __needed__
473 if (N.n_ifile)
474 ofile = N.n_ifile;
475 #endif
476 /*
477 * The error is typically
478 * "directory specified as s-file (cm14)"
479 */
480 fatal(gettext(bulkerror(&N)));
481 }
482 }
483
484 validate(c_path, sid, type, name);
485 inline_err |= infile_err;
486
487 /*
488 * check for error from 'validate' and call 'report'
489 * depending on 'silent' flag.
490 */
491 if (infile_err && !silent) {
492 report(infile_err, "", c_path);
493 }
494 }
495
496 /* This function actually does the validation on the named file.
497 * It determines whether the file is an SCCS-file or if the file
498 * exists. It also determines if the values given for type, SID,
499 * and name match those in the named file. An error code is returned
500 * if any mismatch occurs. See VAL(I) for more information.
501 */
502
503 static void
validate(c_path,c_sid,c_type,c_name)504 validate(c_path, c_sid, c_type, c_name)
505 char *c_path;
506 char *c_sid;
507 char *c_type;
508 char *c_name;
509 {
510 register char *l;
511 int goods, goodt, goodn, hadmflag;
512
513 infile_err = goods = goodt = goodn = hadmflag = 0;
514 dt.d_dtime.dt_sec = odt.d_dtime.dt_sec = 0;
515 dt.d_dtime.dt_nsec = odt.d_dtime.dt_nsec = 0;
516 dt.d_dtime.dt_zone = odt.d_dtime.dt_zone = DT_NO_ZONE;
517
518 s_init(&gpkt, c_path);
519
520 if (!sccsfile(c_path) || (gpkt.p_iop = fopen(c_path, "rb")) == NULL) {
521 if (debug) {
522 if (!sccsfile(c_path)) {
523 printf(gettext("%s%s: not a sccs file\n"),
524 " ", c_path);
525 } else {
526 printf(gettext("%s%s cannot open\n"),
527 " ", c_path);
528 }
529 }
530 infile_err |= FILENAM_ERR;
531 } else {
532 l = get_line(&gpkt); /* read first line in file */
533 /*
534 check that it is header line.
535 */
536 if (l == NULL || (l = checkmagic(&gpkt, l)) == NULL) {
537 if (debug)
538 printf(
539 gettext("%s%s: corrupted first line in file\n"),
540 " ", c_path);
541 infile_err |= CORRUPT_ERR;
542 }
543 else {
544 if (HADV) {
545 if (gpkt.p_flags & PF_V6)
546 printf("SCCS V6 %s\n", c_path);
547 else
548 printf("SCCS V4 %s\n", c_path);
549 sclose(&gpkt);
550 return;
551 }
552
553 /*
554 * Read delta table for get_hashtest()
555 */
556 if (gpkt.p_flags & PF_V6 && HADH)
557 get_setup(c_path);
558
559 /*
560 * get old file checksum count
561 */
562 satoi(l, &gpkt.p_ihash);
563 gpkt.p_chash = 0;
564 gpkt.p_uchash = 0;
565 if (HADR) {
566 struct sid ssid;
567 char *p;
568
569 /*
570 check for invalid or ambiguous SID.
571 */
572 p = sid_ab(c_sid, &ssid);
573 if (*p ||
574 (ssid.s_rel == 0 && ssid.s_lev) ||
575 (ssid.s_lev == 0 && ssid.s_br) ||
576 (ssid.s_br == 0 && ssid.s_seq))
577 infile_err |= INVALSID_ERR;
578 }
579 /*
580 read delta table checking for errors and/or
581 SID.
582 */
583 if (do_delt(&gpkt, goods, c_sid)) {
584 sclose(&gpkt);
585 get_close(); /* for SID checksums */
586 if (debug)
587 printf(gettext(
588 "%s%s: invalid delta table at line %d\n"),
589 " ", c_path, gpkt.p_slnno);
590 infile_err |= CORRUPT_ERR;
591 return;
592 }
593
594 read_to(EUSERNAM, &gpkt);
595
596 if (HADY || HADM) {
597 /*
598 read flag section of delta table.
599 */
600 while (((l = get_line(&gpkt)) != NULL) &&
601 *l++ == CTLCHAR &&
602 *l++ == FLAG) {
603 NONBLANK(l);
604 repl(l, '\n', '\0');
605 if (*l == TYPEFLAG) {
606 l += 2;
607 if (equal(c_type, l))
608 goodt++;
609 }
610 else if (*l == MODFLAG) {
611 hadmflag++;
612 l += 2;
613 if (equal(c_name, l))
614 goodn++;
615 }
616 }
617
618 /*
619 * If there are SCCS v6 flags or SCCS v6 global
620 * meta data, we need to skip this data here.
621 */
622 if (gpkt.p_line != NULL &&
623 gpkt.p_line[0] == CTLCHAR &&
624 gpkt.p_line[1] != BUSERTXT)
625 read_to(BUSERTXT, &gpkt);
626
627 /*
628 * If we did not find the mandatory begin of
629 * the user comment area, the file is corrupt.
630 */
631 if (gpkt.p_line != NULL &&
632 gpkt.p_line[0] == CTLCHAR &&
633 gpkt.p_line[1] != BUSERTXT) {
634 sclose(&gpkt);
635 get_close(); /* for SID checksums */
636 if (debug)
637 printf(gettext(
638 "%s%s: flag section error at line %d\n"),
639 " ", c_path, gpkt.p_slnno);
640 infile_err |= CORRUPT_ERR;
641 return;
642 }
643 /*
644 check if 'y' flag matched '-y' arg value.
645 */
646 if (!goodt && HADY) {
647 if (debug)
648 printf(gettext(
649 "%s%s: mismatch between %cY%c and '%s'\n"),
650 " ", c_path,
651 '%', '%', c_type);
652 infile_err |= TYPE_ERR;
653 }
654 /*
655 check if 'm' flag matched '-m' arg value.
656 */
657 if (HADM && !hadmflag) {
658 if (!equal(auxf(sname(c_path), 'g'), c_name)) {
659 if (debug)
660 printf(gettext(
661 "%s%s: no 'm' flag\n"),
662 " ", c_path);
663 infile_err |= NAME_ERR;
664 }
665 }
666 else if (HADM && hadmflag && !goodn) {
667 if (debug)
668 printf(gettext(
669 "%s%s: mismatch between %cM%c and '%s'\n"),
670 " ", c_path,
671 '%', '%', c_name);
672 infile_err |= NAME_ERR;
673 }
674 }
675 else read_to(BUSERTXT, &gpkt);
676 read_to(EUSERTXT, &gpkt);
677 gpkt.p_chkeof = 1;
678 /*
679 read remainder of file so 'read_mod'
680 can check for corruptness.
681 */
682 while (read_mod(&gpkt))
683 ;
684 }
685 sclose(&gpkt); /* close file pointer */
686 sfree(&gpkt); /* free line pointer */
687 get_close(); /* for SID checksums */
688 }
689 /* return to 'process' function */
690 }
691
692
693 /* This function reads the 'delta' line from the named file and stores
694 * the information into the structure 'del'.
695 */
696
697 static void
getdel(delp,lp,pkt)698 getdel(delp, lp, pkt)
699 register struct delent *delp;
700 register char *lp;
701 struct packet *pkt;
702 {
703 char *p;
704 int dflags = 0;
705 int missfld = 0;
706
707 if (debug) {
708 char str[SID_STRSIZE];
709 char ostr[SID_STRSIZE];
710
711 del_ab(&lp[-2], &dt, pkt); /* We are called with &lp[2] */
712
713 if ((dt.d_dtime.dt_sec != 0 && odt.d_dtime.dt_sec != 0 &&
714 (dt.d_dtime.dt_sec > odt.d_dtime.dt_sec)) ||
715 ((dt.d_dtime.dt_sec == odt.d_dtime.dt_sec) &&
716 (dt.d_dtime.dt_nsec > odt.d_dtime.dt_nsec))) {
717 sid_ba(&dt.d_sid, str);
718 sid_ba(&odt.d_sid, ostr);
719 printf(gettext(
720 "%s%s: warning: date for sid %s is later than the date for sid %s in line %d\n"),
721 " ", pkt->p_file,
722 str, ostr, pkt->p_slnno);
723 }
724 odt = dt; /* Remember last date */
725 }
726
727 NONBLANK(lp);
728 delp->type = *lp++; /* Type 'D' */
729 NONBLANK(lp);
730 delp->osid = lp; /* SID "1.2" */
731 BLANK(lp);
732 if (*lp)
733 *lp++ = '\0';
734 else
735 missfld++;
736 NONBLANK(lp); /* Find date time string */
737 delp->datetime = lp; /* Date "06/12/20 23:46:27" */
738 BLANK(lp); /* Skip past date */
739 NONBLANK(lp); /* Find time */
740 BLANK(lp); /* Skip past time */
741 if (*lp)
742 *lp++ = '\0';
743 else
744 missfld++;
745 p = lp;
746 NONBLANK(lp); /* Find programmer */
747 /*
748 * Old sccs implementations did frequently write something like
749 * "^Ad D 4.42 82/10/19 10:30:32 64 63"
750 * So we check for extra blanks and later verify whether there
751 * are too few fields.
752 */
753 if (lp != p && *p == ' ')
754 dflags |= 1; /* Mark as double space */
755 delp->pgmr = lp; /* Programmer "bill" */
756 BLANK(lp); /* Skip past programmer */
757 if (*lp)
758 *lp++ = '\0';
759 else
760 missfld++;
761 NONBLANK(lp); /* Find serial */
762 delp->serial = lp; /* Serial "42" */
763 BLANK(lp); /* Skip past serial */
764 if (*lp)
765 *lp++ = '\0';
766 else
767 missfld++;
768 NONBLANK(lp); /* Find pred */
769 delp->pred = lp; /* Pred serial "41" */
770 if (missfld == 0 && (*lp == '\0' || *lp == '\n'))
771 missfld++;
772 repl(lp, '\n', '\0');
773 if (dflags && (*lp == '\0' || *lp == '\n')) {
774 if (debug)
775 printf(gettext("%s%s: username missing in line %d\n"),
776 " ", pkt->p_file, pkt->p_slnno);
777 infile_err |= CORRUPT_ERR;
778 }
779 if (missfld > 1 || (missfld && dflags == 0)) {
780 if (debug)
781 printf(gettext("%s%s: missing field in delta in line %d\n"),
782 " ", pkt->p_file, pkt->p_slnno);
783 infile_err |= CORRUPT_ERR;
784 }
785 if ((pkt->p_flags & PF_V6) == 0) {
786 int dterr = 0;
787
788 p = strchr(delp->datetime, '/');
789 if ((p - delp->datetime) > 2) {
790 if (dt.d_dtime.dt_sec < Y1969 ||
791 dt.d_dtime.dt_sec >= Y2038) {
792 /*
793 * In this case, 4-digit year numbers are OK.
794 */
795 if ((p - delp->datetime) != 4)
796 dterr++;
797 } else {
798 dterr++;
799 }
800 }
801 /*
802 * Nanoseconds not allowed in SCCSv4 mode.
803 */
804 if (strchr(delp->datetime, '.'))
805 dterr++;
806 /*
807 * With 2-digit year, the length is 17.
808 */
809 if (strlen(delp->datetime) > 19)
810 dterr++;
811 if (dterr) {
812 if (debug)
813 printf(gettext("%s%s: invalid v4 datetime field '%s' in line %d\n"),
814 " ", pkt->p_file, delp->datetime, pkt->p_slnno);
815 infile_err |= CORRUPT_ERR;
816 }
817 }
818 }
819
820
821 /* This function does a read through the named file until it finds
822 * the character sent over as an argument.
823 */
824
825 static void
read_to(ch,pkt)826 read_to(ch, pkt)
827 register char ch;
828 register struct packet *pkt;
829 {
830 register char *n;
831 while (((n = get_line(pkt)) != NULL) &&
832 !(*n++ == CTLCHAR && *n == ch))
833 ;
834 }
835
836
837 /* This function will report the error that occured on the command
838 * line. It will print one diagnostic message for each error that
839 * was found in the named file.
840 */
841
842 static void
report(code,inp_line,file)843 report(code, inp_line, file)
844 register int code;
845 register char *inp_line;
846 register char *file;
847 {
848 char percent;
849 percent = '%'; /* '%' for -m and/or -y messages */
850 /* xpg4 behaviour
851 if (*inp_line)
852 printf("%s:\n", inp_line);
853 if (code & NAME_ERR)
854 printf(gettext(" %s: %cM%c -m mismatch\n"), file, percent, percent);
855 if (code & TYPE_ERR)
856 printf(gettext(" %s: %cY%c -y mismatch\n"), file, percent, percent);
857 if (code & NONEXSID_ERR)
858 printf(gettext(" %s: SID nonexistent\n"), file);
859 if (code & INVALSID_ERR)
860 printf(gettext(" %s: SID invalid or ambiguous\n"), file);
861 if (code & FILENAM_ERR)
862 printf(gettext(" %s: can't open file or file not SCCS\n"), file);
863 if (code & CORRUPT_ERR)
864 printf(gettext(" %s: corrupted SCCS file\n"), file);
865 if (code & UNKDUP_ERR)
866 printf(gettext(" %s: Unknown or duplicate keyletter argument\n"), file);
867 if (code & FILARG_ERR)
868 printf(gettext(" %s: missing file argument\n"), file);
869 */
870 if (*inp_line)
871 printf("%s\n\n", inp_line);
872 if (code & NAME_ERR)
873 printf(gettext(" %s: %cM%c -m mismatch\n"), file, percent, percent);
874 if (code & TYPE_ERR)
875 printf(gettext(" %s: %cY%c -y mismatch\n"), file, percent, percent);
876 if (code & NONEXSID_ERR)
877 printf(gettext(" %s: SID nonexistent\n"), file);
878 if (code & INVALSID_ERR)
879 printf(gettext(" %s: SID invalid or ambiguous\n"), file);
880 if (code & FILENAM_ERR)
881 printf(gettext(" %s: can't open file or file not SCCS\n"), file);
882 if (code & CORRUPT_ERR)
883 printf(gettext(" %s: corrupted SCCS file\n"), file);
884 if (code & UNKDUP_ERR)
885 printf(gettext(" %s: Unknown or duplicate keyletter argument\n"), file);
886 if (code & FILARG_ERR)
887 printf(gettext(" %s: missing file argument\n"), file);
888 }
889
890
891 /*
892 Routine to read a line into the packet. The main reason for
893 it is to make sure that pkt->p_wrttn gets turned off,
894 and to increment pkt->p_slnno.
895 */
896
897 static char *
get_line(pkt)898 get_line(pkt)
899 register struct packet *pkt;
900 {
901 #ifdef NO_GETDELIM
902 char buf[DEF_LINE_SIZE+1];
903 register size_t nread = 0;
904 #endif
905 int eof = 0;
906 register size_t used = 0;
907
908 #ifndef NO_GETDELIM
909 /*
910 * getdelim() allows to read lines that have embedded nul bytes
911 * and we don't need to call strlen().
912 */
913 errno = 0;
914 used = getdelim(&pkt->p_line, &pkt->p_line_size, '\n', pkt->p_iop);
915 if (used == -1) {
916 if (errno == ENOMEM)
917 fatal(gettext("OUT OF SPACE (ut9)"));
918 if (ferror(pkt->p_iop)) {
919 xmsg(pkt->p_file, NOGETTEXT("getline"));
920 } else if (feof(pkt->p_iop)) {
921 used = 0;
922 eof = 1;
923 }
924 }
925 #else
926 /* read until EOF or newline encountered */
927 do {
928 (void)memset(buf, '\377', sizeof (buf));
929 if (!(eof = (fgets(buf, sizeof (buf), pkt->p_iop) == NULL))) {
930 for (nread = sizeof (buf); --nread > 0; )
931 if (buf[nread] == '\0')
932 break;
933
934 if ((used + nread) >= pkt->p_line_size) {
935 pkt->p_line_size += sizeof (buf);
936 pkt->p_line = (char *) realloc(pkt->p_line, pkt->p_line_size);
937 if (pkt->p_line == NULL) {
938 efatal(gettext("OUT OF SPACE (ut9)"));
939 }
940 }
941
942 memcpy(pkt->p_line + used, buf, nread);
943 used += nread;
944 }
945 } while (!eof && (pkt->p_line[used-1] != '\n'));
946 #endif
947 pkt->p_linebase = pkt->p_line;
948 pkt->p_line_length = used;
949
950 /* check end of file condition */
951 if (eof && (used == 0)) {
952 if (!pkt->p_chkeof) {
953 if (debug)
954 printf(gettext(
955 "%s%s: incomplete delta table\n"),
956 " ", pkt->p_file);
957 infile_err |= CORRUPT_ERR;
958 }
959 if (pkt->do_chksum && (pkt->p_chash ^ pkt->p_ihash)&0xFFFF) {
960 if (pkt->do_chksum && (pkt->p_uchash ^ pkt->p_ihash)&0xFFFF) {
961 if (debug)
962 printf(gettext(
963 "%s%s: invalid checksum %d, expected %d\n"),
964 " ", pkt->p_file,
965 pkt->p_ihash,
966 pkt->p_chash & 0xFFFF);
967 infile_err |= CORRUPT_ERR;
968 }
969 }
970 return (NULL);
971 }
972
973 /* increment line number */
974 if (pkt->p_line[used-1] == '\n') {
975 pkt->p_slnno++;
976 }
977
978 /* update check sum */
979 pkt->p_chash += ssum((char *)pkt->p_line, pkt->p_line_length);
980 pkt->p_uchash += usum((char *)pkt->p_line, pkt->p_line_length);
981
982 return (pkt->p_line);
983 }
984
985
986 /*
987 Does initialization for sccs files and packet.
988 */
989
990 static void
s_init(pkt,file)991 s_init(pkt, file)
992 register struct packet *pkt;
993 register char *file;
994 {
995
996 zero((char *) pkt, sizeof (*pkt));
997 copy(file, pkt->p_file);
998 pkt->p_wrttn = 1;
999 pkt->do_chksum = 1; /* turn on checksum check for getline */
1000 }
1001
1002 static int
read_mod(pkt)1003 read_mod(pkt)
1004 register struct packet *pkt;
1005 {
1006 register char *p;
1007 int ser;
1008 int iord;
1009 register struct apply *ap;
1010
1011 while (get_line(pkt) != NULL) {
1012 p = pkt->p_line;
1013 if (*p++ != CTLCHAR)
1014 continue;
1015 else {
1016 if (!((iord = *p++) == INS || iord == DEL || iord == END)) {
1017 if (iord == CTLCHAR && pkt->p_flags & PF_V6)
1018 continue;
1019 if (iord == NONL && pkt->p_flags & PF_V6)
1020 continue;
1021 if (debug)
1022 printf(gettext(
1023 "%s%s: invalid control in weave data near line %d\n"),
1024 " ", pkt->p_file,
1025 pkt->p_slnno);
1026 infile_err |= CORRUPT_ERR;
1027 return (0);
1028 }
1029 NONBLANK(p);
1030 satoi(p, &ser);
1031 if (iord == END)
1032 rem_q(pkt, ser);
1033 else if (pkt->p_apply != 0) {
1034 ap = &pkt->p_apply[ser];
1035 if (ap->a_code == APPLY)
1036 add_q(pkt, ser, iord == INS ? YES : NO,
1037 iord, ap->a_reason & USER);
1038 else
1039 add_q(pkt, ser, iord == INS ? NO : 0,
1040 iord, ap->a_reason & USER);
1041 } else
1042 add_q(pkt, ser, iord == INS ? NO : 0, iord, 0);
1043 }
1044 }
1045 if (pkt->p_q) {
1046 if (debug)
1047 printf(gettext(
1048 "%s%s: incomplete weave data near line %d\n"),
1049 " ", pkt->p_file,
1050 pkt->p_slnno);
1051 infile_err |= CORRUPT_ERR;
1052 }
1053 return (0);
1054 }
1055
1056 static void
add_q(pkt,ser,keep,iord,user)1057 add_q(pkt, ser, keep, iord, user)
1058 struct packet *pkt;
1059 int ser;
1060 int keep;
1061 int iord;
1062 int user;
1063 {
1064 register struct queue *cur, *prev, *q;
1065
1066 for (cur = (struct queue *) (&pkt->p_q); (cur = (prev = cur)->q_next) != NULL; )
1067 if (cur->q_sernum <= ser)
1068 break;
1069 if (cur && cur != (struct queue *)&pkt->p_q && cur->q_sernum == ser) {
1070 if (debug)
1071 printf(gettext(
1072 "%s%s: duplicate delta block in weave data with serial %d near line %d\n"),
1073 " ", pkt->p_file,
1074 cur->q_sernum, pkt->p_slnno);
1075 infile_err |= CORRUPT_ERR;
1076 }
1077 prev->q_next = q = (struct queue *) fmalloc(sizeof (*q));
1078 q->q_next = cur;
1079 q->q_sernum = ser;
1080 q->q_keep = keep;
1081 q->q_iord = iord;
1082 q->q_user = user;
1083 if (pkt->p_ixuser && (q->q_ixmsg = chk_ix(q, pkt->p_q)))
1084 ++(pkt->p_ixmsg);
1085 else
1086 q->q_ixmsg = 0;
1087
1088 set_keep(pkt);
1089 }
1090
1091 static void
rem_q(pkt,ser)1092 rem_q(pkt, ser)
1093 register struct packet *pkt;
1094 int ser;
1095 {
1096 register struct queue *cur, *prev;
1097
1098 for (cur = (struct queue *) (&pkt->p_q); (cur = (prev = cur)->q_next) != NULL; )
1099 if (cur->q_sernum == ser)
1100 break;
1101 if (cur && cur != (struct queue *)&pkt->p_q) {
1102 if (cur->q_ixmsg)
1103 --(pkt->p_ixmsg);
1104 prev->q_next = cur->q_next;
1105 ffree((char *) cur);
1106 set_keep(pkt);
1107 }
1108 else {
1109 if (debug)
1110 printf(gettext(
1111 "%s%s: incomplete delta block in weave data near line %d\n"),
1112 " ", pkt->p_file,
1113 pkt->p_slnno);
1114 infile_err |= CORRUPT_ERR;
1115 }
1116 }
1117
1118 static void
set_keep(pkt)1119 set_keep(pkt)
1120 register struct packet *pkt;
1121 {
1122 register struct queue *q;
1123 register struct sid *sp;
1124
1125 for (q = pkt->p_q; q; q = q->q_next)
1126 if (q->q_keep != '\0') {
1127 if ((pkt->p_keep = q->q_keep) == YES) {
1128 sp = &pkt->p_idel[q->q_sernum].i_sid;
1129 pkt->p_inssid.s_rel = sp->s_rel;
1130 pkt->p_inssid.s_lev = sp->s_lev;
1131 pkt->p_inssid.s_br = sp->s_br;
1132 pkt->p_inssid.s_seq = sp->s_seq;
1133 }
1134 return;
1135 }
1136 pkt->p_keep = NO;
1137 }
1138
1139
1140 #define apply(qp) ((qp->q_iord == INS && qp->q_keep == YES) || \
1141 (qp->q_iord == DEL && qp->q_keep == NO))
1142 static int
chk_ix(new,head)1143 chk_ix(new, head)
1144 register struct queue *new;
1145 struct queue *head;
1146 {
1147 register int retval;
1148 register struct queue *cur;
1149 int firstins, lastdel;
1150
1151 if (!apply(new))
1152 return (0);
1153 for (cur = head; cur; cur = cur->q_next)
1154 if (cur->q_user)
1155 break;
1156 if (!cur)
1157 return (0);
1158 retval = 0;
1159 firstins = 0;
1160 lastdel = 0;
1161 for (cur = head; cur; cur = cur->q_next) {
1162 if (apply(cur)) {
1163 if (cur->q_iord == DEL)
1164 lastdel = cur->q_sernum;
1165 else if (firstins == 0)
1166 firstins = cur->q_sernum;
1167 }
1168 else if (cur->q_iord == INS)
1169 retval++;
1170 }
1171 if (retval == 0) {
1172 if (lastdel && (new->q_sernum > lastdel))
1173 retval++;
1174 if (firstins && (new->q_sernum < firstins))
1175 retval++;
1176 }
1177 return (retval);
1178 }
1179
1180
1181 /*
1182 * This function reads the delta table entries and checks for the format
1183 * as specifed in sccsfile(V). If the format is incorrect, a corrupt
1184 * error will be issued by 'val'. This function also checks
1185 * if the sid requested is in the file (depending if '-r' was specified).
1186 */
1187
1188 static int
do_delt(pkt,goods,d_sid)1189 do_delt(pkt, goods, d_sid)
1190 register struct packet *pkt;
1191 register int goods;
1192 register char *d_sid;
1193 {
1194 char *l;
1195
1196 while (getstats(pkt)) {
1197 if ((((l = get_line(pkt)) != NULL) && *l++ != CTLCHAR) ||
1198 *l++ != BDELTAB)
1199 return (1);
1200 getdel(&del, l, pkt);
1201 if (HADR && !(infile_err & INVALSID_ERR)) {
1202 if (equal(d_sid, del.osid) &&
1203 (del.type == 'D' || del.type == 'U'))
1204 goods++;
1205 }
1206 if (gpkt.p_flags & PF_V6 &&
1207 HADH && (del.type == 'D' || del.type == 'U')) {
1208 int ser;
1209
1210 satoi(del.serial, &ser);
1211 get_hashtest(ser);
1212 }
1213 while ((l = get_line(pkt)) != NULL)
1214 if (pkt->p_line[0] != CTLCHAR)
1215 break;
1216 else {
1217 switch (pkt->p_line[1]) {
1218 case EDELTAB:
1219 break;
1220 case COMMENTS:
1221 case MRNUM:
1222 case INCLUDE:
1223 case EXCLUDE:
1224 case IGNORE:
1225 continue;
1226 case SIDEXTENS:
1227 if (pkt->p_flags & PF_V6)
1228 continue;
1229 default:
1230 return (1);
1231 }
1232 break;
1233 }
1234 if (l == NULL || pkt->p_line[0] != CTLCHAR)
1235 return (1);
1236 }
1237 if (pkt->p_line[1] != BUSERNAM)
1238 return (1);
1239 if (HADR && !goods && !(infile_err & INVALSID_ERR)) {
1240 infile_err |= NONEXSID_ERR;
1241 }
1242 return (0);
1243 }
1244
1245
1246 /* This function reads the stats line from the sccsfile */
1247
1248 static int
getstats(pkt)1249 getstats(pkt)
1250 register struct packet *pkt;
1251 {
1252 register char *p = get_line(pkt);
1253 register char *op;
1254 register int i = 0;
1255
1256 if (p == NULL || *p++ != CTLCHAR || *p != STATS)
1257 return (0);
1258
1259 /*
1260 * We do not check the format of the statistics line in non-debug mode
1261 * as the related values are otherwise ignored by SCCS.
1262 */
1263 if (!debug)
1264 return (1);
1265
1266 p++;
1267 NONBLANK(p);
1268 if (*p < '0' || *p > '9') {
1269 printf(gettext(
1270 "%s%s: illegal character in statistics in line %d\n"),
1271 " ", pkt->p_file, pkt->p_slnno);
1272 }
1273 while (*p) {
1274 if (*p == '\n')
1275 break;
1276 op = p;
1277 p = getastat(p, NULL);
1278 if ((i == 2 && *p != '\n') || (i < 2 && *p != '/')) {
1279 printf(gettext(
1280 "%s%s: illegal character in statistics in line %d\n"),
1281 " ", pkt->p_file, pkt->p_slnno);
1282 infile_err |= CORRUPT_ERR;
1283 return (1);
1284 } else if ((p - op) != 5) {
1285 if ((p - op) > 5)
1286 printf(gettext(
1287 "%s%s: number exceeds '99999' in statistics in line %d\n"),
1288 " ", pkt->p_file, pkt->p_slnno);
1289 else
1290 printf(gettext(
1291 "%s%s: illegal number length in statistics in line %d\n"),
1292 " ", pkt->p_file, pkt->p_slnno);
1293 infile_err |= CORRUPT_ERR;
1294 return (1);
1295 }
1296 p++;
1297 i++;
1298 }
1299 return (1);
1300 }
1301
1302 static char *
getastat(p,ip)1303 getastat(p, ip)
1304 char *p;
1305 int *ip;
1306 {
1307 int i = 0;
1308 int c;
1309
1310 while ((c = *p) != '\0') {
1311 if (c < '0' || c > '9') {
1312 if (c != '/' && c != '\n')
1313 i = -1;
1314 break;
1315 }
1316 i *= 10;
1317 i += c - '0';
1318 p++;
1319 }
1320 if (ip)
1321 *ip = i;
1322 return (p);
1323 }
1324
1325 LOCAL struct packet pk2;
1326 LOCAL off_t get_off;
1327 LOCAL int slnno;
1328
1329 LOCAL void
get_setup(file)1330 get_setup(file)
1331 char *file;
1332 {
1333 struct stats stats;
1334
1335 sinit(&pk2, file, SI_OPEN);
1336
1337 pk2.do_chksum = 0;
1338 pk2.p_stdout = stderr;
1339 pk2.p_reopen = 1;
1340 pk2.p_cutoff = MAX_TIME;
1341
1342 if ((pk2.p_flags & PF_V6) == 0)
1343 pk2.p_flags |= PF_GMT;
1344
1345 if (dodelt(&pk2, &stats, (struct sid *) 0, 0) == 0)
1346 fmterr(&pk2);
1347 flushto(&pk2, EUSERTXT, FLUSH_NOCOPY);
1348 get_off = stell(&pk2);
1349 slnno = pk2.p_slnno;
1350
1351 if (pk2.p_hash == NULL) {
1352 if (debug)
1353 printf(gettext(
1354 "%s%s: SID checksums missing\n"),
1355 " ", pk2.p_file);
1356 infile_err |= CORRUPT_ERR;
1357 }
1358 }
1359
1360 LOCAL void
get_close()1361 get_close()
1362 {
1363 sclose(&pk2);
1364 sfree(&pk2);
1365 xrm(&gpkt);
1366 ffreeall();
1367 }
1368
1369 LOCAL int
get_hashtest(ser)1370 get_hashtest(ser)
1371 int ser;
1372 {
1373 int max_ser = maxser(&pk2);
1374
1375 if (ser > max_ser)
1376 return (-1);
1377
1378 if (pk2.p_hash == NULL)
1379 return (-1);
1380
1381 sseek(&pk2, get_off, SEEK_SET);
1382 pk2.p_slnno = slnno;
1383
1384 pk2.p_reopen = 1;
1385 pk2.p_chkeof = 1;
1386 pk2.p_gotsid = pk2.p_idel[ser].i_sid;
1387 pk2.p_reqsid = pk2.p_gotsid;
1388
1389 zero((char *) pk2.p_apply, (max_ser+1)*sizeof (*pk2.p_apply));
1390 setup(&pk2, ser);
1391
1392 pk2.p_ghash = 0;
1393 while (readmod(&pk2))
1394 ;
1395
1396 if (pk2.p_hash == NULL)
1397 return (0);
1398
1399 if (pk2.p_hash[ser] != (pk2.p_ghash & 0xFFFF)) {
1400 if (debug) {
1401 char str[SID_STRSIZE];
1402
1403 sid_ba(&pk2.p_gotsid, str);
1404 printf(gettext(
1405 "%s%s: SID %s: invalid checksum %d, expected %d\n"),
1406 " ", pk2.p_file,
1407 str,
1408 pk2.p_ghash & 0xFFFF,
1409 pk2.p_hash[ser]);
1410 }
1411 infile_err |= CORRUPT_ERR;
1412 return (0);
1413 }
1414 return (1);
1415 }
1416