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 SCCSID(@(#)val.c	4.2);
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 
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 
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 
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 
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 
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 
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 */
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 */
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