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