modern syntax for asgops & inits cause Donn's latest ccom rejects the old.
[unix-history] / usr / src / local / sccscmds / sccscmds.ok / cmd / vc.c
CommitLineData
89deffda
EA
1# include "stdio.h"
2# include "seven/types.h"
3# include "seven/macros.h"
4# include "fatal.h"
5
6SCCSID(@(#)vc.c 4.1);
7USXALLOC();
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
31char Ctlchar ':';
32
33struct symtab {
34 int usage;
35 char name[PARMSIZE];
36 char *value;
37 int lenval;
38};
39struct symtab Sym[SYMSIZE];
40
41struct {
42 char chr;
43};
44
45int Skiptabs;
46int 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
56int 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
64int Ifcount;
65int Lineno;
66
67char *Repflag;
68char *Linend;
69int 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
78main(argc, argv)
79int argc;
80char *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
186asgfunc(aptr)
187register 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
216dclfunc(dptr)
217register 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
244errfunc(eptr)
245char *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
258endfunc()
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
273msgfunc(mptr)
274char *mptr;
275{
276 warn("Message(%d):%s\n", Lineno, replace(mptr));
277}
278
279
280repfunc(s)
281char *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
304char *sptr;
305
306iffunc(iptr)
307char *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
336door()
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
350doand()
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
371exp()
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
421getid(gptr)
422register 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
444numcomp(id1, id2)
445register 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
476numck(nptr)
477register 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)
498char *subrng "out of space [line %d] (916)";
499
500replace(ptr)
501char *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
542lookup(lname)
543char *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
574putin(pname, pvalue)
575char *pname;
576char *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
590chksize(s)
591char *s;
592{
593 if (size(s) > PARMSIZE)
594 error("keyword name too long on line %d (908)",Lineno);
595}
596
597
598findch(astr,match)
599char *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
617ecopy(s1,s2)
618char *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
629error(arg)
630{
631 fatal(sprintf(Error,"%r",&arg));
632}
633
634
635findstr(astr,pat)
636char *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
654warn(arg)
655{
656 if (!Silent)
657 fprintf(stderr,"%r",&arg);
658}