Commit | Line | Data |
---|---|---|
89deffda EA |
1 | # include "stdio.h" |
2 | # include "seven/types.h" | |
3 | # include "seven/macros.h" | |
4 | # include "fatal.h" | |
5 | ||
6 | SCCSID(@(#)vc.c 4.1); | |
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 | } |