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 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <libgen.h>
34 #include <errno.h>
35 #include "parser.h"
36 #include "errlog.h"
37 
38 static int find_fun(char *key, char *value, char *parentfun);
39 
40 /*
41  * handles the extends clause of the 'function' keyword
42  * Returns the number of errors encountered
43  * This function is recursive.
44  */
45 int
46 do_extends(const Meta_info parentM, const Translator_info *T_info, char *value)
47 {
48 	static int extends_count = 0;
49 	char funname[BUFSIZ], filename[MAXPATHLEN], parentfun[BUFSIZ],
50 	    buf[BUFSIZ], key[20];
51 	char *ifilename, *f, *p;
52 	char *localvalue = NULL, *buf2 = NULL;
53 	FILE *efp;
54 	Meta_info M;
55 	int found = 0, errors = 0, ki = 0;
56 	int retval;
57 	int scan;
58 
59 	++extends_count;
60 
61 	if (extends_count > MAX_EXTENDS) {
62 		errlog(ERROR, "\"%s\", line %d: Error: Too many levels of "
63 		    "extends\n", parentM.mi_filename, parentM.mi_line_number);
64 		++errors;
65 		goto ret;
66 	}
67 
68 	scan = sscanf(value, "%s %s %s %s", funname, buf, filename, parentfun);
69 	switch (scan) {
70 	case 0: /* funname not set */
71 	case 1: /* buf not set, though ignored */
72 	case 2: /* filename not set */
73 		errlog(ERROR, "\"%s\", line %d: Error: Couldn't parse "
74 		    "'data' or 'function' line\n",
75 		    parentM.mi_filename, parentM.mi_line_number);
76 		++errors;
77 		goto ret;
78 		break;
79 	case 3:
80 		(void) strncpy(parentfun, funname, BUFSIZ);
81 		parentfun[BUFSIZ-1] = '\0';
82 		break;
83 	default:
84 		break;
85 	}
86 
87 	/* All info is from parent file - extends */
88 	M.mi_ext_cnt = extends_count;
89 
90 	if (T_info->ti_verbosity >= TRACING) {
91 		errlog(TRACING, "Extending file %s\nExtending function %s\n"
92 		    "SPEC's from %s\n", filename, parentfun,
93 		    T_info->ti_dash_I);
94 	}
95 
96 	f = pathfind(T_info->ti_dash_I, filename, "f");
97 	if (f == NULL) {
98 		errlog(ERROR, "\"%s\", line %d: Error: Unable to find spec "
99 		    "file \"%s\"\n", parentM.mi_filename,
100 		    parentM.mi_line_number, filename);
101 		++errors;
102 		goto ret;
103 	}
104 	ifilename = strdup(f);
105 	if (ifilename == NULL) {
106 		errlog(ERROR | FATAL, "Error: strdup() of filename failed\n");
107 	}
108 	efp = fopen(ifilename, "r");
109 	if (efp == NULL) {
110 		errlog(ERROR, "\"%s\", line %d: Error: Unable to open "
111 		    "file \"%s\"\n", parentM.mi_filename,
112 		    parentM.mi_line_number, ifilename);
113 		free(ifilename);
114 		++errors;
115 		goto ret;
116 	}
117 
118 	(void) strncpy(M.mi_filename, ifilename, MAXPATHLEN);
119 	M.mi_line_number = 0;
120 
121 	/* search for begin function */
122 	while (M.mi_nlines = readline(&buf2, efp)) {
123 		M.mi_line_number += M.mi_nlines;
124 
125 		if (!non_empty(buf2)) {	 /* is line non empty */
126 			free(buf2);
127 			buf2 = NULL;
128 			continue;
129 		}
130 		p = realloc(localvalue, sizeof (char)*(strlen(buf2)+1));
131 		if (p == NULL) {
132 			errlog(ERROR | FATAL, "Error (do_extends): "
133 			    "Unable to allocate memory\n");
134 		}
135 		localvalue = p;
136 		split(buf2, key, localvalue);
137 		if ((found = find_fun(key, localvalue, parentfun))) {
138 			/* check if architecture matches */
139 			if (found = arch_match(efp, T_info->ti_archtoken))
140 				break;
141 		}
142 		free(buf2);
143 		buf2 = NULL;
144 	}
145 
146 	if (found) {
147 		int extends_err = 0;
148 		static int extends_warn = 0;
149 		extends_err = check4extends(ifilename, localvalue,
150 		T_info->ti_archtoken, efp);
151 		switch (extends_err) {
152 		case -1:	/* Error */
153 			errlog(ERROR, "\"%s\", line %d: Error occurred while "
154 			    "checking for extends clause\n",
155 			    M.mi_filename, M.mi_line_number);
156 			++errors;
157 			/*FALLTHRU*/
158 		case 0:		/* No Extends */
159 			break;
160 		case 1:		/* Extends */
161 			/*
162 			 * Warning on more then one level of extends
163 			 * but only warn once.
164 			 */
165 			if (extends_count == 1) {
166 				extends_warn = 1;
167 			}
168 			if ((extends_err = do_extends(M, T_info, localvalue))
169 			    != 0) {
170 				if (extends_count == 1) {
171 					errlog(ERROR, "\"%s\", line %d: "
172 					    "Error occurred while "
173 					    "processing 'extends'\n",
174 					    parentM.mi_filename,
175 					    parentM.mi_line_number);
176 				}
177 				errors += extends_err;
178 			}
179 			if (extends_warn == 1 && extends_count == 1) {
180 				errlog(ERROR, "\"%s\", line %d: "
181 				    "Warning: \"%s\" does not extend "
182 				    "a base specification",
183 				    parentM.mi_filename,
184 				    parentM.mi_line_number,
185 				    funname);
186 			}
187 			break;
188 		default:	/* Programmer Error */
189 			errlog(ERROR | FATAL,
190 			    "Error: invalid return from "
191 			    "check4extends: %d\n", extends_err);
192 		}
193 
194 		free(buf2);
195 		buf2 = NULL;
196 
197 		while (M.mi_nlines = readline(&buf2, efp)) {
198 			M.mi_line_number += M.mi_nlines;
199 
200 			if (!non_empty(buf2)) { /* is line non empty */
201 				free(buf2);
202 				buf2 = NULL;
203 				continue;
204 			}
205 			p = realloc(localvalue, sizeof (char)*(strlen(buf2)+1));
206 			if (p == NULL) {
207 				p = realloc(NULL,
208 				    sizeof (char)*(strlen(buf2)+1));
209 				if (p == NULL) {
210 					errlog(ERROR | FATAL,
211 					    "Error: unable to "
212 					    "allocate memory\n");
213 				}
214 			}
215 			localvalue = p;
216 			split(buf2, key, localvalue);
217 			ki = interesting_keyword(keywordlist, key);
218 			switch (ki) {
219 			case XLATOR_KW_END:
220 				goto end;
221 				break;
222 			case XLATOR_KW_FUNC:
223 			case XLATOR_KW_DATA:
224 				errlog(ERROR, "\"%s\", line %d: "
225 				    "Error: Interface is missing \"end\"\n"
226 				    "\"%s\", line %d: Error while processing "
227 				    "%s\n", M.mi_filename, M.mi_line_number,
228 				    parentM.mi_filename,
229 				    parentM.mi_line_number, ifilename);
230 				++errors;
231 				goto end;
232 				break;
233 			case XLATOR_KW_NOTFOUND:
234 				if (T_info->ti_verbosity >= TRACING)
235 					errlog(STATUS,
236 					    "uninteresting keyword: %s\n", key);
237 				break;
238 			default:
239 				retval = xlator_take_kvpair(M, ki, localvalue);
240 				if (retval) {
241 					if (T_info->ti_verbosity >= STATUS)
242 						errlog(STATUS,
243 						    "Error in "
244 						    "xlator_take_kvpair\n");
245 					++errors;
246 				}
247 			}
248 			free(buf2);
249 			buf2 = NULL;
250 		}
251 	} else {
252 		errlog(ERROR, "\"%s\", line %d: Error: Unable to find "
253 		    "function %s in %s\n", parentM.mi_filename,
254 		    parentM.mi_line_number, parentfun, ifilename);
255 		++errors;
256 	}
257 end:
258 	(void) fclose(efp);
259 	free(localvalue);
260 	free(ifilename);
261 	free(buf2);
262 ret:
263 	extends_count--;
264 	return (errors);
265 }
266 
267 /*
268  * find_fun()
269  *    given a key value pair, and the name of the function you are
270  *    searching for in the SPEC source file, this function returns 1
271  *    if the beginning of the function in the SPEC source file is found.
272  *    returns 0 otherwise.
273  */
274 static int
275 find_fun(char *key, char *value, char *parentfun)
276 {
277 	char pfun[BUFSIZ];
278 
279 	if (strcasecmp(key, "function") != 0 &&
280 		strcasecmp(key, "data") != 0) {
281 		return (0);
282 	}
283 
284 	(void) sscanf(value, "%1023s", pfun);
285 
286 	if (strcmp(pfun, parentfun) == 0) {
287 		return (1);
288 	}
289 
290 	return (0);
291 }
292 
293 /*
294  * arch_match(FILE *fp, int arch)
295  * This function takes a FILE pointer, and an architecture token
296  * The FILE pointer is assumed to point at the beginning of a Function
297  * or Data specification (specifically at the first line of spec AFTER
298  * the Function or Data line)
299  * It reads all the way to the "End" line.
300  * If it finds an "arch" keyword along the way, it is checked to see if
301  * it matches the architecture currently being generated and returns
302  * 1 if a match is found.  If a match is not found, it returns a
303  * 0. If no "arch" keyword is found, it returns 1.
304  *
305  * XXX - the algorithm in arch_match is very inefficient. it read through
306  * the file to find "arch" and rewinds before returning.
307  * Later all the data that was skipped while searching for "arch" may
308  * be needed and it is re-read from the disk. It would be nice to just
309  * read the data once.
310  */
311 int
312 arch_match(FILE *fp, int arch)
313 {
314 	off_t offset;
315 	char key[20], buf[BUFSIZ], *buf2 = NULL, *localvalue = NULL, *p;
316 	int len;
317 	int has_arch = 0;
318 	int archset = 0;
319 
320 	offset = ftello(fp);
321 	if (offset == -1) {
322 		errlog(ERROR|FATAL, "Unable to determine file position\n");
323 	}
324 
325 	while (fgets(buf, BUFSIZ, fp)) {
326 		/* replace comments with single whitespace */
327 		remcomment(buf);
328 
329 		/* get complete line */
330 		buf2 = line_to_buf(buf2, buf); /* append buf to buf2 */
331 		len = strlen(buf);
332 		if (len > 1) {
333 			while (buf[len-2] == '\\') {
334 				if (!fgets(buf, BUFSIZ, fp)) {
335 					buf2 = line_to_buf(buf2, buf);
336 					break;
337 				}
338 				len = strlen(buf);
339 				buf2 = line_to_buf(buf2, buf);
340 			}
341 		} /* end of 'get complete line' */
342 
343 		if (!non_empty(buf2)) { /* is line non empty */
344 			free(buf2);
345 			buf2 = NULL;
346 			continue;
347 		}
348 		p = realloc(localvalue, sizeof (char)*(strlen(buf2)+1));
349 		if (p == NULL) {
350 			p = realloc(NULL,
351 				sizeof (char)*(strlen(buf2)+1));
352 			if (p == NULL) {
353 				errlog(ERROR | FATAL,
354 					"Error: unable to "
355 					"allocate memory\n");
356 			}
357 		}
358 		localvalue = p;
359 		split(buf2, key, localvalue);
360 		if (strcasecmp(key, "arch") == 0) {
361 			char *alist = localvalue, *a;
362 			has_arch = 1;
363 			while ((a = strtok(alist, " ,\n")) != NULL) {
364 				archset = arch_strtoi(a);
365 				if (arch & archset) {
366 					free(buf2);
367 					free(p);
368 					if (fseeko(fp, offset, SEEK_SET) < 0) {
369 						errlog(ERROR|FATAL,
370 						    "%s", strerror(errno));
371 					}
372 					return (1);
373 				}
374 				alist = NULL;
375 			}
376 		} else if (strcasecmp(key, "end") == 0) {
377 			break;
378 		}
379 		free(buf2);
380 		buf2 = NULL;
381 	}
382 
383 end:
384 	free(buf2);
385 	free(p);
386 
387 	if (fseeko(fp, offset, SEEK_SET) < 0) {
388 		errlog(ERROR|FATAL, "%s", strerror(errno));
389 	}
390 	if (has_arch == 0)
391 		return (1);
392 
393 	return (0);
394 }
395