12155Seric # include "stdio.h" 22155Seric # include "seven/types.h" 32155Seric # include "seven/macros.h" 42155Seric # include "fatal.h" 52155Seric 6*32774Sbostic SCCSID(@(#)vc.c 4.2); 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 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 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 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 234*32774Sbostic Sym[i].usage |= DCL; 2352155Seric NONBLANK(dptr); 2362155Seric } 2372155Seric } 2382155Seric 2392155Seric 2402155Seric /* 2412155Seric * Errfunc calls fatal which stops the process. 2422155Seric */ 2432155Seric 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 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 2732155Seric msgfunc(mptr) 2742155Seric char *mptr; 2752155Seric { 2762155Seric warn("Message(%d):%s\n", Lineno, replace(mptr)); 2772155Seric } 2782155Seric 2792155Seric 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 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; 320*32774Sbostic 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 3362155Seric door() 3372155Seric { 3382155Seric int value; 3392155Seric value = doand(); 3402155Seric NONBLANK(sptr); 3412155Seric while (*sptr=='|') { 3422155Seric sptr++; 343*32774Sbostic value |= doand(); 3442155Seric NONBLANK(sptr); 3452155Seric } 3462155Seric return(value); 3472155Seric } 3482155Seric 3492155Seric 3502155Seric doand() 3512155Seric { 3522155Seric int value; 3532155Seric value = exp(); 3542155Seric NONBLANK(sptr); 3552155Seric while (*sptr=='&') { 3562155Seric sptr++; 357*32774Sbostic 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 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 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 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 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 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; 520*32774Sbostic 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++) 527*32774Sbostic 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 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 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))); 585*32774Sbostic s->usage |= ASG; 5862155Seric return(i); 5872155Seric } 5882155Seric 5892155Seric 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 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 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 6292155Seric error(arg) 6302155Seric { 6312155Seric fatal(sprintf(Error,"%r",&arg)); 6322155Seric } 6332155Seric 6342155Seric 6352155Seric findstr(astr,pat) 6362155Seric char *astr, *pat; 6372155Seric { 6382155Seric register char *s, *t, c; 6392155Seric char *temp; 6402155Seric 6412155Seric for (s=astr; (c = *s) && any(c,pat)==0; s++) 6422155Seric if (c=='\\') { 6432155Seric if (s[1]==0) 6442155Seric error("syntax on line %d (919)",Lineno); 6452155Seric else { 6462155Seric for (t = (temp=s) + 1; *s++ = *t++;); 6472155Seric s = temp; 6482155Seric } 6492155Seric } 6502155Seric return(s); 6512155Seric } 6522155Seric 6532155Seric 6542155Seric warn(arg) 6552155Seric { 6562155Seric if (!Silent) 6572155Seric fprintf(stderr,"%r",&arg); 6582155Seric } 659