1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /* Copyright (c) 1988 AT&T */
22 /* All Rights Reserved */
23 /*
24 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27 /*
28 * Copyright 2006-2020 J. Schilling
29 *
30 * @(#)vc.c 1.14 20/05/08 J. Schilling
31 */
32 #if defined(sun)
33 #pragma ident "@(#)vc.c 1.14 20/05/08 J. Schilling"
34 #endif
35 /*
36 * @(#)vc.c 1.6 06/12/12
37 */
38
39 #if defined(sun)
40 #pragma ident "@(#)vc.c"
41 #pragma ident "@(#)sccs:cmd/vc.c"
42 #endif
43 # include <defines.h>
44
45 # define USD 1
46 # define DCL 2
47 # define ASG 4
48
49 # define EQ '='
50 # define NEQ '!'
51 # define GT '>'
52 # define LT '<'
53 # define DELIM " \t"
54 # define TRUE 1
55 # define FALSE 0
56
57 static char Ctlchar = ':';
58
59 struct symtab {
60 int usage;
61 char *name;
62 char *value;
63 int lenval;
64 };
65 static struct symtab *Sym;
66 static int symsize;
67
68
69 static char *sptr;
70 static int Skiptabs;
71 static int Repall;
72
73 /*
74 * Delflag is used to indicate when text is to be skipped. It is decre-
75 * mented whenever an if condition is false, or when an if occurs
76 * within a false if/end statement. It is decremented whenever an end is
77 * encountered and the Delflag is greater than zero. Whenever Delflag
78 * is greater than zero text is skipped.
79 */
80
81 static int Delflag;
82
83 /*
84 * Ifcount keeps track of the number of ifs and ends. Each time
85 * an if is encountered Ifcount is incremented and each time an end is
86 * encountered it is decremented.
87 */
88
89 static int Ifcount;
90 static int Lineno;
91
92 static char *Repflag;
93 static char *Linend;
94 static int Silent;
95
96 int main __PR((int argc, char **argv));
97 static void asgfunc __PR((char *aptr));
98 static void dclfunc __PR((char *dptr));
99 static void errfunc __PR((char *eptr));
100 static void endfunc __PR((void));
101 static void msgfunc __PR((char *mptr));
102 static void repfunc __PR((char *s));
103 static void iffunc __PR((char *iptr));
104 static int door __PR((void));
105 static int doand __PR((void));
106 #undef exp
107 #define exp vc_exp
108 static int exp __PR((void));
109 static char * getid __PR((char *gptr));
110 static int numcomp __PR((char *id1, char *id2));
111 static void numck __PR((char *nptr));
112 static char * replace __PR((char *ptr));
113 static int lookup __PR((char *lname));
114 static int putin __PR((char *pname, char *pvalue));
115 static char * findch __PR((char *astr, int match));
116 static char * ecopy __PR((char *s1, char *s2));
117 static char * findstr __PR((char *astr, char *pat));
118 static char * fgetl __PR((char **linep, size_t *sizep, FILE *f));
119
120 /*
121 * The main program reads a line of text and sends it to be processed
122 * if it is a version control statement. If it is a line of text and
123 * the Delflag is equal to zero, it is written to the standard output.
124 */
125
126 int
main(argc,argv)127 main(argc, argv)
128 int argc;
129 char *argv[];
130 {
131 register char *lineptr, *p;
132 register int i;
133 char *line = NULL;
134 size_t linesize = 0;
135 extern int Fflags;
136
137 #ifdef SCHILY_BUILD
138 save_args(argc, argv);
139 #endif
140 sccs_setinsbase(INS_BASE);
141
142 Fflags = FTLCLN | FTLMSG | FTLEXIT;
143 #ifdef SCCS_FATALHELP
144 Fflags |= FTLFUNC;
145 Ffunc = sccsfatalhelp;
146 #endif
147 setsig();
148 for(i = 1; i< argc; i++) {
149 p = argv[i];
150 if (p[0] == '-')
151 switch (p[1]) {
152 case 's':
153 Silent = 1;
154 break;
155 case 't':
156 Skiptabs = 1;
157 break;
158 case 'a':
159 Repall = 1;
160 break;
161 case 'c':
162 Ctlchar = p[2];
163 break;
164 }
165 else {
166 p[size(p) - 1] = '\n';
167 asgfunc(p);
168 }
169 }
170 while (fgetl(&line, &linesize, stdin) != NULL) {
171 lineptr = line;
172 Lineno++;
173
174 if (Repflag != 0) {
175 ffree(Repflag);
176 Repflag = 0;
177 }
178
179 if (Skiptabs) {
180 for (p = lineptr; *p; p++)
181 if (*p == '\t')
182 break;
183 if (*p++ == '\t')
184 lineptr = p;
185 }
186
187 if (lineptr[0] != Ctlchar) {
188 if (lineptr[0] == '\\' && lineptr[1] == Ctlchar)
189 for (p = &lineptr[1];
190 (*lineptr++ = *p++) != '\0'; )
191 ;
192 if(Delflag == 0) {
193 if (Repall)
194 repfunc(line);
195 else
196 if (fputs(line,stdout) == EOF) {
197 FAILPUT;
198 }
199 }
200 continue;
201 }
202
203 lineptr++;
204
205 if (imatch("if ", lineptr))
206 iffunc(&lineptr[3]);
207 else if (imatch("end", lineptr))
208 endfunc();
209 else if (Delflag == 0) {
210 if (imatch("asg ", lineptr))
211 asgfunc(&lineptr[4]);
212 else if (imatch("dcl ", lineptr))
213 dclfunc(&lineptr[4]);
214 else if (imatch("err", lineptr))
215 errfunc(&lineptr[3]);
216 else if (imatch("msg", lineptr))
217 msgfunc(&lineptr[3]);
218 else if (lineptr[0] == Ctlchar)
219 repfunc(&lineptr[1]);
220 else if (imatch("on", lineptr))
221 Repall = 1;
222 else if (imatch("off", lineptr))
223 Repall = 0;
224 else if (imatch("ctl ", lineptr))
225 Ctlchar = lineptr[4];
226 else {
227 sprintf(SccsError,"unknown command on line %d (vc1)",
228 Lineno);
229 fatal(SccsError);
230 }
231 }
232 }
233 for(i = 0; i < symsize && Sym[i].usage != 0; i++) {
234 if ((Sym[i].usage&USD) == 0 && !Silent)
235 fprintf(stderr,"`%s' never used (vc2)\n",Sym[i].name);
236 if ((Sym[i].usage&DCL) == 0 && !Silent)
237 fprintf(stderr,"`%s' never declared (vc3)\n",
238 Sym[i].name);
239 if ((Sym[i].usage&ASG) == 0 && !Silent)
240 fprintf(stderr,"`%s' never assigned a value (vc20)\n",
241 Sym[i].name);
242 }
243 if (Ifcount > 0)
244 fatal("`if' with no matching `end' (vc4)");
245
246 return (0);
247 }
248
249
250 /*
251 * Asgfunc accepts a pointer to a line picks up a keyword name, an
252 * equal sign and a value and calls putin to place it in the symbol table.
253 */
254
255 static void
asgfunc(aptr)256 asgfunc(aptr)
257 register char *aptr;
258 {
259 register char *end, *aname;
260 char *avalue;
261
262 aptr = replace(aptr);
263 NONBLANK(aptr);
264 aname = aptr;
265 end = Linend;
266 aptr = findstr(aptr,"= \t");
267 if (*aptr == ' ' || *aptr == '\t') {
268 *aptr++ = '\0';
269 aptr = findch(aptr,'=');
270 }
271 if (aptr == end) {
272 sprintf(SccsError,"syntax on line %d (vc17)",Lineno);
273 fatal(SccsError);
274 }
275 *aptr++ = '\0';
276 avalue = getid(aptr);
277 putin(aname, avalue);
278 }
279
280
281 /*
282 * Dclfunc accepts a pointer to a line and picks up keywords
283 * separated by commas. It calls putin to put each keyword in the
284 * symbol table. It returns when it sees a newline.
285 */
286
287 static void
dclfunc(dptr)288 dclfunc(dptr)
289 register char *dptr;
290 {
291 register char *end, *name;
292 int i;
293
294 dptr = replace(dptr);
295 end = Linend;
296 NONBLANK(dptr);
297 while (dptr < end) {
298 name = dptr;
299 dptr = findch(dptr,',');
300 *dptr++ = '\0';
301 if (Sym[i = lookup(name)].usage&DCL) {
302 sprintf(SccsError,"`%s' declared twice on line %d (vc5)",
303 name, Lineno);
304 fatal(SccsError);
305 }
306 else
307 Sym[i].usage |= DCL;
308 NONBLANK(dptr);
309 }
310 }
311
312
313 /*
314 * Errfunc calls fatal which stops the process.
315 */
316
317 static void
errfunc(eptr)318 errfunc(eptr)
319 char *eptr;
320 {
321 if (!Silent)
322 fprintf(stderr,"ERROR:%s\n",replace(eptr));
323 sprintf(SccsError,"err statement on line %d (vc15)", Lineno);
324 fatal(SccsError);
325 }
326
327
328 /*
329 * Endfunc indicates an end has been found by decrementing the if count
330 * flag. If because of a previous if statement, text was being skipped,
331 * Delflag is also decremented.
332 */
333
334 static void
endfunc()335 endfunc()
336 {
337 if (--Ifcount < 0) {
338 sprintf(SccsError,"`end' without matching `if' on line %d (vc10)",
339 Lineno);
340 fatal(SccsError);
341 }
342 if (Delflag > 0)
343 Delflag--;
344 return;
345 }
346
347
348 /*
349 * Msgfunc accepts a pointer to a line and prints that line on the
350 * diagnostic output.
351 */
352
353 static void
msgfunc(mptr)354 msgfunc(mptr)
355 char *mptr;
356 {
357 if (!Silent)
358 fprintf(stderr,"Message(%d):%s\n", Lineno, replace(mptr));
359 }
360
361
362 static void
repfunc(s)363 repfunc(s)
364 char *s;
365 {
366 fprintf(stdout,"%s\n",replace(s));
367 }
368
369
370 /*
371 * Iffunc and the three functions following it, door, doand, and exp
372 * are responsible for parsing and interperting the condition in the
373 * if statement. The BNF used is as follows:
374 * <iffunc> ::= [ "not" ] <door> EOL
375 * <door> ::= <doand> | <doand> "|" <door>
376 * <doand>::= <exp> | <exp> "&" <doand>
377 * <exp>::= "(" <door> ")" | <value> <operator> <value>
378 * <operator>::= "=" | "!=" | "<" | ">"
379 * And has precedence over or. If the condition is false the Delflag
380 * is bumped to indicate that lines are to be skipped.
381 * An external variable, sptr is used for processing the line in
382 * iffunc, door, doand, exp, getid.
383 * Iffunc accepts a pointer to a line and sets sptr to that line. The
384 * rest of iffunc, door, and doand follow the BNF exactly.
385 */
386
387 static void
iffunc(iptr)388 iffunc(iptr)
389 char *iptr;
390 {
391 register int value, not;
392
393 Ifcount++;
394 if (Delflag > 0)
395 Delflag++;
396
397 else {
398 sptr = replace(iptr);
399 NONBLANK(sptr);
400 if (imatch("not ", sptr)) {
401 not = FALSE;
402 sptr += 4;
403 }
404 else not = TRUE;
405
406 value = door();
407 if( *sptr != 0) {
408 sprintf(SccsError,"syntax on line %d (vc18)",Lineno);
409 fatal(SccsError);
410 }
411
412 if (value != not)
413 Delflag++;
414 }
415
416 return;
417 }
418
419 static int
door()420 door()
421 {
422 int value;
423 value = doand();
424 NONBLANK(sptr);
425 while (*sptr=='|') {
426 sptr++;
427 value |= doand();
428 NONBLANK(sptr);
429 }
430 return(value);
431 }
432
433 static int
doand()434 doand()
435 {
436 int value;
437 value = exp();
438 NONBLANK(sptr);
439 while (*sptr=='&') {
440 sptr++;
441 value &= exp();
442 NONBLANK(sptr);
443 }
444 return(value);
445 }
446
447
448 /*
449 * After exp checks for parentheses, it picks up a value by calling getid,
450 * picks up an operator and calls getid to pick up the second value.
451 * Then based on the operator it calls either numcomp or equal to see
452 * if the exp is true or false and returns the correct value.
453 */
454
455 static int
exp()456 exp()
457 {
458 register char op, save;
459 register int value = 0;
460 char *id1, *id2, next = 0;
461
462 NONBLANK(sptr);
463 if(*sptr == '(') {
464 sptr++;
465 value = door();
466 NONBLANK(sptr);
467 if (*sptr == ')') {
468 sptr++;
469 return(value);
470 }
471 else {
472 sprintf(SccsError,"parenthesis error on line %d (vc11)",
473 Lineno);
474 }
475 }
476
477 id1 = getid(sptr);
478 if ((op = *sptr) != '\0')
479 *sptr++ = '\0';
480 if (op == NEQ && (next = *sptr++) == '\0')
481 --sptr;
482 id2 = getid(sptr);
483 save = *sptr;
484 *sptr = '\0';
485
486 if(op ==LT || op == GT) {
487 value = numcomp(id1, id2);
488 if ((op == GT && value == 1) || (op == LT && value == -1))
489 value = TRUE;
490 else value = FALSE;
491 }
492
493 else if (op==EQ || (op==NEQ && next==EQ)) {
494 value = equal(id1, id2);
495 if ( op == NEQ)
496 value = !value;
497 }
498
499 else {
500 sprintf(SccsError,"invalid operator on line %d (vc12)", Lineno);
501 fatal(SccsError);
502 }
503 *sptr = save;
504 return(value);
505 }
506
507
508 /*
509 * Getid picks up a value off a line and returns a pointer to the value.
510 */
511
512 static char *
getid(gptr)513 getid(gptr)
514 register char *gptr;
515 {
516 register char *id;
517
518 NONBLANK(gptr);
519 id = gptr;
520 gptr = findstr(gptr,DELIM);
521 if (*gptr)
522 *gptr++ = '\0';
523 NONBLANK(gptr);
524 sptr = gptr;
525 return(id);
526 }
527
528
529 /*
530 * Numcomp accepts two pointers to strings of digits and calls numck
531 * to see if the strings contain only digits. It returns -1 if
532 * the first is less than the second, 1 if the first is greater than the
533 * second and 0 if the two are equal.
534 */
535
536 static int
numcomp(id1,id2)537 numcomp(id1, id2)
538 register char *id1, *id2;
539 {
540 int k1, k2;
541
542 numck(id1);
543 numck(id2);
544 while (*id1 == '0')
545 id1++;
546 while (*id2 == '0')
547 id2++;
548 if ((k1 = size(id1)) > (k2 = size(id2)))
549 return(1);
550 else if (k1 < k2)
551 return(-1);
552 else while(*id1 != '\0') {
553 if(*id1 > *id2)
554 return(1);
555 else if(*id1 < *id2)
556 return(-1);
557 id1++;
558 id2++;
559 }
560 return(0);
561 }
562
563
564 /*
565 * Numck accepts a pointer to a string and checks to see if they are
566 * all digits. If they're not it calls fatal, otherwise it returns.
567 */
568
569 static void
numck(nptr)570 numck(nptr)
571 register char *nptr;
572 {
573 for (; *nptr != '\0'; nptr++)
574 if (!numeric(*nptr)) {
575 sprintf(SccsError,"non-numerical value on line %d (vc14)",
576 Lineno);
577 fatal(SccsError);
578 }
579 return;
580 }
581
582
583 /*
584 * Replace accepts a pointer to a line and scans the line for a keyword
585 * enclosed in control characters. If it doesn't find one it returns
586 * a pointer to the begining of the line. Otherwise, it calls
587 * lookup to find the keyword.
588 * It rewrites the line substituting the value for the
589 * keyword enclosed in control characters. It then continues scanning
590 * the line until no control characters are found and returns a pointer to
591 * the begining of the new line.
592 */
593
594 # define INCR(int) if (++int == nslots) { \
595 nslots += 32; \
596 if ((slots = realloc(slots, nslots * sizeof (slots[0]))) == NULL) { \
597 sprintf(SccsError,"out of space [line %d] (vc16)",Lineno); \
598 fatal(SccsError); } \
599 }
600
601 static char *
replace(ptr)602 replace(ptr)
603 char *ptr;
604 {
605 char **slots = NULL;
606 int nslots = 0;
607 int i,j,newlen;
608 register char *s, *t, *p;
609
610 for (s=ptr; *s++!='\n';);
611 *(--s) = '\0';
612 Linend = s;
613 i = -1;
614 for (p=ptr; *(s=findch(p,Ctlchar)); p=t) {
615 *s++ = '\0';
616 INCR(i);
617 slots[i] = p;
618 if (*(t=findch(s,Ctlchar))==0) {
619 sprintf(SccsError,"unmatched `%c' on line %d (vc7)",
620 Ctlchar,Lineno);
621 fatal(SccsError);
622 }
623 *t++ = '\0';
624 INCR(i);
625 slots[i] = Sym[j = lookup(s)].value;
626 Sym[j].usage |= USD;
627 }
628 INCR(i);
629 slots[i] = p;
630 if (i == 0) {
631 free(slots);
632 return (ptr);
633 }
634 newlen = 0;
635 for (j=0; j<=i; j++)
636 newlen += (size(slots[j])-1);
637 t = Repflag = fmalloc((unsigned int) ++newlen);
638 for (j=0; j<=i; j++)
639 t = ecopy(slots[j],t);
640 Linend = t;
641 free(slots);
642 return(Repflag);
643 }
644
645
646 /*
647 * Lookup accepts a pointer to a keyword name and searches the symbol
648 * table for the keyword. It returns its index in the table if its there,
649 * otherwise it puts the keyword in the table.
650 */
651
652 static int
lookup(lname)653 lookup(lname)
654 char *lname;
655 {
656 register int i;
657 register char *t;
658 register struct symtab *s;
659
660 t = lname;
661 while (((i = *t++) != 0) &&
662 ((i>='A' && i<='Z') || (i>='a' && i<='z') ||
663 (i!= *lname && i>='0' && i<='9')));
664 if (i) {
665 sprintf(SccsError,"invalid keyword name on line %d (vc9)",Lineno);
666 fatal(SccsError);
667 }
668
669 for (i = 0; i < symsize && Sym[i].usage != 0; i++)
670 if (equal(lname, Sym[i].name)) return(i);
671 if (i >= symsize) {
672 symsize += 64;
673 Sym = realloc(Sym, symsize * sizeof (Sym[0]));
674 if (Sym == NULL) {
675 fatal("out of space (vc6)");
676 /*NOTREACHED*/
677 }
678 memset(&Sym[i], '\0', (symsize-i) * sizeof (Sym[0]));
679 }
680 s = &Sym[i];
681 copy(lname, (s->name = fmalloc(size(lname))));
682 copy("",(s->value = fmalloc((unsigned int) (s->lenval = 1))));
683 return(i);
684 }
685
686
687 /*
688 * Putin accepts a pointer to a keyword name, and a pointer to a value.
689 * It puts this information in the symbol table by calling lookup.
690 * It returns the index of the name in the table.
691 */
692
693 static int
putin(pname,pvalue)694 putin(pname, pvalue)
695 char *pname;
696 char *pvalue;
697 {
698 register int i;
699 register struct symtab *s;
700
701 s = &Sym[i = lookup(pname)];
702 ffree(s->value);
703 s->lenval = size(pvalue);
704 copy(pvalue, (s->value = fmalloc((unsigned int) (s->lenval))));
705 s->usage |= ASG;
706 return(i);
707 }
708
709 static char *
findch(astr,match)710 findch(astr,match)
711 char *astr, match;
712 {
713 register char *s, *t, c;
714 char *temp;
715
716 for (s=astr; ((c = *s) != '\0') && c!=match; s++)
717 if (c=='\\') {
718 if (s[1]==0) {
719 sprintf(SccsError,"syntax on line %d (vc19)",Lineno);
720 fatal(SccsError);
721 }
722 else {
723 for (t = (temp=s) + 1; (*s++ = *t++) != '\0';);
724 s = temp;
725 }
726 }
727 return(s);
728 }
729
730
731 static char *
ecopy(s1,s2)732 ecopy(s1,s2)
733 char *s1, *s2;
734 {
735 register char *r1, *r2;
736
737 r1 = s1;
738 r2 = s2;
739 while ((*r2++ = *r1++) != '\0');
740 return(--r2);
741 }
742
743
744 static char *
findstr(astr,pat)745 findstr(astr,pat)
746 char *astr, *pat;
747 {
748 register char *s, *t, c;
749 char *temp;
750
751 for (s=astr; ((c = *s) != '\0') && any(c,pat)==0; s++)
752 if (c=='\\') {
753 if (s[1]==0) {
754 sprintf(SccsError,"syntax on line %d (vc19)",Lineno);
755 fatal(SccsError);
756 }
757 else {
758 for (t = (temp=s) + 1; (*s++ = *t++) != '\0';);
759 s = temp;
760 }
761 }
762 return(s);
763 }
764
765 #define DEF_LINE_SIZE 128
766
767 static char *
fgetl(linep,sizep,f)768 fgetl(linep, sizep, f)
769 char **linep;
770 size_t *sizep;
771 FILE *f;
772 {
773 int eof;
774 register size_t used = 0;
775 register size_t line_size;
776 register char *line;
777
778 line_size = *sizep;
779 line = *linep;
780 if (line_size == 0) {
781 line_size = DEF_LINE_SIZE;
782 line = (char *) malloc(line_size);
783 if (line == NULL)
784 fatal(gettext("line too long (vc21)"));
785 }
786 /* read until EOF or newline encountered */
787 line[0] = '\0';
788 do {
789 line[line_size - 1] = '\t'; /* arbitrary non-zero char */
790 line[line_size - 2] = ' '; /* arbitrary non-newline char */
791 if (!(eof = (fgets(line+used,
792 line_size-used,
793 f) == NULL))) {
794
795 if (line[line_size - 1] != '\0' ||
796 line[line_size - 2] == '\n')
797 break;
798
799 used = line_size - 1;
800
801 line_size += DEF_LINE_SIZE;
802 line = (char *) realloc(line, line_size);
803 if (line == NULL)
804 fatal(gettext("line too long (vc21)"));
805 }
806 } while (!eof);
807 used += strlen(&line[used]);
808 *linep = line;
809 *sizep = line_size;
810 if (eof && (used == 0))
811 return (NULL);
812 return (line);
813 }
814