xref: /illumos-gate/usr/src/cmd/abi/spectrans/parser/frontend.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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
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
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
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
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 *
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
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
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
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
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
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
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