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