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