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