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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include "xlator.h"
32 #include "util.h"
33 #include "bucket.h"
34 #include "errlog.h"
35 
36 /* Types: */
37 #define	TRUE	1
38 #define	FALSE	0
39 #define	MAXLINE 1024
40 
41 
42 typedef enum {
43 	PARENT, UNCLE
44 } RELATION;
45 
46 
47 /* Statics: */
48 /* The parser is a dfa, driven by the following: */
49 static FILE *Fp;
50 static const char *Filename;
51 static char Previous[MAXLINE];
52 static char LeftMostChild[MAXLINE];
53 static int Selected = FALSE;
54 static int Line;
55 static int Errors;
56 
57 
58 /* The grammar is: */
59 static int arch(void);
60 static int comment(void);
61 static int arch_name(void);
62 static int set_list(void);
63 static int set(void);
64 
65 /* The supporting code is: */
66 static int accept_token(char *);
67 static void skip_to(char *);
68 
69 /* And the tokenizer is: */
70 static char *tokenize(char *);
71 static char *currtok(void);
72 static char *nexttok(void);
73 static char *skipb(char *);
74 static char *skipover(char *);
75 static char *CurrTok = NULL;
76 
77 static int set_parents(void);
78 
79 static table_t *Vers;
80 static table_t *Varch;
81 
82 static void init_tables(void);
83 
84 static void add_valid_arch(char *);
85 static void add_valid_version(char *vers_name);
86 
87 
88 #define	in_specials(c)  ((c) == '{' || (c) == '}' || (c) == '+' || \
89 	(c) == '-' || (c) == ';' || (c) == ':' || (c) == ',' || \
90 	(c) == '[' || (c) == ']')
91 
92 #define	eq(s1, s2)	(strcmp((s1), (s2)) == 0)
93 
94 
95 /*
96  * parse_versions -- parse the file whose name is passed, return
97  *	the number of (fatal) errors encountered. Currently only
98  *	knows about reading set files and writing vers files.
99  */
100 int
parse_versions(const char * fileName)101 parse_versions(const char *fileName)
102 {
103 
104 	/* Prime the set-file parser dfa: */
105 	assert(fileName != NULL, "passed null filename to parse_versions");
106 	errlog(BEGIN, "parse_versions(%s) {", fileName);
107 
108 
109 	if ((Fp = fopen(fileName, "r")) == NULL) {
110 		(void) fprintf(stderr, "Cannot open version file \"%s\"\n",
111 		    fileName);
112 		errlog(END, "} /* parse_versions */");
113 		return (1);
114 	}
115 	Filename = fileName;
116 	Line = 0;
117 
118 	errlog(VERBOSE, "reading set file %s looking for architecture %s",
119 	    Filename, TargetArchStr);
120 
121 	/* Run the dfa. */
122 	while (arch())
123 		continue;
124 
125 	(void) fclose(Fp);
126 	/* print_all_buckets(); */
127 	errlog(END, "} /* parse_versions */");
128 	return (Errors);
129 }
130 
131 
132 /*
133  * The parser. This implements the grammar:
134  *    setfile::= (arch())+ <EOF>
135  *             | <EOF>
136  *    arch::= <ARCHITECTURE> "{" (set_list())* "}"
137  *    set_list::= (set())+ ";"
138  *    set::= <IDENTIFIER> ["[" "WEAK" "]"] ":" "{" (ancestors) "}" ";"
139  *    ancestors::= <IDENTIFIER> | <ancestors> "," <IDENTIFIER>
140  *    where <ARCHITECTURE> and <IDENTIFIER> are tokens.
141  */
142 static int
arch(void)143 arch(void)
144 {
145 	int olderrors;
146 
147 	errlog(BEGIN, "arch() {");
148 	if (comment()) {
149 		errlog(END, "} /* arch */");
150 		return (TRUE);
151 	}
152 	if (arch_name() == FALSE) {
153 		errlog(END, "} /* arch */");
154 		return (FALSE);
155 	}
156 	if (accept_token("{") == FALSE) {
157 		errlog(END, "} /* arch */");
158 		return (FALSE);
159 	}
160 
161 	olderrors = Errors;
162 	if (set_list() == FALSE) {
163 		if (olderrors != Errors) {
164 			errlog(END, "} /* arch */");
165 			return (FALSE);
166 		}
167 	}
168 
169 	errlog(END, "} /* arch */");
170 	return (TRUE);
171 }
172 
173 static int
comment(void)174 comment(void)
175 {
176 	char *token = currtok();
177 
178 	if (token == NULL || *token != '#') {
179 		return (FALSE);
180 	} else {
181 		/* Swallow token. */
182 		token =  nexttok();
183 		return (TRUE);
184 	}
185 }
186 
187 static int
arch_name(void)188 arch_name(void)
189 {
190 	char *token = currtok();
191 
192 	errlog(BEGIN, "arch_name() {");
193 	errlog(VERBOSE, "token = '%s';",
194 		token ? token : "<NULL>");
195 
196 	if (token == NULL) {
197 		errlog(END, "} /* arch_name */");
198 		return (FALSE);
199 
200 	} else if (in_specials(*token)) {
201 		/* It's not an architecture */
202 		Selected = FALSE;
203 
204 		/* Report a syntax error: TBD */
205 		errlog(INPUT | ERROR, "found special char. %c "
206 		    "while looking for an architecture name",
207 		    *token);
208 
209 		skip_to("}");	/* The follower set for arch_name. */
210 		errlog(END, "} /* arch name */");
211 
212 		Errors++;
213 		return (FALSE);
214 
215 	} else if (!eq(token, TargetArchStr)) {
216 		/* It's an architecture ... */
217 		errlog(VERBOSE, "Begin unselected architecture: %s", token);
218 		add_valid_arch(token);
219 		(void) nexttok();
220 
221 		/* ... but the the wrong one. */
222 		Selected = FALSE;
223 		errlog(END, "} /* arch name */");
224 		return (TRUE);
225 	} else {
226 		/* Found the right architecture. */
227 		errlog(VERBOSE, "Begin selected architecture: %s", token);
228 		add_valid_arch(token);
229 		(void) nexttok();
230 		Selected = TRUE;
231 		errlog(END, "} /* arch name */");
232 		return (TRUE);
233 	}
234 }
235 
236 
237 static int
set_list(void)238 set_list(void)
239 {
240 	int olderrors;
241 	char *token = currtok();
242 
243 	errlog(BEGIN, "set_list() {");
244 	errlog(VERBOSE, "token = '%s'",
245 	    (token) ? token : "<NULL>");
246 	if (set() == FALSE) {
247 		errlog(END, "} /* set_list */");
248 		return (FALSE);
249 	}
250 
251 	olderrors = Errors;
252 	while (set()) {
253 		continue;
254 	}
255 	if (olderrors != Errors) {
256 		errlog(END, "} /* set_list */");
257 		return (FALSE);
258 	}
259 
260 	errlog(END, "} /* set_list */");
261 	return (TRUE);
262 }
263 
264 
265 static int
set(void)266 set(void)
267 {
268 	char *token = currtok();
269 	int has_parent = 0;
270 
271 	errlog(BEGIN, "set() {");
272 	errlog(VERBOSE, "token = '%s'",
273 	    (token) ? token : "<NULL>");
274 
275 	if (in_specials(*token)) {
276 		errlog(INPUT|ERROR, "unexpected token \"%s\" found. "
277 		    "Version name expected", token);
278 		Errors++;
279 		errlog(END, "} /* set */");
280 		return (FALSE);
281 	}
282 
283 	errlog(VERBOSE, "Begin Version: %s", token);
284 	*Previous = '\0';
285 	if (Selected) {
286 		if (add_parent(token, Previous, 0) == FALSE) {
287 			errlog(INPUT | ERROR, "unable to add a parent version "
288 			    "from the set file");
289 			Errors++;
290 			errlog(END, "} /* set */");
291 			return (FALSE);
292 		}
293 	}
294 
295 	add_valid_version(token);
296 	(void) strncpy(LeftMostChild, token, MAXLINE);
297 	LeftMostChild[MAXLINE-1] = '\0';
298 	(void) strncpy(Previous, token, MAXLINE);
299 	Previous[MAXLINE-1] = '\0';
300 
301 	token = nexttok();
302 
303 	switch (*token) {
304 		case ':':
305 			errlog(VERBOSE, "token ':' found");
306 			(void) accept_token(":");
307 			if (set_parents() == FALSE) {
308 				errlog(END, "} /* set */");
309 				return (FALSE);
310 			}
311 			if (accept_token(";") == FALSE) {
312 				errlog(END, "} /* set */");
313 				return (FALSE);
314 			}
315 			errlog(VERBOSE, "End Version");
316 			break;
317 
318 		case ';':
319 			errlog(VERBOSE, "token ';' found");
320 			(void) accept_token(";");
321 			errlog(VERBOSE, "End version ':'");
322 			break;
323 
324 		case '[':
325 			(void) accept_token("[");
326 			if (accept_token("WEAK") == FALSE) {
327 				errlog(END, "} /* set */");
328 				return (FALSE);
329 			}
330 			if (accept_token("]") == FALSE) {
331 				errlog(END, "} /* set */");
332 				return (FALSE);
333 			}
334 			token = currtok();
335 			if (eq(token, ":")) {
336 				(void) accept_token(":");
337 				has_parent = 1;
338 			} else if (eq(token, ";")) {
339 				(void) accept_token(";");
340 			} else {
341 				errlog(ERROR|INPUT,
342 				    "Unexpected token \"%s\" found. ':'"
343 				    "or ';' expected.", token);
344 				Errors++;
345 				errlog(END, "} /* set */");
346 				return (FALSE);
347 			}
348 			errlog(VERBOSE, "WEAK version detected\n");
349 			if (Selected)
350 				set_weak(LeftMostChild, TRUE);
351 
352 			if (has_parent) {
353 				if (set_parents() == FALSE) {
354 					errlog(END, "} /* set */");
355 					return (FALSE);
356 				}
357 				if (accept_token(";") == FALSE) {
358 					errlog(END, "} /* set */");
359 					return (FALSE);
360 				}
361 			}
362 			errlog(VERBOSE, "End Version");
363 			break;
364 		default:
365 			/* CSTYLED */
366 			errlog(ERROR|INPUT,
367 			    "Unexpected token \"%s\" found. ';' expected.",
368 			    token);
369 			Errors++;
370 			errlog(END, "} /* set */");
371 			return (FALSE);
372 	}
373 
374 	token = currtok();
375 	if (eq(token, "}")) {
376 		(void) accept_token("}");
377 		errlog(VERBOSE, "End architecture");
378 		errlog(END, "} /* set */");
379 		return (FALSE);
380 	}
381 
382 	errlog(END, "} /* set */");
383 	return (TRUE);
384 }
385 
386 static int
set_parents(void)387 set_parents(void)
388 {
389 	char *token = currtok();
390 	int uncle;
391 
392 	errlog(BEGIN, "set_parents() {");
393 	errlog(VERBOSE, "token = '%s'",
394 	    (token) ? token : "<NULL>");
395 
396 	if (accept_token("{") == FALSE) {
397 		errlog(INPUT|ERROR, "set_parents(): Unexpected token: %s\n",
398 		    token);
399 		Errors++;
400 		errlog(END, "} /* set_parents */");
401 		return (FALSE);
402 	}
403 
404 	token = currtok();
405 
406 	if (in_specials(*token)) {
407 		errlog(INPUT|ERROR, "set_parents(): Unexpected token: %c "
408 		    "found. Version token expected", *token);
409 		Errors++;
410 		errlog(END, "} /* set_parents */");
411 		return (FALSE);
412 	}
413 
414 	uncle = 0;
415 	while (token && *token != '}') {
416 		errlog(VERBOSE, "Begin parent list: %s\n", token);
417 		if (Selected) {
418 			if (uncle)
419 				(void) add_uncle(token, LeftMostChild, 0);
420 			else
421 				(void) add_parent(token, Previous, 0);
422 		}
423 		(void) strncpy(Previous, token, MAXLINE);
424 		add_valid_version(token);
425 		Previous[MAXLINE-1] = '\0';
426 
427 		token = nexttok();
428 
429 		if (*token == ',') {
430 			token = nexttok();
431 			/* following identifiers are all uncles */
432 			uncle = 1;
433 			continue;
434 		}
435 
436 		if (*token == '}') {
437 			if (accept_token("}") == FALSE) {
438 				errlog(END, "} /* set_parents */");
439 				return (FALSE);
440 			}
441 			errlog(VERBOSE, "set_parent: End of parent list");
442 			errlog(END, "} /* set_parents */");
443 			return (TRUE);
444 		}
445 
446 		errlog(INPUT|ERROR,
447 		    "set_parents(): Unexpected token \"%s\" "
448 		    "found. ',' or '}' were expected", token);
449 		Errors++;
450 		errlog(END, "} /* set_parents */");
451 		return (FALSE);
452 	}
453 	errlog(END, "} /* set_parents */");
454 	return (TRUE);
455 }
456 
457 
458 /*
459  * parser support routines
460  */
461 
462 
463 /*
464  * accept_token -- get a specified token or complain loudly.
465  */
466 static int
accept_token(char * expected)467 accept_token(char *expected)
468 {
469 	char *token = currtok();
470 
471 	assert(expected != NULL, "null token passed to accept_token");
472 	errlog(OTHER | TRACING, "accept_token, at %s expecting %s",
473 		(token) ? token : "<NULL>", expected);
474 
475 	if (token == NULL) {
476 		/* We're at EOF */
477 		return (TRUE);
478 	}
479 	if (eq(token, expected)) {
480 		(void) nexttok();
481 		return (TRUE);
482 	} else {
483 		errlog(INPUT | ERROR,
484 			"accept_token, found %s while looking for %s",
485 			(token) ? token : "<NULL>", expected);
486 		++Errors;
487 		return (FALSE);
488 	}
489 }
490 
491 static void
skip_to(char * target)492 skip_to(char *target)
493 {
494 	char *token = currtok();
495 
496 	assert(target != NULL, "null target passed to skip_to");
497 	while (token && !eq(token, target)) {
498 		errlog(VERBOSE, "skipping over %s",
499 			(token) ? token : "<NULL>");
500 		token = nexttok();
501 	}
502 }
503 
504 
505 /*
506  * tokenizer -- below the grammar lives this, like a troll
507  *	under a bridge.
508  */
509 
510 
511 /*
512  * skipb -- skip over blanks (whitespace, actually), stopping
513  *      on first non-blank.
514  */
515 static char *
skipb(char * p)516 skipb(char *p)
517 {
518 
519 	while (*p && isspace(*p))
520 		++p;
521 	return (p);
522 }
523 
524 /*
525  * skipover -- skip over non-separators (alnum, . and _, actually),
526  *      stopping on first separator.
527  */
528 static char *
skipover(char * p)529 skipover(char *p)
530 {
531 
532 	while (*p && (isalnum(*p) || (*p == '_' || *p == '.')))
533 		++p;
534 	return (p);
535 }
536 
537 
538 /*
539  * currtok/nexttok -- get the current/next token
540  */
541 static char *
currtok(void)542 currtok(void)
543 {
544 
545 	if (CurrTok == NULL) {
546 		(void) nexttok();
547 	}
548 	return (CurrTok);
549 }
550 
551 static char *
nexttok(void)552 nexttok(void)
553 {
554 	static char line[MAXLINE];
555 	char *p;
556 
557 	if ((p = tokenize(NULL)) == NULL) {
558 		/* We're at an end of line. */
559 		do {
560 			if (fgets(line, sizeof (line), Fp) == NULL) {
561 				/* Which is also end of file. */
562 				CurrTok = NULL;
563 				return (NULL);
564 			}
565 			++Line;
566 			seterrline(Line, Filename, "", line);
567 		} while ((p = tokenize(line)) == NULL);
568 	}
569 	CurrTok = p;
570 	return (p);
571 }
572 
573 
574 
575 /*
576  * tokenize -- a version of the standard strtok with specific behavior.
577  */
578 static char *
tokenize(char * line)579 tokenize(char *line)
580 {
581 	static char *p = NULL;
582 	static char saved = 0;
583 	char *q;
584 
585 	if (line == NULL && p == NULL) {
586 		/* It's the very first time */
587 		return (NULL);
588 	} else if (line != NULL) {
589 		/* Initialize with a new line */
590 		q = skipb(line);
591 	} else {
592 		/* Restore previous line. */
593 		*p = saved;
594 		q = skipb(p);
595 	}
596 	/* q is at the beginning of a token or at EOL, p is irrelevant. */
597 
598 	if (*q == '\0') {
599 		/* It's at EOL. */
600 		p = q;
601 	} else if (in_specials(*q)) {
602 		/* We have a special-character token. */
603 		p = q + 1;
604 	} else if (*q == '#') {
605 		/* The whole rest of the line is a comment token. */
606 		return (NULL);
607 	} else {
608 		/* We have a word token. */
609 		p = skipover(q);
610 	}
611 	saved = *p;
612 	*p = '\0';
613 
614 	if (p == q) {
615 		/* End of line */
616 		return (NULL);
617 	} else {
618 		return (q);
619 	}
620 }
621 
622 
623 /*
624  * valid_version -- see if a version string was mentioned in the set file.
625  */
626 int
valid_version(const char * vers_name)627 valid_version(const char *vers_name)
628 {
629 
630 	if (Vers == NULL) {
631 		init_tables();
632 	}
633 	return (in_stringtable(Vers, vers_name));
634 }
635 
636 /*
637  * valid_arch -- see if the arch was mentioned in the set file.
638  */
639 int
valid_arch(const char * arch_name)640 valid_arch(const char *arch_name)
641 {
642 
643 	if (Vers == NULL) {
644 		init_tables();
645 	}
646 	return (in_stringtable(Varch, arch_name));
647 }
648 
649 /*
650  * add_valid_version and _arch -- add a name to the table.
651  */
652 static void
add_valid_version(char * vers_name)653 add_valid_version(char *vers_name)
654 {
655 	errlog(BEGIN, "add_valid_version(\"%s\") {", vers_name);
656 	if (Vers == NULL) {
657 		init_tables();
658 	}
659 	Vers = add_to_stringtable(Vers, vers_name);
660 	errlog(END, "}");
661 }
662 
663 static void
add_valid_arch(char * arch_name)664 add_valid_arch(char *arch_name)
665 {
666 
667 	errlog(BEGIN, "add_valid_arch(\"%s\") {", arch_name);
668 	if (Vers == NULL) {
669 		init_tables();
670 	}
671 	Varch = add_to_stringtable(Varch, arch_name);
672 	errlog(END, "}");
673 }
674 
675 /*
676  * init_tables -- creat them when first used.
677  */
678 static void
init_tables(void)679 init_tables(void)
680 {
681 	Vers = create_stringtable(TABLE_INITIAL);
682 	Varch = create_stringtable(TABLE_INITIAL);
683 }
684