12155Seric # include "stdio.h"
22155Seric # include "seven/types.h"
32155Seric # include "seven/macros.h"
42155Seric # include "fatal.h"
52155Seric
6*33423Sbostic SCCSID(@(#)vc.c 4.3);
72155Seric USXALLOC();
82155Seric
92155Seric /*
102155Seric * The symbol table size is set to a limit of forty keywords per input
112155Seric * file. Should this limit be changed it should also be changed in the
122155Seric * Help file.
132155Seric */
142155Seric
152155Seric # define SYMSIZE 40
162155Seric # define PARMSIZE 10
172155Seric # define NSLOTS 32
182155Seric
192155Seric # define USD 1
202155Seric # define DCL 2
212155Seric # define ASG 4
222155Seric
232155Seric # define EQ '='
242155Seric # define NEQ '!'
252155Seric # define GT '>'
262155Seric # define LT '<'
272155Seric # define DELIM " \t"
282155Seric # define TRUE 1
292155Seric # define FALSE 0
302155Seric
312155Seric char Ctlchar ':';
322155Seric
332155Seric struct symtab {
342155Seric int usage;
352155Seric char name[PARMSIZE];
362155Seric char *value;
372155Seric int lenval;
382155Seric };
392155Seric struct symtab Sym[SYMSIZE];
402155Seric
412155Seric struct {
422155Seric char chr;
432155Seric };
442155Seric
452155Seric int Skiptabs;
462155Seric int Repall;
472155Seric
482155Seric /*
492155Seric * Delflag is used to indicate when text is to be skipped. It is decre-
502155Seric * mented whenever an if condition is false, or when an if occurs
512155Seric * within a false if/end statement. It is decremented whenever an end is
522155Seric * encountered and the Delflag is greater than zero. Whenever Delflag
532155Seric * is greater than zero text is skipped.
542155Seric */
552155Seric
562155Seric int Delflag;
572155Seric
582155Seric /*
592155Seric * Ifcount keeps track of the number of ifs and ends. Each time
602155Seric * an if is encountered Ifcount is incremented and each time an end is
612155Seric * encountered it is decremented.
622155Seric */
632155Seric
642155Seric int Ifcount;
652155Seric int Lineno;
662155Seric
672155Seric char *Repflag;
682155Seric char *Linend;
692155Seric int Silent;
702155Seric
712155Seric
722155Seric /*
732155Seric * The main program reads a line of text and sends it to be processed
742155Seric * if it is a version control statement. If it is a line of text and
752155Seric * the Delflag is equal to zero, it is written to the standard output.
762155Seric */
772155Seric
main(argc,argv)782155Seric main(argc, argv)
792155Seric int argc;
802155Seric char *argv[];
812155Seric {
822155Seric register char *lineptr, *p;
832155Seric register int i;
842155Seric char line[512];
852155Seric extern int Fflags;
862155Seric
872155Seric Fflags = FTLCLN | FTLMSG | FTLEXIT;
882155Seric setsig();
892155Seric for(i = 1; i< argc; i++) {
902155Seric p = argv[i];
912155Seric if (p[0] == '-')
922155Seric switch (p[1]) {
932155Seric case 's':
942155Seric Silent = 1;
952155Seric break;
962155Seric case 't':
972155Seric Skiptabs = 1;
982155Seric break;
992155Seric case 'a':
1002155Seric Repall = 1;
1012155Seric break;
1022155Seric case 'c':
1032155Seric Ctlchar = p[2];
1042155Seric break;
1052155Seric }
1062155Seric else {
1072155Seric p[size(p) - 1] = '\n';
1082155Seric asgfunc(p);
1092155Seric }
1102155Seric }
1112155Seric while (fgets(line,sizeof(line),stdin) != NULL) {
1122155Seric lineptr = line;
1132155Seric Lineno++;
1142155Seric
1152155Seric if (Repflag != 0) {
1162155Seric free(Repflag);
1172155Seric Repflag = 0;
1182155Seric }
1192155Seric
1202155Seric if (Skiptabs) {
1212155Seric for (p = lineptr; *p; p++)
1222155Seric if (*p == '\t')
1232155Seric break;
1242155Seric if (*p++ == '\t')
1252155Seric lineptr = p;
1262155Seric }
1272155Seric
1282155Seric if (lineptr[0] != Ctlchar) {
1292155Seric if (lineptr[0] == '\\' && lineptr[1] == Ctlchar)
1302155Seric for (p = &lineptr[1]; *lineptr++ = *p++; )
1312155Seric ;
1322155Seric if(Delflag == 0) {
1332155Seric if (Repall)
1342155Seric repfunc(line);
1352155Seric else
1362155Seric fputs(line,stdout);
1372155Seric }
1382155Seric continue;
1392155Seric }
1402155Seric
1412155Seric lineptr++;
1422155Seric
1432155Seric if (imatch("if ", lineptr))
1442155Seric iffunc(&lineptr[3]);
1452155Seric else if (imatch("end", lineptr))
1462155Seric endfunc();
1472155Seric else if (Delflag == 0) {
1482155Seric if (imatch("asg ", lineptr))
1492155Seric asgfunc(&lineptr[4]);
1502155Seric else if (imatch("dcl ", lineptr))
1512155Seric dclfunc(&lineptr[4]);
1522155Seric else if (imatch("err", lineptr))
1532155Seric errfunc(&lineptr[3]);
1542155Seric else if (imatch("msg", lineptr))
1552155Seric msgfunc(&lineptr[3]);
1562155Seric else if (lineptr[0] == Ctlchar)
1572155Seric repfunc(&lineptr[1]);
1582155Seric else if (imatch("on", lineptr))
1592155Seric Repall = 1;
1602155Seric else if (imatch("off", lineptr))
1612155Seric Repall = 0;
1622155Seric else if (imatch("ctl ", lineptr))
1632155Seric Ctlchar = lineptr[4];
1642155Seric else error("unknown command on line %d (901)",Lineno);
1652155Seric }
1662155Seric }
1672155Seric for(i = 0; Sym[i].usage != 0 && i<SYMSIZE; i++) {
1682155Seric if ((Sym[i].usage&USD) == 0)
1692155Seric warn("`%s' never used (902)\n",Sym[i].name);
1702155Seric if ((Sym[i].usage&DCL) == 0)
1712155Seric warn("`%s' never declared (903)\n", Sym[i].name);
1722155Seric if ((Sym[i].usage&ASG) == 0)
1732155Seric warn("`%s' never assigned a value (920)\n", Sym[i].name);
1742155Seric }
1752155Seric if (Ifcount > 0)
1762155Seric error("`if' with no matching `end' (904)");
1772155Seric exit(0);
1782155Seric }
1792155Seric
1802155Seric
1812155Seric /*
1822155Seric * Asgfunc accepts a pointer to a line picks up a keyword name, an
1832155Seric * equal sign and a value and calls putin to place it in the symbol table.
1842155Seric */
1852155Seric
asgfunc(aptr)1862155Seric asgfunc(aptr)
1872155Seric register char *aptr;
1882155Seric {
1892155Seric register char *end, *aname;
1902155Seric char *avalue;
1912155Seric
1922155Seric aptr = replace(aptr);
1932155Seric NONBLANK(aptr);
1942155Seric aname = aptr;
1952155Seric end = Linend;
1962155Seric aptr = findstr(aptr,"= \t");
1972155Seric if (*aptr == ' ' || *aptr == '\t') {
1982155Seric *aptr++ = '\0';
1992155Seric aptr = findch(aptr,'=');
2002155Seric }
2012155Seric if (aptr == end)
2022155Seric error("syntax on line %d (917)",Lineno);
2032155Seric *aptr++ = '\0';
2042155Seric avalue = getid(aptr);
2052155Seric chksize(aname);
2062155Seric putin(aname, avalue);
2072155Seric }
2082155Seric
2092155Seric
2102155Seric /*
2112155Seric * Dclfunc accepts a pointer to a line and picks up keywords
2122155Seric * separated by commas. It calls putin to put each keyword in the
2132155Seric * symbol table. It returns when it sees a newline.
2142155Seric */
2152155Seric
dclfunc(dptr)2162155Seric dclfunc(dptr)
2172155Seric register char *dptr;
2182155Seric {
2192155Seric register char *end, *dname;
2202155Seric int i;
2212155Seric
2222155Seric dptr = replace(dptr);
2232155Seric end = Linend;
2242155Seric NONBLANK(dptr);
2252155Seric while (dptr < end) {
2262155Seric dname = dptr;
2272155Seric dptr = findch(dptr,',');
2282155Seric *dptr++ = '\0';
2292155Seric chksize(dname);
2302155Seric if (Sym[i = lookup(dname)].usage&DCL)
2312155Seric error("`%s' declared twice on line %d (905)",
2322155Seric dname, Lineno);
2332155Seric else
23432774Sbostic Sym[i].usage |= DCL;
2352155Seric NONBLANK(dptr);
2362155Seric }
2372155Seric }
2382155Seric
2392155Seric
2402155Seric /*
2412155Seric * Errfunc calls fatal which stops the process.
2422155Seric */
2432155Seric
errfunc(eptr)2442155Seric errfunc(eptr)
2452155Seric char *eptr;
2462155Seric {
2472155Seric warn("ERROR:%s\n",replace(eptr));
2482155Seric error("err statement on line %d (915)", Lineno);
2492155Seric }
2502155Seric
2512155Seric
2522155Seric /*
2532155Seric * Endfunc indicates an end has been found by decrementing the if count
2542155Seric * flag. If because of a previous if statement, text was being skipped,
2552155Seric * Delflag is also decremented.
2562155Seric */
2572155Seric
endfunc()2582155Seric endfunc()
2592155Seric {
2602155Seric if (--Ifcount < 0)
2612155Seric error("`end' without matching `if' on line %d (910)",Lineno);
2622155Seric if (Delflag > 0)
2632155Seric Delflag--;
2642155Seric return;
2652155Seric }
2662155Seric
2672155Seric
2682155Seric /*
2692155Seric * Msgfunc accepts a pointer to a line and prints that line on the
2702155Seric * diagnostic output.
2712155Seric */
2722155Seric
msgfunc(mptr)2732155Seric msgfunc(mptr)
2742155Seric char *mptr;
2752155Seric {
2762155Seric warn("Message(%d):%s\n", Lineno, replace(mptr));
2772155Seric }
2782155Seric
2792155Seric
repfunc(s)2802155Seric repfunc(s)
2812155Seric char *s;
2822155Seric {
2832155Seric fprintf(stdout,"%s\n",replace(s));
2842155Seric }
2852155Seric
2862155Seric
2872155Seric /*
2882155Seric * Iffunc and the three functions following it, door, doand, and exp
2892155Seric * are responsible for parsing and interperting the condition in the
2902155Seric * if statement. The BNF used is as follows:
2912155Seric * <iffunc> ::= [ "not" ] <door> EOL
2922155Seric * <door> ::= <doand> | <doand> "|" <door>
2932155Seric * <doand>::= <exp> | <exp> "&" <doand>
2942155Seric * <exp>::= "(" <door> ")" | <value> <operator> <value>
2952155Seric * <operator>::= "=" | "!=" | "<" | ">"
2962155Seric * And has precedence over or. If the condition is false the Delflag
2972155Seric * is bumped to indicate that lines are to be skipped.
2982155Seric * An external variable, sptr is used for processing the line in
2992155Seric * iffunc, door, doand, exp, getid.
3002155Seric * Iffunc accepts a pointer to a line and sets sptr to that line. The
3012155Seric * rest of iffunc, door, and doand follow the BNF exactly.
3022155Seric */
3032155Seric
3042155Seric char *sptr;
3052155Seric
iffunc(iptr)3062155Seric iffunc(iptr)
3072155Seric char *iptr;
3082155Seric {
3092155Seric register int value, not;
3102155Seric
3112155Seric Ifcount++;
3122155Seric if (Delflag > 0)
3132155Seric Delflag++;
3142155Seric
3152155Seric else {
3162155Seric sptr = replace(iptr);
3172155Seric NONBLANK(sptr);
3182155Seric if (imatch("not ", sptr)) {
3192155Seric not = FALSE;
32032774Sbostic sptr += 4;
3212155Seric }
3222155Seric else not = TRUE;
3232155Seric
3242155Seric value = door();
3252155Seric if( *sptr != 0)
3262155Seric error("syntax on line %d (918)",Lineno);
3272155Seric
3282155Seric if (value != not)
3292155Seric Delflag++;
3302155Seric }
3312155Seric
3322155Seric return;
3332155Seric }
3342155Seric
3352155Seric
door()3362155Seric door()
3372155Seric {
3382155Seric int value;
3392155Seric value = doand();
3402155Seric NONBLANK(sptr);
3412155Seric while (*sptr=='|') {
3422155Seric sptr++;
34332774Sbostic value |= doand();
3442155Seric NONBLANK(sptr);
3452155Seric }
3462155Seric return(value);
3472155Seric }
3482155Seric
3492155Seric
doand()3502155Seric doand()
3512155Seric {
3522155Seric int value;
3532155Seric value = exp();
3542155Seric NONBLANK(sptr);
3552155Seric while (*sptr=='&') {
3562155Seric sptr++;
35732774Sbostic value &= exp();
3582155Seric NONBLANK(sptr);
3592155Seric }
3602155Seric return(value);
3612155Seric }
3622155Seric
3632155Seric
3642155Seric /*
3652155Seric * After exp checks for parentheses, it picks up a value by calling getid,
3662155Seric * picks up an operator and calls getid to pick up the second value.
3672155Seric * Then based on the operator it calls either numcomp or equal to see
3682155Seric * if the exp is true or false and returns the correct value.
3692155Seric */
3702155Seric
exp()3712155Seric exp()
3722155Seric {
3732155Seric register char op, save;
3742155Seric register int value;
3752155Seric char *id1, *id2, next;
3762155Seric
3772155Seric NONBLANK(sptr);
3782155Seric if(*sptr == '(') {
3792155Seric sptr++;
3802155Seric value = door();
3812155Seric NONBLANK(sptr);
3822155Seric if (*sptr == ')') {
3832155Seric sptr++;
3842155Seric return(value);
3852155Seric }
3862155Seric else error("parenthesis error on line %d (911)",Lineno);
3872155Seric }
3882155Seric
3892155Seric id1 = getid(sptr);
3902155Seric if (op = *sptr)
3912155Seric *sptr++ = '\0';
3922155Seric if (op == NEQ && (next = *sptr++) == '\0')
3932155Seric --sptr;
3942155Seric id2 = getid(sptr);
3952155Seric save = *sptr;
3962155Seric *sptr = '\0';
3972155Seric
3982155Seric if(op ==LT || op == GT) {
3992155Seric value = numcomp(id1, id2);
4002155Seric if ((op == GT && value == 1) || (op == LT && value == -1))
4012155Seric value = TRUE;
4022155Seric else value = FALSE;
4032155Seric }
4042155Seric
4052155Seric else if (op==EQ || (op==NEQ && next==EQ)) {
4062155Seric value = equal(id1, id2);
4072155Seric if ( op == NEQ)
4082155Seric value = !value;
4092155Seric }
4102155Seric
4112155Seric else error("invalid operator on line %d (912)", Lineno);
4122155Seric *sptr = save;
4132155Seric return(value);
4142155Seric }
4152155Seric
4162155Seric
4172155Seric /*
4182155Seric * Getid picks up a value off a line and returns a pointer to the value.
4192155Seric */
4202155Seric
getid(gptr)4212155Seric getid(gptr)
4222155Seric register char *gptr;
4232155Seric {
4242155Seric register char c, *id;
4252155Seric
4262155Seric NONBLANK(gptr);
4272155Seric id = gptr;
4282155Seric gptr = findstr(gptr,DELIM);
4292155Seric if (*gptr)
4302155Seric *gptr++ = '\0';
4312155Seric NONBLANK(gptr);
4322155Seric sptr = gptr;
4332155Seric return(id);
4342155Seric }
4352155Seric
4362155Seric
4372155Seric /*
4382155Seric * Numcomp accepts two pointers to strings of digits and calls numck
4392155Seric * to see if the strings contain only digits. It returns -1 if
4402155Seric * the first is less than the second, 1 if the first is greater than the
4412155Seric * second and 0 if the two are equal.
4422155Seric */
4432155Seric
numcomp(id1,id2)4442155Seric numcomp(id1, id2)
4452155Seric register char *id1, *id2;
4462155Seric {
4472155Seric int k1, k2;
4482155Seric
4492155Seric numck(id1);
4502155Seric numck(id2);
4512155Seric while (*id1 == '0')
4522155Seric id1++;
4532155Seric while (*id2 == '0')
4542155Seric id2++;
4552155Seric if ((k1 = size(id1)) > (k2 = size(id2)))
4562155Seric return(1);
4572155Seric else if (k1 < k2)
4582155Seric return(-1);
4592155Seric else while(*id1 != '\0') {
4602155Seric if(*id1 > *id2)
4612155Seric return(1);
4622155Seric else if(*id1 < *id2)
4632155Seric return(-1);
4642155Seric id1++;
4652155Seric id2++;
4662155Seric }
4672155Seric return(0);
4682155Seric }
4692155Seric
4702155Seric
4712155Seric /*
4722155Seric * Numck accepts a pointer to a string and checks to see if they are
4732155Seric * all digits. If they're not it calls fatal, otherwise it returns.
4742155Seric */
4752155Seric
numck(nptr)4762155Seric numck(nptr)
4772155Seric register char *nptr;
4782155Seric {
4792155Seric for (; *nptr != '\0'; nptr++)
4802155Seric if (!numeric(*nptr))
4812155Seric error("non-numerical value on line %d (914)",Lineno);
4822155Seric return;
4832155Seric }
4842155Seric
4852155Seric
4862155Seric /*
4872155Seric * Replace accepts a pointer to a line and scans the line for a keyword
4882155Seric * enclosed in control characters. If it doesn't find one it returns
4892155Seric * a pointer to the begining of the line. Otherwise, it calls
4902155Seric * lookup to find the keyword.
4912155Seric * It rewrites the line substituting the value for the
4922155Seric * keyword enclosed in control characters. It then continues scanning
4932155Seric * the line until no control characters are found and returns a pointer to
4942155Seric * the begining of the new line.
4952155Seric */
4962155Seric
4972155Seric # define INCR(int) if (++int==NSLOTS) error(subrng,Lineno)
4982155Seric char *subrng "out of space [line %d] (916)";
4992155Seric
replace(ptr)5002155Seric replace(ptr)
5012155Seric char *ptr;
5022155Seric {
5032155Seric char *slots[NSLOTS];
5042155Seric int i,j,newlen;
5052155Seric register char *s, *t, *p;
5062155Seric
5072155Seric for (s=ptr; *s++!='\n';);
5082155Seric *(--s) = '\0';
5092155Seric Linend = s;
5102155Seric i = -1;
5112155Seric for (p=ptr; *(s=findch(p,Ctlchar)); p=t) {
5122155Seric *s++ = '\0';
5132155Seric INCR(i);
5142155Seric slots[i] = p;
5152155Seric if (*(t=findch(s,Ctlchar))==0)
5162155Seric error("unmatched `%c' on line %d (907)",Ctlchar,Lineno);
5172155Seric *t++ = '\0';
5182155Seric INCR(i);
5192155Seric slots[i] = Sym[j = lookup(s)].value;
52032774Sbostic Sym[j].usage |= USD;
5212155Seric }
5222155Seric INCR(i);
5232155Seric slots[i] = p;
5242155Seric if (i==0) return(ptr);
5252155Seric newlen = 0;
5262155Seric for (j=0; j<=i; j++)
52732774Sbostic newlen += (size(slots[j])-1);
5282155Seric t = Repflag = alloc(++newlen);
5292155Seric for (j=0; j<=i; j++)
5302155Seric t = ecopy(slots[j],t);
5312155Seric Linend = t;
5322155Seric return(Repflag);
5332155Seric }
5342155Seric
5352155Seric
5362155Seric /*
5372155Seric * Lookup accepts a pointer to a keyword name and searches the symbol
5382155Seric * table for the keyword. It returns its index in the table if its there,
5392155Seric * otherwise it puts the keyword in the table.
5402155Seric */
5412155Seric
lookup(lname)5422155Seric lookup(lname)
5432155Seric char *lname;
5442155Seric {
5452155Seric register int i;
5462155Seric register char *t;
5472155Seric register struct symtab *s;
5482155Seric
5492155Seric t = lname;
5502155Seric while ((i.chr = *t++) &&
5512155Seric ((i.chr>='A' && i.chr<='Z') || (i.chr>='a' && i.chr<='z') ||
5522155Seric (i.chr!= *lname && i.chr>='0' && i.chr<='9')));
5532155Seric if (i.chr)
5542155Seric error("invalid keyword name on line %d (909)",Lineno);
5552155Seric
5562155Seric for(i =0; Sym[i].usage != 0 && i<SYMSIZE; i++)
5572155Seric if (equal(lname, Sym[i].name)) return(i);
5582155Seric s = &Sym[i];
5592155Seric if (s->usage == 0) {
5602155Seric copy(lname,s->name);
5612155Seric copy("",(s->value = alloc(s->lenval = 1)));
5622155Seric return(i);
5632155Seric }
5642155Seric error("out of space (906)");
5652155Seric }
5662155Seric
5672155Seric
5682155Seric /*
5692155Seric * Putin accepts a pointer to a keyword name, and a pointer to a value.
5702155Seric * It puts this information in the symbol table by calling lookup.
5712155Seric * It returns the index of the name in the table.
5722155Seric */
5732155Seric
putin(pname,pvalue)5742155Seric putin(pname, pvalue)
5752155Seric char *pname;
5762155Seric char *pvalue;
5772155Seric {
5782155Seric register int i;
5792155Seric register struct symtab *s;
5802155Seric
5812155Seric s = &Sym[i = lookup(pname)];
5822155Seric free(s->value);
5832155Seric s->lenval = size(pvalue);
5842155Seric copy(pvalue, (s->value = alloc(s->lenval)));
58532774Sbostic s->usage |= ASG;
5862155Seric return(i);
5872155Seric }
5882155Seric
5892155Seric
chksize(s)5902155Seric chksize(s)
5912155Seric char *s;
5922155Seric {
5932155Seric if (size(s) > PARMSIZE)
5942155Seric error("keyword name too long on line %d (908)",Lineno);
5952155Seric }
5962155Seric
5972155Seric
findch(astr,match)5982155Seric findch(astr,match)
5992155Seric char *astr, match;
6002155Seric {
6012155Seric register char *s, *t, c;
6022155Seric char *temp;
6032155Seric
6042155Seric for (s=astr; (c = *s) && c!=match; s++)
6052155Seric if (c=='\\') {
6062155Seric if (s[1]==0)
6072155Seric error("syntax on line %d (919)",Lineno);
6082155Seric else {
6092155Seric for (t = (temp=s) + 1; *s++ = *t++;);
6102155Seric s = temp;
6112155Seric }
6122155Seric }
6132155Seric return(s);
6142155Seric }
6152155Seric
6162155Seric
ecopy(s1,s2)6172155Seric ecopy(s1,s2)
6182155Seric char *s1, *s2;
6192155Seric {
6202155Seric register char *r1, *r2;
6212155Seric
6222155Seric r1 = s1;
6232155Seric r2 = s2;
6242155Seric while (*r2++ = *r1++);
6252155Seric return(--r2);
6262155Seric }
6272155Seric
6282155Seric
error(arg)6292155Seric error(arg)
6302155Seric {
631*33423Sbostic sprintf(Error,"%r",&arg);
632*33423Sbostic fatal(Error);
6332155Seric }
6342155Seric
6352155Seric
findstr(astr,pat)6362155Seric findstr(astr,pat)
6372155Seric char *astr, *pat;
6382155Seric {
6392155Seric register char *s, *t, c;
6402155Seric char *temp;
6412155Seric
6422155Seric for (s=astr; (c = *s) && any(c,pat)==0; s++)
6432155Seric if (c=='\\') {
6442155Seric if (s[1]==0)
6452155Seric error("syntax on line %d (919)",Lineno);
6462155Seric else {
6472155Seric for (t = (temp=s) + 1; *s++ = *t++;);
6482155Seric s = temp;
6492155Seric }
6502155Seric }
6512155Seric return(s);
6522155Seric }
6532155Seric
6542155Seric
warn(arg)6552155Seric warn(arg)
6562155Seric {
6572155Seric if (!Silent)
6582155Seric fprintf(stderr,"%r",&arg);
6592155Seric }
660