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
38static 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 */
45int
46do_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	}
257end:
258	(void) fclose(efp);
259	free(localvalue);
260	free(ifilename);
261	free(buf2);
262ret:
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 */
274static int
275find_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 */
311int
312arch_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
383end:
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