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 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <dlfcn.h>
33 #include <dirent.h>
34 #include <libgen.h>
35 #include <sys/param.h>
36 #include <errno.h>
37
38 #include "parser.h"
39 #include "errlog.h"
40
41 static char const *ARCH_I386 = "i386";
42 static char const *ARCH_SPARC = "sparc";
43 static char const *ARCH_SPARCV9 = "sparcv9";
44 static char const *ARCH_IA64 = "ia64";
45 static char const *ARCH_AMD64 = "amd64";
46 static char const *ARCH_ALL = "all";
47
48 static int dofiles(const Translator_info *);
49 static int read_spec(const Translator_info *, char *);
50
51 static int Curlineno;
52
53 xlator_keyword_t *keywordlist;
54
55 /*
56 * frontend entry point
57 * returns the number of errors encountered
58 */
59 int
frontend(const Translator_info * T_info)60 frontend(const Translator_info *T_info)
61 {
62 int retval, i = 0, errors = 0;
63
64 keywordlist = xlator_init(T_info);
65 if (keywordlist == NULL) {
66 errlog(ERROR, "Error: Unable to get keywordlist\n");
67 return (1);
68 }
69
70 if (T_info->ti_verbosity >= STATUS) {
71 errlog(STATUS, "interesting keywords:\n");
72 while (keywordlist[i].key != NULL) {
73 errlog(STATUS, "\t%s\n", keywordlist[i].key);
74 ++i;
75 };
76 }
77
78 retval = xlator_startlib(T_info->ti_liblist);
79 switch (retval) {
80 case XLATOR_SKIP:
81 if (T_info->ti_verbosity >= STATUS)
82 errlog(STATUS, "Skipping %s\n", T_info->ti_liblist);
83 retval = 0;
84 break;
85
86 case XLATOR_NONFATAL:
87 ++errors;
88 retval = 0;
89 break;
90
91 case XLATOR_SUCCESS:
92 retval = dofiles(T_info);
93 errors += retval;
94 if ((retval = xlator_endlib()) != XLATOR_SUCCESS)
95 ++errors;
96 retval = 0;
97 break;
98
99 default:
100 errlog(ERROR | FATAL,
101 "Error: Invalid return code from xlator_startlib()\n");
102 exit(1);
103 }
104
105 if ((retval = xlator_end()) != XLATOR_SUCCESS)
106 ++errors;
107
108 return (errors);
109 }
110
111 /*
112 * dofiles(const Translator_info *T_info);
113 * iterate through files specified in the command line and process
114 * them one by one
115 * requires spec files to have a ".spec" suffix
116 * returns the number of errors;
117 */
118 static int
dofiles(const Translator_info * T_info)119 dofiles(const Translator_info *T_info)
120 {
121 int nfiles, flen, findex, retval = 0, errors = 0;
122
123 nfiles = T_info->ti_nfiles;
124
125 for (findex = 0; findex < nfiles; ++findex) {
126 flen = strlen(filelist[findex]);
127 if ((flen <= 5) ||
128 strcmp(&filelist[findex][flen-5], ".spec") != 0) {
129 errlog(ERROR,
130 "Error: File specified does not have the "
131 ".spec extension: %s\n", filelist[findex]);
132 ++errors;
133 continue;
134 };
135 retval = read_spec(T_info, filelist[findex]);
136 errors += retval;
137 }
138 return (errors);
139 }
140
141 /*
142 * read_spec -
143 * Given a filename, this function will reads the spec file to
144 * recognize keywords which it passes along with the corresponding
145 * value to the back-end translator to process. The following
146 * back-end interfaces are called:
147 * xlator_startfile
148 * xlator_start_if
149 * xlator_take_kvpair
150 * xlator_end_if
151 * xlator_endfile
152 */
153 static int
read_spec(const Translator_info * T_info,char * spec_filename)154 read_spec(const Translator_info *T_info, char *spec_filename)
155 {
156 FILE *spec_fp;
157 Meta_info meta_info;
158 char key[BUFSIZ], *value = NULL, *p = NULL;
159 char *buf2 = NULL;
160 int retval = 0, errors = 0, ki = 0; /* keyword indicator */
161 int start_if_fail = 0, skip_if = 0;
162 int extends_err = 0;
163
164 meta_info.mi_ext_cnt = 0; /* All info is non-extends */
165 meta_info.mi_flags = 0;
166
167 retval = xlator_startfile(spec_filename);
168
169 switch (retval) {
170 case XLATOR_SKIP:
171 if (T_info->ti_verbosity >= WARNING)
172 errlog(WARNING, "Warning: Skipping %s\n",
173 spec_filename);
174 return (errors);
175
176 case XLATOR_NONFATAL:
177 errlog(ERROR, "Error in xlator_startfile\n");
178 ++errors;
179 return (errors);
180
181 case XLATOR_SUCCESS:
182 break;
183
184 default:
185 errlog(ERROR,
186 "Error: Invalid return code from xlator_startfile()\n");
187 ++errors;
188 return (errors);
189 };
190
191 /* file processing */
192 spec_fp = fopen(spec_filename, "r");
193 if (spec_fp == NULL) {
194 errlog(ERROR, "Error: Unable to open spec file %s: %s\n",
195 spec_filename, strerror(errno));
196 ++errors;
197 return (errors);
198 }
199
200 (void) strncpy(meta_info.mi_filename, spec_filename, BUFSIZ);
201 meta_info.mi_line_number = 0;
202 Curlineno = meta_info.mi_line_number;
203 while (meta_info.mi_nlines = readline(&buf2, spec_fp)) {
204 meta_info.mi_line_number += meta_info.mi_nlines;
205 Curlineno = meta_info.mi_line_number;
206 if (!non_empty(buf2)) {
207 free(buf2);
208 buf2 = NULL;
209 continue;
210 }
211 p = realloc(value, sizeof (char)*(strlen(buf2)+1));
212 if (p == NULL) {
213 errlog(ERROR | FATAL,
214 "Error: Unable to allocate memory for "
215 "value: %d\n", errno);
216 }
217 value = p;
218 split(buf2, key, value);
219 ki = interesting_keyword(keywordlist, key);
220 switch (ki) {
221 case XLATOR_KW_FUNC: /* Function keyword */
222 case XLATOR_KW_DATA: /* Data keyword */
223 meta_info.mi_extended = 0;
224 retval = xlator_start_if(meta_info, ki, value);
225 switch (retval) {
226 case XLATOR_FATAL: /* FATAL ERROR */
227 if (T_info->ti_verbosity >= STATUS) {
228 errlog(STATUS,
229 "Error in xlator_start_if: ");
230 }
231 ++errors;
232 return (errors);
233 case XLATOR_NONFATAL: /* NON-FATAL ERROR */
234 if (T_info->ti_verbosity >= STATUS)
235 errlog(STATUS,
236 "Error in xlator_start_if\n");
237 ++errors;
238 start_if_fail = 1;
239 break;
240 case XLATOR_SUCCESS: /* OK */
241 start_if_fail = 0;
242 extends_err = check4extends(spec_filename,
243 value, T_info->ti_archtoken, spec_fp);
244 switch (extends_err) {
245 case -1: /* Error */
246 errlog(ERROR, "\"%s\", line %d: "
247 "Error occurred while "
248 "checking for extends clause\n",
249 spec_filename, Curlineno);
250 ++errors;
251 /*FALLTHRU*/
252 case 0: /* No Extends */
253 break;
254 case 1: /* Extends */
255 meta_info.mi_extended = 1;
256 extends_err = do_extends(meta_info,
257 T_info, value);
258 if (extends_err) {
259 errors += extends_err;
260 }
261 break;
262 default: /* Programmer Error */
263 errlog(ERROR | FATAL,
264 "Error: invalid return from "
265 "check4extends %d\n", extends_err);
266 }
267 break;
268 case XLATOR_SKIP: /* SKIP */
269 if (T_info->ti_verbosity >= WARNING)
270 errlog(WARNING, "Warning: Skipping "
271 "interface %s\n", value);
272 skip_if = 1;
273 start_if_fail = 0;
274 break;
275 default:
276 /* Invalid Return */
277 errlog(ERROR | FATAL,
278 "Error: Invalid return code "
279 "from xlator_start_if (): %d\n", retval);
280 }
281 break;
282 case XLATOR_KW_END: /* END keyword */
283 if (start_if_fail == 0 && skip_if == 0) {
284 retval = xlator_end_if(meta_info, value);
285 if (retval)
286 ++errors;
287 }
288 skip_if = 0;
289 break;
290 case XLATOR_KW_NOTFOUND:
291 if (T_info->ti_verbosity >= TRACING)
292 errlog(TRACING, "uninteresting keyword: %s\n",
293 key);
294 break;
295 default:
296 if (skip_if == 0 && start_if_fail == 0) {
297 retval = xlator_take_kvpair(meta_info,
298 ki, value);
299 if (retval) {
300 if (T_info->ti_verbosity >= STATUS)
301 errlog(STATUS, "Error in "
302 "xlator_take_kvpair\n");
303 ++errors;
304 }
305 }
306 }
307 free(buf2);
308 buf2 = NULL;
309 }
310
311 if ((retval = xlator_endfile()) != XLATOR_SUCCESS) {
312 if (T_info->ti_verbosity >= STATUS)
313 errlog(STATUS, "Error in xlator_endfile\n");
314 ++errors;
315 }
316 free(p);
317 (void) fclose(spec_fp);
318 return (errors);
319 }
320
321 /*
322 * interesting_keyword(char **keywordlist, const char *key) {
323 * returns the token associated with key if key is found in keywordlist
324 * returns XLATOR_KW_NOTFOUND if key is NOT found in keywordlist
325 * "Function" and "End" are always interesting, return XLATOR_KW_FUNC
326 * and XLATOR_KW_DATA respectively;
327 * "End" is always interesting, return XLATOR_KW_END;
328 *
329 */
330 int
interesting_keyword(xlator_keyword_t * keywordlist,const char * key)331 interesting_keyword(xlator_keyword_t *keywordlist, const char *key)
332 {
333 int i = 0;
334
335 if (strcasecmp(key, "data") == 0) {
336 return (XLATOR_KW_DATA);
337 }
338 if (strcasecmp(key, "function") == 0) {
339 return (XLATOR_KW_FUNC);
340 }
341
342 if (strcasecmp(key, "end") == 0)
343 return (XLATOR_KW_END);
344
345 while (keywordlist[i].key != NULL) {
346 if (strcasecmp(keywordlist[i].key, key) == 0)
347 return (keywordlist[i].token);
348 ++i;
349 }
350 return (XLATOR_KW_NOTFOUND);
351 }
352
353 /*
354 * line_to_buf(char *dest, const char *src) {
355 * appends src to dest, dynamically increasing the size of dest.
356 * replaces the trailing '\' continuation character with a space.
357 *
358 * if src is continuation of dest, dest != NULL, and
359 * the last character in dest before the newline must be a `\'
360 * if src is not continuation of dest, then dest must be NULL
361 */
362 char *
line_to_buf(char * dest,const char * src)363 line_to_buf(char *dest, const char *src)
364 {
365 int slen = strlen(src);
366 int dlen;
367
368 if (dest == NULL) {
369 /* We're being called for the first time */
370 dest = malloc(sizeof (char) * (slen + 1));
371 if (dest == NULL) {
372 errlog(ERROR | FATAL,
373 "Error: Unable to allocate memory for dest\n");
374 }
375 (void) strcpy(dest, src);
376 return (dest);
377 }
378
379 dlen = strlen(dest);
380
381 dest = realloc(dest, (size_t)(sizeof (char) * (dlen+slen+1)));
382 if (dest == NULL) {
383 errlog(ERROR | FATAL,
384 "Error: Unable to allocate memory for dest\n");
385 }
386
387 if (dlen > 1) {
388 /*
389 * remove continuation character
390 * we replace the '\' from the previous line with a space
391 */
392 if (dest[dlen-2] == '\\') {
393 dest[dlen-2] = ' ';
394 }
395 }
396
397 /* join the two strings */
398 (void) strcat(dest, src);
399
400 return (dest);
401 }
402
403 /*
404 * non_empty(const char *str)
405 * assumes str is non null
406 * checks if str is a non empty string
407 * returns 1 if string contains non whitespace
408 * returns 0 if string contains only whitespace
409 */
410 int
non_empty(const char * str)411 non_empty(const char *str)
412 {
413 while (*str != '\0') {
414 if (!isspace(*str))
415 return (1);
416 ++str;
417 };
418 return (0);
419 }
420
421 /*
422 * split(const char *line, char *key, char *value);
423 * splits the line into keyword (key) and value pair
424 */
425 void
split(const char * line,char * key,char * value)426 split(const char *line, char *key, char *value)
427 {
428 char *p;
429
430 p = (char *)line;
431
432 /* skip leading whitespace */
433 while (isspace(*p)&& *p != '\0')
434 ++p;
435
436 /* copy keyword from line into key */
437 while (!isspace(*p) && *p != '\0')
438 *key++ = *p++;
439
440 *key = '\0';
441
442 /* skip whitespace */
443 while (isspace(*p) && *p != '\0')
444 p++;
445
446 (void) strcpy(value, p);
447
448 }
449
450 /*
451 * check4extends(char *filename, char *value, int arch, FILE *fp)
452 * if no arch keyword is found or there is a MATCHING arch keyword
453 * returns 1 if value is of the form "data|function name extends"
454 * -1 for error
455 * 0 no other keyword after the function name
456 * else
457 * return 0
458 *
459 * filename is used only for error reporting
460 */
461 int
check4extends(const char * filename,const char * value,int arch,FILE * fp)462 check4extends(const char *filename, const char *value, int arch, FILE *fp)
463 {
464 char fun[BUFSIZ];
465 char extends[BUFSIZ];
466 int n;
467
468 if (arch_match(fp, arch)) {
469 split(value, fun, extends);
470 n = strlen(extends);
471 if (extends[n-1] == '\n')
472 extends[n-1] = '\0';
473 if (strncasecmp("extends", extends, 7) == 0) {
474 return (1);
475 } else {
476 if (*extends != '\0') {
477 errlog(ERROR, "\"%s\", line %d: Error: "
478 "Trailing garbage after function name\n",
479 filename, Curlineno);
480 return (-1);
481 }
482 }
483 }
484 return (0);
485 }
486
487 /*
488 * remcomment (char *buf)
489 * replace comments with single whitespace
490 */
491 /* XXX: There is currently no way to escape a comment character */
492 void
remcomment(char const * buf)493 remcomment(char const *buf)
494 {
495 char *p;
496 p = strchr(buf, '#');
497 if (p) {
498 *p = ' ';
499 *(p+1) = '\0';
500 }
501 }
502
503 /*
504 * arch_strtoi()
505 *
506 * input: string
507 * return: XLATOR_I386 if string == ARCH_I386
508 * XLATOR_SPARC if string == ARCH_SPARC
509 * XLATOR_SPARCV9 if string == ARCH_SPARCV9
510 * XLATOR_IA64 if string == ARCH_IA64
511 * XLATOR_AMD64 if string == ARCH_AMD64
512 * XLATOR_ALLARCH if string == ARCH_ALL
513 * 0 if outside the known set {i386, sparc, sparcv9, ia64, amd64}.
514 */
515 int
arch_strtoi(const char * arch_str)516 arch_strtoi(const char *arch_str)
517 {
518 if (arch_str != NULL) {
519 if (strcmp(arch_str, ARCH_I386) == 0)
520 return (XLATOR_I386);
521 else if (strcmp(arch_str, ARCH_SPARC) == 0)
522 return (XLATOR_SPARC);
523 else if (strcmp(arch_str, ARCH_SPARCV9) == 0)
524 return (XLATOR_SPARCV9);
525 else if (strcmp(arch_str, ARCH_IA64) == 0)
526 return (XLATOR_IA64);
527 else if (strcmp(arch_str, ARCH_AMD64) == 0)
528 return (XLATOR_AMD64);
529 else if (strcmp(arch_str, ARCH_ALL) == 0)
530 return (XLATOR_ALLARCH);
531 } else {
532 errlog(ERROR, "\"%s\", line %d: Error: "
533 "arch keyword with no value");
534 }
535 return (0);
536 }
537
538 int
readline(char ** buffer,FILE * fp)539 readline(char **buffer, FILE *fp)
540 {
541 int nlines = 0;
542 int len;
543 char buf[BUFSIZ];
544
545 if (fgets(buf, BUFSIZ, fp)) {
546 nlines++;
547 /* replace comments with single whitespace */
548 remcomment(buf);
549
550 /* get complete line */
551 *buffer = line_to_buf(*buffer, buf); /* append buf to buffer */
552 len = strlen(buf);
553 if (len > 1) {
554 /* handle continuation lines */
555 while (buf[len-2] == '\\') {
556 if (!fgets(buf, BUFSIZ, fp)) {
557 *buffer = line_to_buf(*buffer, buf);
558 break;
559 }
560 nlines++;
561 len = strlen(buf);
562 *buffer = line_to_buf(*buffer, buf);
563 }
564 } /* end of 'get complete line' */
565 }
566 return (nlines);
567 }
568