1 # include	"stdio.h"
2 # include	"seven/types.h"
3 # include	"seven/macros.h"
4 # include	"fatal.h"
5 
6 SCCSID(@(#)vc.c	4.2);
7 USXALLOC();
8 
9 /*
10  * The symbol table size is set to a limit of forty keywords per input
11  * file.  Should this limit be changed it should also be changed in the
12  * Help file.
13  */
14 
15 # define SYMSIZE 40
16 # define PARMSIZE 10
17 # define NSLOTS 32
18 
19 # define USD  1
20 # define DCL 2
21 # define ASG 4
22 
23 # define EQ '='
24 # define NEQ '!'
25 # define GT '>'
26 # define LT '<'
27 # define DELIM " \t"
28 # define TRUE 1
29 # define FALSE 0
30 
31 char	Ctlchar	':';
32 
33 struct	symtab	{
34 	int	usage;
35 	char	name[PARMSIZE];
36 	char	*value;
37 	int	lenval;
38 };
39 struct	symtab	Sym[SYMSIZE];
40 
41 struct {
42 	char	chr;
43 };
44 
45 int	Skiptabs;
46 int	Repall;
47 
48 /*
49  * Delflag is used to indicate when text is to be skipped.  It is decre-
50  * mented whenever an if condition is false, or when an if occurs
51  * within a false if/end statement.  It is decremented whenever an end is
52  * encountered and the Delflag is greater than zero.  Whenever Delflag
53  * is greater than zero text is skipped.
54  */
55 
56 int	Delflag;
57 
58 /*
59  * Ifcount keeps track of the number of ifs and ends.  Each time
60  * an if is encountered Ifcount is incremented and each time an end is
61  * encountered it is decremented.
62  */
63 
64 int	Ifcount;
65 int	Lineno;
66 
67 char	*Repflag;
68 char	*Linend;
69 int	Silent;
70 
71 
72 /*
73  * The main program reads a line of text and sends it to be processed
74  * if it is a version control statement. If it is a line of text and
75  * the Delflag is equal to zero, it is written to the standard output.
76  */
77 
78 main(argc, argv)
79 int argc;
80 char *argv[];
81 {
82 	register  char *lineptr, *p;
83 	register int i;
84 	char line[512];
85 	extern int Fflags;
86 
87 	Fflags = FTLCLN | FTLMSG | FTLEXIT;
88 	setsig();
89 	for(i = 1; i< argc; i++) {
90 		p = argv[i];
91 		if (p[0] == '-')
92 			switch (p[1]) {
93 			case 's':
94 				Silent = 1;
95 				break;
96 			case 't':
97 				Skiptabs = 1;
98 				break;
99 			case 'a':
100 				Repall = 1;
101 				break;
102 			case 'c':
103 				Ctlchar = p[2];
104 				break;
105 			}
106 		else {
107 			p[size(p) - 1] = '\n';
108 			asgfunc(p);
109 		}
110 	}
111 	while (fgets(line,sizeof(line),stdin) != NULL) {
112 		lineptr = line;
113 		Lineno++;
114 
115 		if (Repflag != 0) {
116 			free(Repflag);
117 			Repflag = 0;
118 		}
119 
120 		if (Skiptabs) {
121 			for (p = lineptr; *p; p++)
122 				if (*p == '\t')
123 					break;
124 			if (*p++ == '\t')
125 				lineptr = p;
126 		}
127 
128 		if (lineptr[0] != Ctlchar) {
129 			if (lineptr[0] == '\\' && lineptr[1] == Ctlchar)
130 				for (p = &lineptr[1]; *lineptr++ = *p++; )
131 					;
132 			if(Delflag == 0) {
133 				if (Repall)
134 					repfunc(line);
135 				else
136 					fputs(line,stdout);
137 			}
138 			continue;
139 		}
140 
141 		lineptr++;
142 
143 		if (imatch("if ", lineptr))
144 			iffunc(&lineptr[3]);
145 		else if (imatch("end", lineptr))
146 			endfunc();
147 		else if (Delflag == 0) {
148 			if (imatch("asg ", lineptr))
149 				asgfunc(&lineptr[4]);
150 			else if (imatch("dcl ", lineptr))
151 				dclfunc(&lineptr[4]);
152 			else if (imatch("err", lineptr))
153 				errfunc(&lineptr[3]);
154 			else if (imatch("msg", lineptr))
155 				msgfunc(&lineptr[3]);
156 			else if (lineptr[0] == Ctlchar)
157 				repfunc(&lineptr[1]);
158 			else if (imatch("on", lineptr))
159 				Repall = 1;
160 			else if (imatch("off", lineptr))
161 				Repall = 0;
162 			else if (imatch("ctl ", lineptr))
163 				Ctlchar = lineptr[4];
164 			else error("unknown command on line %d (901)",Lineno);
165 		}
166 	}
167 	for(i = 0; Sym[i].usage != 0 && i<SYMSIZE; i++) {
168 		if ((Sym[i].usage&USD) == 0)
169 			warn("`%s' never used (902)\n",Sym[i].name);
170 		if ((Sym[i].usage&DCL) == 0)
171 			warn("`%s' never declared (903)\n", Sym[i].name);
172 		if ((Sym[i].usage&ASG) == 0)
173 			warn("`%s' never assigned a value (920)\n", Sym[i].name);
174 	}
175 	if (Ifcount > 0)
176 		error("`if' with no matching `end' (904)");
177 	exit(0);
178 }
179 
180 
181 /*
182  * Asgfunc accepts a pointer to a line picks up a keyword name, an
183  * equal sign and a value and calls putin to place it in the symbol table.
184  */
185 
186 asgfunc(aptr)
187 register char *aptr;
188 {
189 	register char *end, *aname;
190 	char *avalue;
191 
192 	aptr = replace(aptr);
193 	NONBLANK(aptr);
194 	aname = aptr;
195 	end = Linend;
196 	aptr = findstr(aptr,"= \t");
197 	if (*aptr == ' ' || *aptr == '\t') {
198 		*aptr++ = '\0';
199 		aptr = findch(aptr,'=');
200 	}
201 	if (aptr == end)
202 		error("syntax on line %d (917)",Lineno);
203 	*aptr++ = '\0';
204 	avalue = getid(aptr);
205 	chksize(aname);
206 	putin(aname, avalue);
207 }
208 
209 
210 /*
211  * Dclfunc accepts a pointer to a line and picks up keywords
212  * separated by commas.  It calls putin to put each keyword in the
213  * symbol table.  It returns when it sees a newline.
214  */
215 
216 dclfunc(dptr)
217 register char *dptr;
218 {
219 	register char *end, *dname;
220 	int i;
221 
222 	dptr = replace(dptr);
223 	end = Linend;
224 	NONBLANK(dptr);
225 	while (dptr < end) {
226 		dname = dptr;
227 		dptr = findch(dptr,',');
228 		*dptr++ = '\0';
229 		chksize(dname);
230 		if (Sym[i = lookup(dname)].usage&DCL)
231 			error("`%s' declared twice on line %d (905)",
232 				dname, Lineno);
233 		else
234 			Sym[i].usage |= DCL;
235 		NONBLANK(dptr);
236 	}
237 }
238 
239 
240 /*
241  * Errfunc calls fatal which stops the process.
242  */
243 
244 errfunc(eptr)
245 char *eptr;
246 {
247 	warn("ERROR:%s\n",replace(eptr));
248 	error("err statement on line %d (915)", Lineno);
249 }
250 
251 
252 /*
253  * Endfunc indicates an end has been found by decrementing the if count
254  * flag.  If because of a previous if statement, text was being skipped,
255  * Delflag is also decremented.
256  */
257 
258 endfunc()
259 {
260 	if (--Ifcount < 0)
261 		error("`end' without matching `if' on line %d (910)",Lineno);
262 	if (Delflag > 0)
263 		Delflag--;
264 	return;
265 }
266 
267 
268 /*
269  * Msgfunc accepts a pointer to a line and prints that line on the
270  * diagnostic output.
271  */
272 
273 msgfunc(mptr)
274 char *mptr;
275 {
276 	warn("Message(%d):%s\n", Lineno, replace(mptr));
277 }
278 
279 
280 repfunc(s)
281 char *s;
282 {
283 	fprintf(stdout,"%s\n",replace(s));
284 }
285 
286 
287 /*
288  * Iffunc and the three functions following it, door, doand, and exp
289  * are responsible for parsing and interperting the condition in the
290  * if statement.  The BNF used is as follows:
291  *	<iffunc> ::=   [ "not" ] <door> EOL
292  *	<door> ::=     <doand> | <doand> "|" <door>
293  *	<doand>::=     <exp> | <exp> "&" <doand>
294  *	<exp>::=       "(" <door> ")" | <value> <operator> <value>
295  *	<operator>::=  "=" | "!=" | "<" | ">"
296  * And has precedence over or.  If the condition is false the Delflag
297  * is bumped to indicate that lines are to be skipped.
298  * An external variable, sptr is used for processing the line in
299  * iffunc, door, doand, exp, getid.
300  * Iffunc accepts a pointer to a line and sets sptr to that line.  The
301  * rest of iffunc, door, and doand follow the BNF exactly.
302  */
303 
304 char *sptr;
305 
306 iffunc(iptr)
307 char *iptr;
308 {
309 	register int value, not;
310 
311 	Ifcount++;
312 	if (Delflag > 0)
313 		Delflag++;
314 
315 	else {
316 		sptr = replace(iptr);
317 		NONBLANK(sptr);
318 		if (imatch("not ", sptr)) {
319 			not = FALSE;
320 			sptr += 4;
321 		}
322 		else not = TRUE;
323 
324 		value = door();
325 		if( *sptr != 0)
326 			error("syntax on line %d (918)",Lineno);
327 
328 		if (value != not)
329 			Delflag++;
330 	}
331 
332 	return;
333 }
334 
335 
336 door()
337 {
338 	int value;
339 	value = doand();
340 	NONBLANK(sptr);
341 	while (*sptr=='|') {
342 		sptr++;
343 		value |= doand();
344 		NONBLANK(sptr);
345 	}
346 	return(value);
347 }
348 
349 
350 doand()
351 {
352 	int value;
353 	value = exp();
354 	NONBLANK(sptr);
355 	while (*sptr=='&') {
356 		sptr++;
357 		value &= exp();
358 		NONBLANK(sptr);
359 	}
360 	return(value);
361 }
362 
363 
364 /*
365  * After exp checks for parentheses, it picks up a value by calling getid,
366  * picks up an operator and calls getid to pick up the second value.
367  * Then based on the operator it calls either numcomp or equal to see
368  * if the exp is true or false and returns the correct value.
369  */
370 
371 exp()
372 {
373 	register char op, save;
374 	register int value;
375 	char *id1, *id2, next;
376 
377 	NONBLANK(sptr);
378 	if(*sptr == '(') {
379 		sptr++;
380 		value = door();
381 		NONBLANK(sptr);
382 		if (*sptr == ')') {
383 			sptr++;
384 			return(value);
385 		}
386 		else error("parenthesis error on line %d (911)",Lineno);
387 	}
388 
389 	id1 = getid(sptr);
390 	if (op = *sptr)
391 		*sptr++ = '\0';
392 	if (op == NEQ && (next = *sptr++) == '\0')
393 		--sptr;
394 	id2 = getid(sptr);
395 	save = *sptr;
396 	*sptr = '\0';
397 
398 	if(op ==LT || op == GT) {
399 		value = numcomp(id1, id2);
400 		if ((op == GT && value == 1) || (op == LT && value == -1))
401 			value = TRUE;
402 		else value = FALSE;
403 	}
404 
405 	else if (op==EQ || (op==NEQ && next==EQ)) {
406 		value = equal(id1, id2);
407 		if ( op == NEQ)
408 			value = !value;
409 	}
410 
411 	else error("invalid operator on line %d (912)", Lineno);
412 	*sptr = save;
413 	return(value);
414 }
415 
416 
417 /*
418  * Getid picks up a value off a line and returns a pointer to the value.
419  */
420 
421 getid(gptr)
422 register char *gptr;
423 {
424 	register char c, *id;
425 
426 	NONBLANK(gptr);
427 	id = gptr;
428 	gptr = findstr(gptr,DELIM);
429 	if (*gptr)
430 		*gptr++ = '\0';
431 	NONBLANK(gptr);
432 	sptr = gptr;
433 	return(id);
434 }
435 
436 
437 /*
438  * Numcomp accepts two pointers to strings of digits and calls numck
439  * to see if the strings contain only digits.  It returns -1 if
440  * the first is less than the second, 1 if the first is greater than the
441  * second and 0 if the two are equal.
442  */
443 
444 numcomp(id1, id2)
445 register char *id1, *id2;
446 {
447 	int k1, k2;
448 
449 	numck(id1);
450 	numck(id2);
451 	while (*id1 == '0')
452 		id1++;
453 	while (*id2 == '0')
454 		id2++;
455 	if ((k1 = size(id1)) > (k2 = size(id2)))
456 		return(1);
457 	else if (k1 < k2)
458 		return(-1);
459 	else while(*id1 != '\0') {
460 		if(*id1 > *id2)
461 			return(1);
462 		else if(*id1 < *id2)
463 			return(-1);
464 		id1++;
465 		id2++;
466 	}
467 	return(0);
468 }
469 
470 
471 /*
472  * Numck accepts a pointer to a string and checks to see if they are
473  * all digits.  If they're not it calls fatal, otherwise it returns.
474  */
475 
476 numck(nptr)
477 register char *nptr;
478 {
479 	for (; *nptr != '\0'; nptr++)
480 		if (!numeric(*nptr))
481 			error("non-numerical value on line %d (914)",Lineno);
482 	return;
483 }
484 
485 
486 /*
487  * Replace accepts a pointer to a line and scans the line for a keyword
488  * enclosed in control characters.  If it doesn't find one it returns
489  * a pointer to the begining of the line.  Otherwise, it calls
490  * lookup to find the keyword.
491  * It rewrites the line substituting the value for the
492  * keyword enclosed in control characters.  It then continues scanning
493  * the line until no control characters are found and returns a pointer to
494  * the begining of the new line.
495  */
496 
497 # define INCR(int) if (++int==NSLOTS) error(subrng,Lineno)
498 char *subrng "out of space [line %d] (916)";
499 
500 replace(ptr)
501 char *ptr;
502 {
503 	char *slots[NSLOTS];
504 	int i,j,newlen;
505 	register char *s, *t, *p;
506 
507 	for (s=ptr; *s++!='\n';);
508 	*(--s) = '\0';
509 	Linend = s;
510 	i = -1;
511 	for (p=ptr; *(s=findch(p,Ctlchar)); p=t) {
512 		*s++ = '\0';
513 		INCR(i);
514 		slots[i] = p;
515 		if (*(t=findch(s,Ctlchar))==0)
516 			error("unmatched `%c' on line %d (907)",Ctlchar,Lineno);
517 		*t++ = '\0';
518 		INCR(i);
519 		slots[i] = Sym[j = lookup(s)].value;
520 		Sym[j].usage |= USD;
521 	}
522 	INCR(i);
523 	slots[i] = p;
524 	if (i==0) return(ptr);
525 	newlen = 0;
526 	for (j=0; j<=i; j++)
527 		newlen += (size(slots[j])-1);
528 	t = Repflag = alloc(++newlen);
529 	for (j=0; j<=i; j++)
530 		t = ecopy(slots[j],t);
531 	Linend = t;
532 	return(Repflag);
533 }
534 
535 
536 /*
537  * Lookup accepts a pointer to a keyword name and searches the symbol
538  * table for the keyword.  It returns its index in the table if its there,
539  * otherwise it puts the keyword in the table.
540  */
541 
542 lookup(lname)
543 char *lname;
544 {
545 	register int i;
546 	register char *t;
547 	register struct symtab *s;
548 
549 	t = lname;
550 	while ((i.chr = *t++) &&
551 		((i.chr>='A' && i.chr<='Z') || (i.chr>='a' && i.chr<='z') ||
552 			(i.chr!= *lname && i.chr>='0' && i.chr<='9')));
553 	if (i.chr)
554 		error("invalid keyword name on line %d (909)",Lineno);
555 
556 	for(i =0; Sym[i].usage != 0 && i<SYMSIZE; i++)
557 		if (equal(lname, Sym[i].name)) return(i);
558 	s = &Sym[i];
559 	if (s->usage == 0) {
560 		copy(lname,s->name);
561 		copy("",(s->value = alloc(s->lenval = 1)));
562 		return(i);
563 	}
564 	error("out of space (906)");
565 }
566 
567 
568 /*
569  * Putin accepts a pointer to a keyword name, and a pointer to a value.
570  * It puts this information in the symbol table by calling lookup.
571  * It returns the index of the name in the table.
572  */
573 
574 putin(pname, pvalue)
575 char *pname;
576 char *pvalue;
577 {
578 	register int i;
579 	register struct symtab *s;
580 
581 	s = &Sym[i = lookup(pname)];
582 	free(s->value);
583 	s->lenval = size(pvalue);
584 	copy(pvalue, (s->value = alloc(s->lenval)));
585 	s->usage |= ASG;
586 	return(i);
587 }
588 
589 
590 chksize(s)
591 char *s;
592 {
593 	if (size(s) > PARMSIZE)
594 		error("keyword name too long on line %d (908)",Lineno);
595 }
596 
597 
598 findch(astr,match)
599 char *astr, match;
600 {
601 	register char *s, *t, c;
602 	char *temp;
603 
604 	for (s=astr; (c = *s) && c!=match; s++)
605 		if (c=='\\') {
606 			if (s[1]==0)
607 				error("syntax on line %d (919)",Lineno);
608 			else {
609 				for (t = (temp=s) + 1; *s++ = *t++;);
610 				s = temp;
611 			}
612 		}
613 	return(s);
614 }
615 
616 
617 ecopy(s1,s2)
618 char *s1, *s2;
619 {
620 	register char *r1, *r2;
621 
622 	r1 = s1;
623 	r2 = s2;
624 	while (*r2++ = *r1++);
625 	return(--r2);
626 }
627 
628 
629 error(arg)
630 {
631 	fatal(sprintf(Error,"%r",&arg));
632 }
633 
634 
635 findstr(astr,pat)
636 char *astr, *pat;
637 {
638 	register char *s, *t, c;
639 	char *temp;
640 
641 	for (s=astr; (c = *s) && any(c,pat)==0; s++)
642 		if (c=='\\') {
643 			if (s[1]==0)
644 				error("syntax on line %d (919)",Lineno);
645 			else {
646 				for (t = (temp=s) + 1; *s++ = *t++;);
647 				s = temp;
648 			}
649 		}
650 	return(s);
651 }
652 
653 
654 warn(arg)
655 {
656 	if (!Silent)
657 		fprintf(stderr,"%r",&arg);
658 }
659