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 } 659