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
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
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
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
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 *
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
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
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
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
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
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
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