1 /************************************************************************/
2 /* */
3 /* val - */
4 /* val [-mname] [-rSID] [-s] [-ytype] file ... */
5 /* */
6 /************************************************************************/
7
8 # include "../hdr/defines.h"
9 # include "../hdr/had.h"
10
11 # define FILARG_ERR 0200 /* no file name given */
12 # define UNKDUP_ERR 0100 /* unknown or duplicate keyletter */
13 # define CORRUPT_ERR 040 /* corrupt file error code */
14 # define FILENAM_ERR 020 /* file name error code */
15 # define INVALSID_ERR 010 /* invalid or ambiguous SID error */
16 # define NONEXSID_ERR 04 /* non-existent SID error code */
17 # define TYPE_ERR 02 /* type arg value error code */
18 # define NAME_ERR 01 /* name arg value error code */
19 # define TRUE 1
20 # define FALSE 0
21 # define BLANK(l) while (!(*l == ' ' || *l == '\t')) l++;
22
23 int ret_code; /* prime return code from 'main' program */
24 int inline_err; /* input line error code (from 'process') */
25 int infile_err; /* file error code (from 'validate') */
26 int inpstd; /* TRUE = args from standard input */
27
28 struct packet gpkt;
29
30 char had[26]; /* had flag used in 'process' function */
31 char path[50]; /* storage for file name value */
32 char sid[50]; /* storage for sid (-r) value */
33 char type[50]; /* storage for type (-y) value */
34 char name[50]; /* storage for name (-m) value */
35 char line[BUFSIZ];
36 char *get_line(); /* function returning ptr to line read */
37 char *getval(); /* function returning adjusted ptr to line */
38 char *alloc(); /* function returning ptr */
39 char *fgets(); /* function returning i/o ptr */
40
41 struct delent { /* structure for delta table entry */
42 char type;
43 char *osid;
44 char *datetime;
45 char *pgmr;
46 char *serial;
47 char *pred;
48 } del;
49
50 static char Sccsid[] = "@(#)val.c 4.3 02/15/87";
51
52 /* This is the main program that determines whether the command line
53 * comes from the standard input or read off the original command
54 * line. See VAL(I) for more information.
55 */
main(argc,argv)56 main(argc,argv)
57 int argc;
58 char *argv[];
59 {
60 FILE *iop;
61 register int j;
62
63 ret_code = 0;
64 if (argc == 2 && argv[1][0] == '-' && !(argv[1][1])) {
65 inpstd = TRUE;
66 iop = stdin; /* read from standard input */
67 while (fgets(line,BUFSIZ,iop) != NULL) {
68 if (line[0] != '\n') {
69 repl (line,'\n','\0');
70 process(line);
71 ret_code |= inline_err;
72 }
73 }
74 }
75 else {
76 inpstd = FALSE;
77 for (j = 1; j < argc; j++)
78 sprintf(&(line[strlen(line)]),"%s ",argv[j]);
79 j = strlen(line) - 1;
80 line[j > 0 ? j : 0] = NULL;
81 process(line);
82 ret_code = inline_err;
83 }
84 exit(ret_code);
85 }
86
87
88 /* This function processes the line sent by the main routine. It
89 * determines which keyletter values are present on the command
90 * line and assigns the values to the correct storage place. It
91 * then calls validate for each file name on the command line
92 * It will return to main if the input line contains an error,
93 * otherwise it returns any error code found by validate.
94 */
process(p_line)95 process(p_line)
96 char *p_line;
97 {
98 register int j;
99 register int testklt;
100 register int line_sw;
101
102 int silent;
103 int num_files;
104
105 char filelist[50][50];
106 char *savelinep;
107 char c;
108
109 silent = FALSE;
110 path[0] = sid[0] = type[0] = name[0] = 0;
111 num_files = inline_err = 0;
112
113 /*
114 make copy of 'line' for use later
115 */
116 savelinep = p_line;
117 /*
118 clear out had flags for each 'line' processed
119 */
120 for (j = 0; j < 27; j++)
121 had[j] = 0;
122 /*
123 execute loop until all characters in 'line' are checked.
124 */
125 while (*p_line) {
126 testklt = 1;
127 NONBLANK(p_line);
128 if (*p_line == '-') {
129 p_line += 1;
130 c = *p_line;
131 p_line++;
132 switch (c) {
133 case 's':
134 testklt = 0;
135 /*
136 turn on 'silent' flag.
137 */
138 silent = TRUE;
139 break;
140 case 'r':
141 p_line = getval(p_line,sid);
142 break;
143 case 'y':
144 p_line = getval(p_line,type);
145 break;
146 case 'm':
147 p_line = getval(p_line,name);
148 break;
149 default:
150 inline_err |= UNKDUP_ERR;
151 }
152 /*
153 use 'had' array and determine if the keyletter
154 was given twice.
155 */
156 if (had[c - 'a']++ && testklt++)
157 inline_err |= UNKDUP_ERR;
158 }
159 else {
160 /*
161 assume file name if no '-' preceeded argument
162 */
163 p_line = getval(p_line,filelist[num_files]);
164 num_files++;
165 }
166 }
167 /*
168 check if any files were named as arguments
169 */
170 if (num_files == 0)
171 inline_err |= FILARG_ERR;
172 /*
173 check for error in command line.
174 */
175 if (inline_err && !silent) {
176 if (inpstd)
177 report(inline_err,savelinep,"");
178 else report(inline_err,"","");
179 return; /* return to 'main' routine */
180 }
181 line_sw = 1; /* print command line flag */
182 /*
183 loop through 'validate' for each file on command line.
184 */
185 for (j = 0; j < num_files; j++) {
186 /*
187 read a file from 'filelist' and place into 'path'.
188 */
189 sprintf(path,"%s",filelist[j]);
190 validate(path,sid,type,name);
191 inline_err |= infile_err;
192 /*
193 check for error from 'validate' and call 'report'
194 depending on 'silent' flag.
195 */
196 if (infile_err && !silent) {
197 if (line_sw && inpstd) {
198 report(infile_err,savelinep,path);
199 line_sw = 0;
200 }
201 else report(infile_err,"",path);
202 }
203 }
204 return; /* return to 'main' routine */
205 }
206
207
208 /* This function actually does the validation on the named file.
209 * It determines whether the file is an SCCS-file or if the file
210 * exists. It also determines if the values given for type, SID,
211 * and name match those in the named file. An error code is returned
212 * if any mismatch occurs. See VAL(I) for more information.
213 */
validate(c_path,c_sid,c_type,c_name)214 validate(c_path,c_sid,c_type,c_name)
215 char *c_path;
216 char *c_sid;
217 char *c_type;
218 char *c_name;
219 {
220 register char *l;
221 int goods,goodt,goodn,hadmflag;
222
223 infile_err = goods = goodt = goodn = hadmflag = 0;
224 sinit(&gpkt,c_path);
225 if (!sccsfile(c_path) || (gpkt.p_iop = fopen(c_path,"r")) == NULL)
226 infile_err |= FILENAM_ERR;
227 else {
228 l = get_line(&gpkt); /* read first line in file */
229 /*
230 check that it is header line.
231 */
232 if (*l++ != CTLCHAR || *l++ != HEAD)
233 infile_err |= CORRUPT_ERR;
234
235 else {
236 /*
237 get old file checksum count
238 */
239 satoi(l,&gpkt.p_ihash);
240 gpkt.p_chash = 0;
241 if (HADR)
242 /*
243 check for invalid or ambiguous SID.
244 */
245 if (invalid(c_sid))
246 infile_err |= INVALSID_ERR;
247 /*
248 read delta table checking for errors and/or
249 SID.
250 */
251 if (do_delt(&gpkt,goods,c_sid)) {
252 fclose(gpkt.p_iop);
253 infile_err |= CORRUPT_ERR;
254 return;
255 }
256
257 read_to(EUSERNAM,&gpkt);
258
259 if (HADY || HADM) {
260 /*
261 read flag section of delta table.
262 */
263 while ((l = get_line(&gpkt)) &&
264 *l++ == CTLCHAR &&
265 *l++ == FLAG) {
266 NONBLANK(l);
267 repl(l,'\n','\0');
268 if (*l == TYPEFLAG) {
269 l += 2;
270 if (equal(c_type,l))
271 goodt++;
272 }
273 else if (*l == MODFLAG) {
274 hadmflag++;
275 l += 2;
276 if (equal(c_name,l))
277 goodn++;
278 }
279 }
280 if (*(--l) != BUSERTXT) {
281 fclose(gpkt.p_iop);
282 infile_err |= CORRUPT_ERR;
283 return;
284 }
285 /*
286 check if 'y' flag matched '-y' arg value.
287 */
288 if (!goodt && HADY)
289 infile_err |= TYPE_ERR;
290 /*
291 check if 'm' flag matched '-m' arg value.
292 */
293 if (HADM && !hadmflag) {
294 if (!equal(auxf(sname(c_path),'g'),c_name))
295 infile_err |= NAME_ERR;
296 }
297 else if (HADM && hadmflag && !goodn)
298 infile_err |= NAME_ERR;
299 }
300 else read_to(BUSERTXT,&gpkt);
301 read_to(EUSERTXT,&gpkt);
302 gpkt.p_chkeof = 1;
303 /*
304 read remainder of file so 'read_mod'
305 can check for corruptness.
306 */
307 while (read_mod(&gpkt))
308 ;
309 }
310 fclose(gpkt.p_iop); /* close file pointer */
311 }
312 return; /* return to 'process' function */
313 }
314
315
316 /* This function reads the 'delta' line from the named file and stores
317 * the information into the structure 'del'.
318 */
getdel(delp,lp)319 getdel(delp,lp)
320 register struct delent *delp;
321 register char *lp;
322 {
323 NONBLANK(lp);
324 delp->type = *lp++;
325 NONBLANK(lp);
326 delp->osid = lp;
327 BLANK(lp);
328 *lp++ = '\0';
329 NONBLANK(lp);
330 delp->datetime = lp;
331 BLANK(lp);
332 NONBLANK(lp);
333 BLANK(lp);
334 *lp++ = '\0';
335 NONBLANK(lp);
336 delp->pgmr = lp;
337 BLANK(lp);
338 *lp++ = '\0';
339 NONBLANK(lp);
340 delp->serial = lp;
341 BLANK(lp);
342 *lp++ = '\0';
343 NONBLANK(lp);
344 delp->pred = lp;
345 repl(lp,'\n','\0');
346 }
347
348
349 /* This function does a read through the named file until it finds
350 * the character sent over as an argument.
351 */
read_to(ch,pkt)352 read_to(ch,pkt)
353 register char ch;
354 register struct packet *pkt;
355 {
356 register char *n;
357 while ((n = get_line(pkt)) &&
358 !(*n++ == CTLCHAR && *n == ch))
359 ;
360 return;
361 }
362
363
364 /* This function places into a specified destination characters which
365 * are delimited by either a space, tab or 0. It obtains the char-
366 * acters from a line of characters.
367 */
getval(sourcep,destp)368 char *getval(sourcep,destp)
369 register char *sourcep;
370 register char *destp;
371 {
372 while (*sourcep != ' ' && *sourcep != '\t' && *sourcep != '\0')
373 *destp++ = *sourcep++;
374 *destp = 0;
375 return(sourcep);
376 }
377
378
379 /* This function will report the error that occured on the command
380 * line. It will print one diagnostic message for each error that
381 * was found in the named file.
382 */
report(code,inp_line,file)383 report(code,inp_line,file)
384 register int code;
385 register char *inp_line;
386 register char *file;
387 {
388 char percent;
389 percent = '%'; /* '%' for -m and/or -y messages */
390 if (*inp_line)
391 printf("%s\n\n",inp_line);
392 if (code & NAME_ERR)
393 printf(" %s: %cM%c, -m mismatch\n",file,percent,percent);
394 if (code & TYPE_ERR)
395 printf(" %s: %cY%c, -y mismatch\n",file,percent,percent);
396 if (code & NONEXSID_ERR)
397 printf(" %s: SID nonexistent\n",file);
398 if (code & INVALSID_ERR)
399 printf(" %s: SID invalid or ambiguous\n",file);
400 if (code & FILENAM_ERR)
401 printf(" %s: can't open file or file not SCCS\n",file);
402 if (code & CORRUPT_ERR)
403 printf(" %s: corrupted SCCS file\n",file);
404 if (code & UNKDUP_ERR)
405 printf(" %s: Unknown or dupilcate keyletter argument\n",file);
406 if (code & FILARG_ERR)
407 printf(" %s: missing file argument\n",file);
408 return;
409 }
410
411
412 /* This function takes as it's argument the SID inputed and determines
413 * whether or not it is valid (e. g. not ambiguous or illegal).
414 */
invalid(i_sid)415 invalid(i_sid)
416 register char *i_sid;
417 {
418 register int count;
419 register int digits;
420 count = digits = 0;
421 if (*i_sid == '0' || *i_sid == '.')
422 return (1);
423 i_sid++;
424 digits++;
425 while (*i_sid != '\0') {
426 if (*i_sid++ == '.') {
427 digits = 0;
428 count++;
429 if (*i_sid == '0' || *i_sid == '.')
430 return (1);
431 }
432 digits++;
433 if (digits > 5)
434 return (1);
435 }
436 if (*(--i_sid) == '.' )
437 return (1);
438 if (count == 1 || count == 3)
439 return (0);
440 return (1);
441 }
442
443
444 /*
445 Routine to read a line into the packet. The main reason for
446 it is to make sure that pkt->p_wrttn gets turned off,
447 and to increment pkt->p_slnno.
448 */
449
get_line(pkt)450 char *get_line(pkt)
451 register struct packet *pkt;
452 {
453 register char *n;
454 register char *p;
455
456 if ((n = fgets(pkt->p_line,sizeof(pkt->p_line),pkt->p_iop)) != NULL) {
457 pkt->p_slnno++;
458 for (p = pkt->p_line; *p; )
459 pkt->p_chash += *p++;
460 }
461 else {
462 if (!pkt->p_chkeof)
463 infile_err |= CORRUPT_ERR;
464 if (pkt->do_chksum && (pkt->p_chash ^ pkt->p_ihash)&0xFFFF)
465 infile_err |= CORRUPT_ERR;
466 }
467 return(n);
468 }
469
470
471 /*
472 Does initialization for sccs files and packet.
473 */
474
sinit(pkt,file)475 sinit(pkt,file)
476 register struct packet *pkt;
477 register char *file;
478 {
479
480 bzero(pkt,sizeof(*pkt));
481 copy(file,pkt->p_file);
482 pkt->p_wrttn = 1;
483 pkt->do_chksum = 1; /* turn on checksum check for getline */
484 }
485
486
read_mod(pkt)487 read_mod(pkt)
488 register struct packet *pkt;
489 {
490 register char *p;
491 int ser;
492 int iord;
493 register struct apply *ap;
494
495 while (get_line(pkt) != NULL) {
496 p = pkt->p_line;
497 if (*p++ != CTLCHAR)
498 continue;
499 else {
500 if (!((iord = *p++) == INS || iord == DEL || iord == END)) {
501 infile_err |= CORRUPT_ERR;
502 return(0);
503 }
504 NONBLANK(p);
505 satoi(p,&ser);
506 if (iord == END)
507 remq(pkt,ser);
508 else if ((ap = &pkt->p_apply[ser])->a_code == APPLY)
509 addq(pkt,ser,iord == INS ? YES : NO,iord,ap->a_reason & USER);
510 else
511 addq(pkt,ser,iord == INS ? NO : NULL,iord,ap->a_reason & USER);
512 }
513 }
514 if (pkt->p_q)
515 infile_err |= CORRUPT_ERR;
516 return(0);
517 }
518
519
520 addq(pkt,ser,keep,iord,user)
521 struct packet *pkt;
522 int ser;
523 int keep;
524 int iord;
525 {
526 register struct queue *cur, *prev, *q;
527
528 for (cur = &pkt->p_q; cur = (prev = cur)->q_next; )
529 if (cur->q_sernum <= ser)
530 break;
531 if (cur->q_sernum == ser)
532 infile_err |= CORRUPT_ERR;
533 prev->q_next = q = alloc(sizeof(*q));
534 q->q_next = cur;
535 q->q_sernum = ser;
536 q->q_keep = keep;
537 q->q_iord = iord;
538 q->q_user = user;
539 if (pkt->p_ixuser && (q->q_ixmsg = chkix(q,&pkt->p_q)))
540 ++(pkt->p_ixmsg);
541 else
542 q->q_ixmsg = 0;
543
544 setkeep(pkt);
545 }
546
547
remq(pkt,ser)548 remq(pkt,ser)
549 register struct packet *pkt;
550 int ser;
551 {
552 register struct queue *cur, *prev;
553
554 for (cur = &pkt->p_q; cur = (prev = cur)->q_next; )
555 if (cur->q_sernum == ser)
556 break;
557 if (cur) {
558 if (cur->q_ixmsg)
559 --(pkt->p_ixmsg);
560 prev->q_next = cur->q_next;
561 free(cur);
562 setkeep(pkt);
563 }
564 else
565 infile_err |= CORRUPT_ERR;
566 }
567
568
setkeep(pkt)569 setkeep(pkt)
570 register struct packet *pkt;
571 {
572 register struct queue *q;
573 register struct sid *sp;
574
575 for (q = &pkt->p_q; q = q->q_next; )
576 if (q->q_keep != NULL) {
577 if ((pkt->p_keep = q->q_keep) == YES) {
578 sp = &pkt->p_idel[q->q_sernum].i_sid;
579 pkt->p_inssid.s_rel = sp->s_rel;
580 pkt->p_inssid.s_lev = sp->s_lev;
581 pkt->p_inssid.s_br = sp->s_br;
582 pkt->p_inssid.s_seq = sp->s_seq;
583 }
584 return;
585 }
586 pkt->p_keep = NO;
587 }
588
589
590 # define apply(qp) ((qp->q_iord == INS && qp->q_keep == YES) || (qp->q_iord == DEL && qp->q_keep == NO))
591
chkix(new,head)592 chkix(new,head)
593 register struct queue *new;
594 struct queue *head;
595 {
596 register int retval;
597 register struct queue *cur;
598 int firstins, lastdel;
599
600 if (!apply(new))
601 return(0);
602 for (cur = head; cur = cur->q_next; )
603 if (cur->q_user)
604 break;
605 if (!cur)
606 return(0);
607 retval = 0;
608 firstins = 0;
609 lastdel = 0;
610 for (cur = head; cur = cur->q_next; ) {
611 if (apply(cur)) {
612 if (cur->q_iord == DEL)
613 lastdel = cur->q_sernum;
614 else if (firstins == 0)
615 firstins = cur->q_sernum;
616 }
617 else if (cur->q_iord == INS)
618 retval++;
619 }
620 if (retval == 0) {
621 if (lastdel && (new->q_sernum > lastdel))
622 retval++;
623 if (firstins && (new->q_sernum < firstins))
624 retval++;
625 }
626 return(retval);
627 }
628
629
630 /* This function reads the delta table entries and checks for the format
631 * as specifed in sccsfile(V). If the format is incorrect, a corrupt
632 * error will be issued by 'val'. This function also checks
633 * if the sid requested is in the file (depending if '-r' was specified).
634 */
do_delt(pkt,goods,d_sid)635 do_delt(pkt,goods,d_sid)
636 register struct packet *pkt;
637 register int goods;
638 register char *d_sid;
639 {
640 char *l;
641
642 while(getstats(pkt)) {
643 if ((l = get_line(pkt)) && *l++ != CTLCHAR || *l++ != BDELTAB)
644 return(1);
645 if (HADR && !(infile_err & INVALSID_ERR)) {
646 getdel(&del,l);
647 if (equal(d_sid,del.osid) && del.type == 'D')
648 goods++;
649 }
650 while ((l = get_line(pkt)) != NULL)
651 if (pkt->p_line[0] != CTLCHAR)
652 break;
653 else {
654 switch(pkt->p_line[1]) {
655 case EDELTAB:
656 break;
657 case COMMENTS:
658 case MRNUM:
659 case INCLUDE:
660 case EXCLUDE:
661 case IGNORE:
662 continue;
663 default:
664 return(1);
665 }
666 break;
667 }
668 if (l == NULL || pkt->p_line[0] != CTLCHAR)
669 return(1);
670 }
671 if (pkt->p_line[1] != BUSERNAM)
672 return(1);
673 if (HADR && !goods && !(infile_err & INVALSID_ERR))
674 infile_err |= NONEXSID_ERR;
675 return(0);
676 }
677
678
679 /* This function reads the stats line from the sccsfile */
getstats(pkt)680 getstats(pkt)
681 register struct packet *pkt;
682 {
683 register char *p;
684 p = pkt->p_line;
685 if (get_line(pkt) == NULL || *p++ != CTLCHAR || *p != STATS)
686 return(0);
687 return(1);
688 }
689