parse.c revision 0eb10989
1/* $Xorg: parse.c,v 1.6 2001/02/09 02:03:16 xorgcvs Exp $ */
2/*
3
4Copyright (c) 1993, 1994, 1998 The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26*/
27/* $XFree86: xc/config/makedepend/parse.c,v 1.11 2001/12/17 20:52:22 dawes Exp $ */
28
29#include "def.h"
30
31extern char	*directives[];
32extern struct inclist	inclist[ MAXFILES ],
33			*inclistnext,
34			maininclist;
35extern char	*includedirs[ ],
36		**includedirsnext;
37
38static int deftype (char *line, struct filepointer *filep,
39		    struct inclist *file_red, struct inclist *file,
40		    int parse_it);
41static int zero_value(char *filename, char *exp, struct filepointer *filep,
42		    struct inclist *file_red);
43static int merge2defines(struct inclist *file1, struct inclist *file2);
44
45static int
46gobble(struct filepointer *filep, struct inclist *file,
47       struct inclist *file_red)
48{
49	char	*line;
50	int	type;
51
52	while ((line = getnextline(filep))) {
53		switch(type = deftype(line, filep, file_red, file, FALSE)) {
54		case IF:
55		case IFFALSE:
56		case IFGUESSFALSE:
57		case IFDEF:
58		case IFNDEF:
59			type = gobble(filep, file, file_red);
60			while ((type == ELIF) || (type == ELIFFALSE) ||
61			       (type == ELIFGUESSFALSE))
62			    type = gobble(filep, file, file_red);
63			if (type == ELSE)
64			        (void)gobble(filep, file, file_red);
65			break;
66		case ELSE:
67		case ENDIF:
68			debug(0,("%s, line %d: #%s\n",
69				file->i_file, filep->f_line,
70				directives[type]));
71			return(type);
72		case DEFINE:
73		case UNDEF:
74		case INCLUDE:
75		case INCLUDEDOT:
76		case PRAGMA:
77		case ERROR:
78		case IDENT:
79		case SCCS:
80		case EJECT:
81		case WARNING:
82		case INCLUDENEXT:
83		case INCLUDENEXTDOT:
84			break;
85		case ELIF:
86		case ELIFFALSE:
87		case ELIFGUESSFALSE:
88			return(type);
89		case -1:
90			warning("%s", file_red->i_file);
91			if (file_red != file)
92				warning1(" (reading %s)", file->i_file);
93			warning1(", line %d: unknown directive == \"%s\"\n",
94				filep->f_line, line);
95			break;
96		}
97	}
98	return(-1);
99}
100
101/*
102 * Decide what type of # directive this line is.
103 */
104static int
105deftype (char *line, struct filepointer *filep,
106	     struct inclist *file_red, struct inclist *file, int parse_it)
107{
108	register char	*p;
109	char	*directive, savechar, *q;
110	register int	ret;
111
112	/*
113	 * Parse the directive...
114	 */
115	directive=line+1;
116	while (*directive == ' ' || *directive == '\t')
117		directive++;
118
119	p = directive;
120	while ((*p == '_') || (*p >= 'a' && *p <= 'z'))
121		p++;
122	savechar = *p;
123	*p = '\0';
124	ret = match(directive, directives);
125	*p = savechar;
126
127	/* If we don't recognize this compiler directive or we happen to just
128	 * be gobbling up text while waiting for an #endif or #elif or #else
129	 * in the case of an #elif we must check the zero_value and return an
130	 * ELIF or an ELIFFALSE.
131	 */
132
133	if (ret == ELIF && !parse_it)
134	{
135	    while (*p == ' ' || *p == '\t')
136		p++;
137	    /*
138	     * parse an expression.
139	     */
140	    debug(0,("%s, line %d: #elif %s ",
141		   file->i_file, filep->f_line, p));
142	    ret = zero_value(file->i_file, p, filep, file_red);
143	    if (ret != IF)
144	    {
145		debug(0,("false...\n"));
146		if (ret == IFFALSE)
147		    return(ELIFFALSE);
148		else
149		    return(ELIFGUESSFALSE);
150	    }
151	    else
152	    {
153		debug(0,("true...\n"));
154		return(ELIF);
155	    }
156	}
157
158	if (ret < 0 || ! parse_it)
159		return(ret);
160
161	/*
162	 * now decide how to parse the directive, and do it.
163	 */
164	while (*p == ' ' || *p == '\t')
165		p++;
166	q = p + strlen(p);
167	do {
168		q--;
169	} while (*q == ' ' || *q == '\t');
170	q[1] = '\0';
171	switch (ret) {
172	case IF:
173		/*
174		 * parse an expression.
175		 */
176		ret = zero_value(file->i_file, p, filep, file_red);
177		debug(0,("%s, line %d: %s #if %s\n",
178			 file->i_file, filep->f_line, ret?"false":"true", p));
179		break;
180	case IFDEF:
181	case IFNDEF:
182		debug(0,("%s, line %d: #%s %s\n",
183			file->i_file, filep->f_line, directives[ret], p));
184	case UNDEF:
185		/*
186		 * separate the name of a single symbol.
187		 */
188		while (isalnum(*p) || *p == '_')
189			*line++ = *p++;
190		*line = '\0';
191		break;
192	case INCLUDE:
193	case INCLUDENEXT:
194		debug(2,("%s, line %d: #include%s %s\n",
195			file->i_file, filep->f_line,
196			(ret == INCLUDE) ? "" : "_next", p));
197
198		/* Support ANSI macro substitution */
199		while (1) {
200			struct symtab **sym;
201
202			if (!*p || *p == '"' || *p == '<')
203				break;
204
205		    	sym = isdefined(p, file_red, NULL);
206			if (!sym)
207				break;
208
209			p = (*sym)->s_value;
210			debug(3,("%s : #includes SYMBOL %s = %s\n",
211			       file->i_incstring,
212			       (*sym) -> s_name,
213			       (*sym) -> s_value));
214			/* mark file as having included a 'soft include' */
215			file->i_flags |= INCLUDED_SYM;
216		}
217
218		/*
219		 * Separate the name of the include file.
220		 */
221		while (*p && *p != '"' && *p != '<')
222			p++;
223		if (! *p)
224			return(-2);
225		if (*p++ == '"') {
226			if (ret == INCLUDE)
227				ret = INCLUDEDOT;
228			else
229				ret = INCLUDENEXTDOT;
230			while (*p && *p != '"')
231				*line++ = *p++;
232		} else
233			while (*p && *p != '>')
234				*line++ = *p++;
235		*line = '\0';
236		break;
237	case DEFINE:
238		/*
239		 * copy the definition back to the beginning of the line.
240		 */
241		strcpy (line, p);
242		break;
243	case ELSE:
244	case ENDIF:
245	case ELIF:
246	case PRAGMA:
247	case ERROR:
248	case IDENT:
249	case SCCS:
250	case EJECT:
251	case WARNING:
252		debug(0,("%s, line %d: #%s\n",
253			file->i_file, filep->f_line, directives[ret]));
254		/*
255		 * nothing to do.
256		 */
257		break;
258	}
259	return(ret);
260}
261
262struct symtab **
263fdefined(char *symbol, struct inclist *file, struct inclist **srcfile)
264{
265	struct inclist	**ip;
266	struct symtab	**val;
267	int	i;
268	static int	recurse_lvl = 0;
269
270	if (file->i_flags & DEFCHECKED)
271		return(NULL);
272	debug(2,("Looking for %s in %s\n", symbol, file->i_file));
273	file->i_flags |= DEFCHECKED;
274	if ((val = slookup(symbol, file)))
275		debug(1,("%s defined in %s as %s\n",
276			 symbol, file->i_file, (*val)->s_value));
277	if (val == NULL && file->i_list)
278	{
279		for (ip = file->i_list, i=0; i < file->i_listlen; i++, ip++)
280			if (file->i_merged[i]==FALSE) {
281				val = fdefined(symbol, *ip, srcfile);
282				file->i_merged[i]=merge2defines(file,*ip);
283				if (val!=NULL) break;
284			}
285	}
286	else if (val != NULL && srcfile != NULL) *srcfile = file;
287	recurse_lvl--;
288	file->i_flags &= ~DEFCHECKED;
289
290	return(val);
291}
292
293struct symtab **
294isdefined(char *symbol, struct inclist *file, struct inclist **srcfile)
295{
296	struct symtab	**val;
297
298	if ((val = slookup(symbol, &maininclist))) {
299		debug(1,("%s defined on command line\n", symbol));
300		if (srcfile != NULL) *srcfile = &maininclist;
301		return(val);
302	}
303	if ((val = fdefined(symbol, file, srcfile)))
304		return(val);
305	debug(1,("%s not defined in %s\n", symbol, file->i_file));
306	return(NULL);
307}
308
309/*
310 * Return type based on if the #if expression evaluates to 0
311 */
312static int
313zero_value(char *filename,
314	   char *exp,
315	   struct filepointer *filep,
316	   struct inclist *file_red)
317{
318	if (cppsetup(filename, exp, filep, file_red))
319	    return(IFFALSE);
320	else
321	    return(IF);
322}
323
324void
325define2(char *name, char *val, struct inclist *file)
326{
327    int first, last, below;
328    register struct symtab **sp = NULL, **dest;
329    struct symtab *stab;
330
331    /* Make space if it's needed */
332    if (file->i_defs == NULL)
333    {
334	file->i_defs = (struct symtab **)
335			malloc(sizeof (struct symtab*) * SYMTABINC);
336	file->i_ndefs = 0;
337    }
338    else if (!(file->i_ndefs % SYMTABINC))
339	file->i_defs = (struct symtab **)
340			realloc(file->i_defs,
341			   sizeof(struct symtab*)*(file->i_ndefs+SYMTABINC));
342
343    if (file->i_defs == NULL)
344	fatalerr("malloc()/realloc() failure in insert_defn()\n");
345
346    below = first = 0;
347    last = file->i_ndefs - 1;
348    while (last >= first)
349    {
350	/* Fast inline binary search */
351	register char *s1;
352	register char *s2;
353	register int middle = (first + last) / 2;
354
355	/* Fast inline strchr() */
356	s1 = name;
357	s2 = file->i_defs[middle]->s_name;
358	while (*s1++ == *s2++)
359	    if (s2[-1] == '\0') break;
360
361	/* If exact match, set sp and break */
362	if (*--s1 == *--s2)
363	{
364	    sp = file->i_defs + middle;
365	    break;
366	}
367
368	/* If name > i_defs[middle] ... */
369	if (*s1 > *s2)
370	{
371	    below = first;
372	    first = middle + 1;
373	}
374	/* else ... */
375	else
376	{
377	    below = last = middle - 1;
378	}
379    }
380
381    /* Search is done.  If we found an exact match to the symbol name,
382       just replace its s_value */
383    if (sp != NULL)
384    {
385	debug(1,("redefining %s from %s to %s in file %s\n",
386		name, (*sp)->s_value, val, file->i_file));
387	free((*sp)->s_value);
388	(*sp)->s_value = copy(val);
389	return;
390    }
391
392    sp = file->i_defs + file->i_ndefs++;
393    dest = file->i_defs + below + 1;
394    while (sp > dest)
395    {
396	*sp = sp[-1];
397	sp--;
398    }
399    stab = (struct symtab *) malloc(sizeof (struct symtab));
400    if (stab == NULL)
401	fatalerr("malloc()/realloc() failure in insert_defn()\n");
402
403    debug(1,("defining %s to %s in file %s\n", name, val, file->i_file));
404    stab->s_name = copy(name);
405    stab->s_value = copy(val);
406    *sp = stab;
407}
408
409void
410define(char *def, struct inclist *file)
411{
412    char *val;
413
414    /* Separate symbol name and its value */
415    val = def;
416    while (isalnum(*val) || *val == '_')
417	val++;
418    if (*val)
419	*val++ = '\0';
420    while (*val == ' ' || *val == '\t')
421	val++;
422
423    if (!*val)
424	val = "1";
425    define2(def, val, file);
426}
427
428struct symtab **
429slookup(char *symbol, struct inclist *file)
430{
431	register int first = 0;
432	register int last;
433
434	if (file == NULL)
435	    return NULL;
436
437	last = file->i_ndefs - 1;
438
439	while (last >= first)
440	{
441	    /* Fast inline binary search */
442	    register char *s1;
443	    register char *s2;
444	    register int middle = (first + last) / 2;
445
446	    /* Fast inline strchr() */
447	    s1 = symbol;
448	    s2 = file->i_defs[middle]->s_name;
449	    while (*s1++ == *s2++)
450	        if (s2[-1] == '\0') break;
451
452	    /* If exact match, we're done */
453	    if (*--s1 == *--s2)
454	    {
455	        return file->i_defs + middle;
456	    }
457
458	    /* If symbol > i_defs[middle] ... */
459	    if (*s1 > *s2)
460	    {
461	        first = middle + 1;
462	    }
463	    /* else ... */
464	    else
465	    {
466	        last = middle - 1;
467	    }
468	}
469	return(NULL);
470}
471
472static int
473merge2defines(struct inclist *file1, struct inclist *file2)
474{
475	int i;
476
477	if ((file1==NULL) || (file2==NULL) ||
478	    !(file2->i_flags & FINISHED))
479		return 0;
480
481	for (i=0; i < file2->i_listlen; i++)
482		if (file2->i_merged[i]==FALSE)
483			return 0;
484
485	{
486		int first1 = 0;
487		int last1 = file1->i_ndefs - 1;
488
489		int first2 = 0;
490		int last2 = file2->i_ndefs - 1;
491
492                int first=0;
493                struct symtab** i_defs = NULL;
494		int deflen=file1->i_ndefs+file2->i_ndefs;
495
496		debug(2,("merging %s into %s\n",
497			file2->i_file, file1->i_file));
498
499                if (deflen>0)
500                {
501                	/* make sure deflen % SYMTABINC == 0 is still true */
502                	deflen += (SYMTABINC - deflen % SYMTABINC) % SYMTABINC;
503                	i_defs=(struct symtab**)
504			    malloc(deflen*sizeof(struct symtab*));
505                	if (i_defs==NULL) return 0;
506        	}
507
508        	while ((last1 >= first1) && (last2 >= first2))
509        	{
510	    		char *s1=file1->i_defs[first1]->s_name;
511	    		char *s2=file2->i_defs[first2]->s_name;
512
513     			if (strcmp(s1,s2) < 0)
514                        	i_defs[first++]=file1->i_defs[first1++];
515     			else if (strcmp(s1,s2) > 0)
516                        	i_defs[first++]=file2->i_defs[first2++];
517                        else /* equal */
518                        {
519                        	i_defs[first++]=file2->i_defs[first2++];
520                                first1++;
521                        }
522        	}
523        	while (last1 >= first1)
524        	{
525                        i_defs[first++]=file1->i_defs[first1++];
526        	}
527        	while (last2 >= first2)
528        	{
529                        i_defs[first++]=file2->i_defs[first2++];
530        	}
531
532                if (file1->i_defs) free(file1->i_defs);
533                file1->i_defs=i_defs;
534                file1->i_ndefs=first;
535
536		return 1;
537  	}
538}
539
540void
541undefine(char *symbol, struct inclist *file)
542{
543	register struct symtab **ptr;
544	struct inclist *srcfile;
545	while ((ptr = isdefined(symbol, file, &srcfile)) != NULL)
546	{
547	    srcfile->i_ndefs--;
548	    for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++)
549		*ptr = ptr[1];
550	}
551}
552
553int
554find_includes(struct filepointer *filep, struct inclist *file,
555	      struct inclist *file_red, int recursion, boolean failOK)
556{
557	struct inclist	*inclistp;
558	char		**includedirsp;
559	register char	*line;
560	register int	type;
561	boolean recfailOK;
562
563	while ((line = getnextline(filep))) {
564		switch(type = deftype(line, filep, file_red, file, TRUE)) {
565		case IF:
566		doif:
567			type = find_includes(filep, file,
568				file_red, recursion+1, failOK);
569			while ((type == ELIF) || (type == ELIFFALSE) ||
570			       (type == ELIFGUESSFALSE))
571				type = gobble(filep, file, file_red);
572			if (type == ELSE)
573				gobble(filep, file, file_red);
574			break;
575		case IFFALSE:
576		case IFGUESSFALSE:
577		    doiffalse:
578			if (type == IFGUESSFALSE || type == ELIFGUESSFALSE)
579			    recfailOK = TRUE;
580			else
581			    recfailOK = failOK;
582			type = gobble(filep, file, file_red);
583			if (type == ELSE)
584			    find_includes(filep, file,
585					  file_red, recursion+1, recfailOK);
586			else
587			if (type == ELIF)
588			    goto doif;
589			else
590			if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE))
591			    goto doiffalse;
592			break;
593		case IFDEF:
594		case IFNDEF:
595			if ((type == IFDEF && isdefined(line, file_red, NULL))
596			 || (type == IFNDEF && !isdefined(line, file_red, NULL))) {
597				debug(1,(type == IFNDEF ?
598				    "line %d: %s !def'd in %s via %s%s\n" : "",
599				    filep->f_line, line,
600				    file->i_file, file_red->i_file, ": doit"));
601				type = find_includes(filep, file,
602					file_red, recursion+1, failOK);
603				while (type == ELIF || type == ELIFFALSE || type == ELIFGUESSFALSE)
604					type = gobble(filep, file, file_red);
605				if (type == ELSE)
606					gobble(filep, file, file_red);
607			}
608			else {
609				debug(1,(type == IFDEF ?
610				    "line %d: %s !def'd in %s via %s%s\n" : "",
611				    filep->f_line, line,
612				    file->i_file, file_red->i_file, ": gobble"));
613				type = gobble(filep, file, file_red);
614				if (type == ELSE)
615					find_includes(filep, file,
616						file_red, recursion+1, failOK);
617				else if (type == ELIF)
618				    	goto doif;
619				else if (type == ELIFFALSE || type == ELIFGUESSFALSE)
620				    	goto doiffalse;
621			}
622			break;
623		case ELSE:
624		case ELIFFALSE:
625		case ELIFGUESSFALSE:
626		case ELIF:
627			if (!recursion)
628				gobble(filep, file, file_red);
629		case ENDIF:
630			if (recursion)
631				return(type);
632		case DEFINE:
633			define(line, file);
634			break;
635		case UNDEF:
636			if (!*line) {
637			    warning("%s", file_red->i_file);
638			    if (file_red != file)
639				warning1(" (reading %s)", file->i_file);
640			    warning1(", line %d: incomplete undef == \"%s\"\n",
641				filep->f_line, line);
642			    break;
643			}
644			undefine(line, file_red);
645			break;
646		case INCLUDE:
647		case INCLUDEDOT:
648		case INCLUDENEXT:
649		case INCLUDENEXTDOT:
650			inclistp = inclistnext;
651			includedirsp = includedirsnext;
652			debug(2,("%s, reading %s, includes %s\n",
653				file_red->i_file, file->i_file, line));
654			add_include(filep, file, file_red, line, type, failOK);
655			inclistnext = inclistp;
656			includedirsnext = includedirsp;
657			break;
658		case ERROR:
659		case WARNING:
660		    	warning("%s", file_red->i_file);
661			if (file_red != file)
662				warning1(" (reading %s)", file->i_file);
663			warning1(", line %d: %s\n",
664				 filep->f_line, line);
665		    	break;
666
667		case PRAGMA:
668		case IDENT:
669		case SCCS:
670		case EJECT:
671			break;
672		case -1:
673			warning("%s", file_red->i_file);
674			if (file_red != file)
675			    warning1(" (reading %s)", file->i_file);
676			warning1(", line %d: unknown directive == \"%s\"\n",
677				 filep->f_line, line);
678			break;
679		case -2:
680			warning("%s", file_red->i_file);
681			if (file_red != file)
682			    warning1(" (reading %s)", file->i_file);
683			warning1(", line %d: incomplete include == \"%s\"\n",
684				 filep->f_line, line);
685			break;
686		}
687	}
688	file->i_flags |= FINISHED;
689	debug(2,("finished with %s\n", file->i_file));
690	return(-1);
691}
692