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