michael@0: /* $Xorg: parse.c,v 1.6 2001/02/09 02:03:16 xorgcvs Exp $ */ michael@0: /* michael@0: michael@0: Copyright (c) 1993, 1994, 1998 The Open Group michael@0: michael@0: Permission to use, copy, modify, distribute, and sell this software and its michael@0: documentation for any purpose is hereby granted without fee, provided that michael@0: the above copyright notice appear in all copies and that both that michael@0: copyright notice and this permission notice appear in supporting michael@0: documentation. michael@0: michael@0: The above copyright notice and this permission notice shall be included in michael@0: all copies or substantial portions of the Software. michael@0: michael@0: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR michael@0: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, michael@0: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE michael@0: OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN michael@0: AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN michael@0: CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. michael@0: michael@0: Except as contained in this notice, the name of The Open Group shall not be michael@0: used in advertising or otherwise to promote the sale, use or other dealings michael@0: in this Software without prior written authorization from The Open Group. michael@0: michael@0: */ michael@0: /* $XFree86: xc/config/makedepend/parse.c,v 1.12 2002/02/26 05:09:10 tsi Exp $ */ michael@0: michael@0: #include "def.h" michael@0: michael@0: extern char *directives[]; michael@0: extern struct inclist inclist[ MAXFILES ], michael@0: *inclistnext, michael@0: maininclist; michael@0: extern char *includedirs[ ], michael@0: **includedirsnext; michael@0: michael@0: static int deftype (char *line, struct filepointer *filep, michael@0: struct inclist *file_red, struct inclist *file, michael@0: int parse_it); michael@0: static int zero_value(char *filename, char *exp, struct filepointer *filep, michael@0: struct inclist *file_red); michael@0: static int merge2defines(struct inclist *file1, struct inclist *file2); michael@0: michael@0: static int michael@0: gobble(struct filepointer *filep, struct inclist *file, michael@0: struct inclist *file_red) michael@0: { michael@0: char *line; michael@0: int type; michael@0: michael@0: while ((line = getnextline(filep))) { michael@0: switch(type = deftype(line, filep, file_red, file, FALSE)) { michael@0: case IF: michael@0: case IFFALSE: michael@0: case IFGUESSFALSE: michael@0: case IFDEF: michael@0: case IFNDEF: michael@0: type = gobble(filep, file, file_red); michael@0: while ((type == ELIF) || (type == ELIFFALSE) || michael@0: (type == ELIFGUESSFALSE)) michael@0: type = gobble(filep, file, file_red); michael@0: if (type == ELSE) michael@0: (void)gobble(filep, file, file_red); michael@0: break; michael@0: case ELSE: michael@0: case ENDIF: michael@0: debug(0,("%s, line %d: #%s\n", michael@0: file->i_file, filep->f_line, michael@0: directives[type])); michael@0: return(type); michael@0: case DEFINE: michael@0: case UNDEF: michael@0: case INCLUDE: michael@0: case INCLUDEDOT: michael@0: case PRAGMA: michael@0: case ERROR: michael@0: case IDENT: michael@0: case SCCS: michael@0: case EJECT: michael@0: case WARNING: michael@0: case INCLUDENEXT: michael@0: case INCLUDENEXTDOT: michael@0: break; michael@0: case ELIF: michael@0: case ELIFFALSE: michael@0: case ELIFGUESSFALSE: michael@0: return(type); michael@0: case -1: michael@0: warning("%s", file_red->i_file); michael@0: if (file_red != file) michael@0: warning1(" (reading %s)", file->i_file); michael@0: warning1(", line %d: unknown directive == \"%s\"\n", michael@0: filep->f_line, line); michael@0: break; michael@0: } michael@0: } michael@0: return(-1); michael@0: } michael@0: michael@0: /* michael@0: * Decide what type of # directive this line is. michael@0: */ michael@0: static int michael@0: deftype (char *line, struct filepointer *filep, michael@0: struct inclist *file_red, struct inclist *file, int parse_it) michael@0: { michael@0: register char *p; michael@0: char *directive, savechar, *q; michael@0: register int ret; michael@0: michael@0: /* michael@0: * Parse the directive... michael@0: */ michael@0: directive=line+1; michael@0: while (*directive == ' ' || *directive == '\t') michael@0: directive++; michael@0: michael@0: p = directive; michael@0: while ((*p == '_') || (*p >= 'a' && *p <= 'z')) michael@0: p++; michael@0: savechar = *p; michael@0: *p = '\0'; michael@0: ret = match(directive, directives); michael@0: *p = savechar; michael@0: michael@0: /* If we don't recognize this compiler directive or we happen to just michael@0: * be gobbling up text while waiting for an #endif or #elif or #else michael@0: * in the case of an #elif we must check the zero_value and return an michael@0: * ELIF or an ELIFFALSE. michael@0: */ michael@0: michael@0: if (ret == ELIF && !parse_it) michael@0: { michael@0: while (*p == ' ' || *p == '\t') michael@0: p++; michael@0: /* michael@0: * parse an expression. michael@0: */ michael@0: debug(0,("%s, line %d: #elif %s ", michael@0: file->i_file, filep->f_line, p)); michael@0: ret = zero_value(file->i_file, p, filep, file_red); michael@0: if (ret != IF) michael@0: { michael@0: debug(0,("false...\n")); michael@0: if (ret == IFFALSE) michael@0: return(ELIFFALSE); michael@0: else michael@0: return(ELIFGUESSFALSE); michael@0: } michael@0: else michael@0: { michael@0: debug(0,("true...\n")); michael@0: return(ELIF); michael@0: } michael@0: } michael@0: michael@0: if (ret < 0 || ! parse_it) michael@0: return(ret); michael@0: michael@0: /* michael@0: * now decide how to parse the directive, and do it. michael@0: */ michael@0: while (*p == ' ' || *p == '\t') michael@0: p++; michael@0: q = p + strlen(p); michael@0: do { michael@0: q--; michael@0: } while (*q == ' ' || *q == '\t'); michael@0: q[1] = '\0'; michael@0: switch (ret) { michael@0: case IF: michael@0: /* michael@0: * parse an expression. michael@0: */ michael@0: ret = zero_value(file->i_file, p, filep, file_red); michael@0: debug(0,("%s, line %d: %s #if %s\n", michael@0: file->i_file, filep->f_line, ret?"false":"true", p)); michael@0: break; michael@0: case IFDEF: michael@0: case IFNDEF: michael@0: debug(0,("%s, line %d: #%s %s\n", michael@0: file->i_file, filep->f_line, directives[ret], p)); michael@0: case UNDEF: michael@0: /* michael@0: * separate the name of a single symbol. michael@0: */ michael@0: while (isalnum(*p) || *p == '_') michael@0: *line++ = *p++; michael@0: *line = '\0'; michael@0: break; michael@0: case INCLUDE: michael@0: case INCLUDENEXT: michael@0: debug(2,("%s, line %d: #include%s %s\n", michael@0: file->i_file, filep->f_line, michael@0: (ret == INCLUDE) ? "" : "_next", p)); michael@0: michael@0: /* Support ANSI macro substitution */ michael@0: while (1) { michael@0: struct symtab **sym; michael@0: michael@0: if (!*p || *p == '"' || *p == '<') michael@0: break; michael@0: michael@0: sym = isdefined(p, file_red, NULL); michael@0: if (!sym) michael@0: break; michael@0: michael@0: p = (*sym)->s_value; michael@0: debug(3,("%s : #includes SYMBOL %s = %s\n", michael@0: file->i_incstring, michael@0: (*sym) -> s_name, michael@0: (*sym) -> s_value)); michael@0: /* mark file as having included a 'soft include' */ michael@0: file->i_flags |= INCLUDED_SYM; michael@0: } michael@0: michael@0: /* michael@0: * Separate the name of the include file. michael@0: */ michael@0: while (*p && *p != '"' && *p != '<') michael@0: p++; michael@0: if (! *p) michael@0: return(-2); michael@0: if (*p++ == '"') { michael@0: if (ret == INCLUDE) michael@0: ret = INCLUDEDOT; michael@0: else michael@0: ret = INCLUDENEXTDOT; michael@0: while (*p && *p != '"') michael@0: *line++ = *p++; michael@0: } else michael@0: while (*p && *p != '>') michael@0: *line++ = *p++; michael@0: *line = '\0'; michael@0: break; michael@0: case DEFINE: michael@0: /* michael@0: * copy the definition back to the beginning of the line. michael@0: */ michael@0: strcpy (line, p); michael@0: break; michael@0: case ELSE: michael@0: case ENDIF: michael@0: case ELIF: michael@0: case PRAGMA: michael@0: case ERROR: michael@0: case IDENT: michael@0: case SCCS: michael@0: case EJECT: michael@0: case WARNING: michael@0: debug(0,("%s, line %d: #%s\n", michael@0: file->i_file, filep->f_line, directives[ret])); michael@0: /* michael@0: * nothing to do. michael@0: */ michael@0: break; michael@0: } michael@0: return(ret); michael@0: } michael@0: michael@0: struct symtab ** michael@0: fdefined(char *symbol, struct inclist *file, struct inclist **srcfile) michael@0: { michael@0: struct inclist **ip; michael@0: struct symtab **val; michael@0: int i; michael@0: static int recurse_lvl = 0; michael@0: michael@0: if (file->i_flags & DEFCHECKED) michael@0: return(NULL); michael@0: debug(2,("Looking for %s in %s\n", symbol, file->i_file)); michael@0: file->i_flags |= DEFCHECKED; michael@0: if ((val = slookup(symbol, file))) michael@0: debug(1,("%s defined in %s as %s\n", michael@0: symbol, file->i_file, (*val)->s_value)); michael@0: if (val == NULL && file->i_list) michael@0: { michael@0: for (ip = file->i_list, i=0; i < file->i_listlen; i++, ip++) michael@0: if (file->i_merged[i]==FALSE) { michael@0: val = fdefined(symbol, *ip, srcfile); michael@0: file->i_merged[i]=merge2defines(file,*ip); michael@0: if (val!=NULL) break; michael@0: } michael@0: } michael@0: else if (val != NULL && srcfile != NULL) *srcfile = file; michael@0: recurse_lvl--; michael@0: file->i_flags &= ~DEFCHECKED; michael@0: michael@0: return(val); michael@0: } michael@0: michael@0: struct symtab ** michael@0: isdefined(char *symbol, struct inclist *file, struct inclist **srcfile) michael@0: { michael@0: struct symtab **val; michael@0: michael@0: if ((val = slookup(symbol, &maininclist))) { michael@0: debug(1,("%s defined on command line\n", symbol)); michael@0: if (srcfile != NULL) *srcfile = &maininclist; michael@0: return(val); michael@0: } michael@0: if ((val = fdefined(symbol, file, srcfile))) michael@0: return(val); michael@0: debug(1,("%s not defined in %s\n", symbol, file->i_file)); michael@0: return(NULL); michael@0: } michael@0: michael@0: /* michael@0: * Return type based on if the #if expression evaluates to 0 michael@0: */ michael@0: static int michael@0: zero_value(char *filename, michael@0: char *exp, michael@0: struct filepointer *filep, michael@0: struct inclist *file_red) michael@0: { michael@0: if (cppsetup(filename, exp, filep, file_red)) michael@0: return(IFFALSE); michael@0: else michael@0: return(IF); michael@0: } michael@0: michael@0: void michael@0: define2(char *name, char *val, struct inclist *file) michael@0: { michael@0: int first, last, below; michael@0: register struct symtab **sp = NULL, **dest; michael@0: struct symtab *stab; michael@0: michael@0: /* Make space if it's needed */ michael@0: if (file->i_defs == NULL) michael@0: { michael@0: file->i_defs = (struct symtab **) michael@0: malloc(sizeof (struct symtab*) * SYMTABINC); michael@0: file->i_ndefs = 0; michael@0: } michael@0: else if (!(file->i_ndefs % SYMTABINC)) michael@0: file->i_defs = (struct symtab **) michael@0: realloc(file->i_defs, michael@0: sizeof(struct symtab*)*(file->i_ndefs+SYMTABINC)); michael@0: michael@0: if (file->i_defs == NULL) michael@0: fatalerr("malloc()/realloc() failure in insert_defn()\n"); michael@0: michael@0: below = first = 0; michael@0: last = file->i_ndefs - 1; michael@0: while (last >= first) michael@0: { michael@0: /* Fast inline binary search */ michael@0: register char *s1; michael@0: register char *s2; michael@0: register int middle = (first + last) / 2; michael@0: michael@0: /* Fast inline strchr() */ michael@0: s1 = name; michael@0: s2 = file->i_defs[middle]->s_name; michael@0: while (*s1++ == *s2++) michael@0: if (s2[-1] == '\0') break; michael@0: michael@0: /* If exact match, set sp and break */ michael@0: if (*--s1 == *--s2) michael@0: { michael@0: sp = file->i_defs + middle; michael@0: break; michael@0: } michael@0: michael@0: /* If name > i_defs[middle] ... */ michael@0: if (*s1 > *s2) michael@0: { michael@0: below = first; michael@0: first = middle + 1; michael@0: } michael@0: /* else ... */ michael@0: else michael@0: { michael@0: below = last = middle - 1; michael@0: } michael@0: } michael@0: michael@0: /* Search is done. If we found an exact match to the symbol name, michael@0: just replace its s_value */ michael@0: if (sp != NULL) michael@0: { michael@0: debug(1,("redefining %s from %s to %s in file %s\n", michael@0: name, (*sp)->s_value, val, file->i_file)); michael@0: free((*sp)->s_value); michael@0: (*sp)->s_value = copy(val); michael@0: return; michael@0: } michael@0: michael@0: sp = file->i_defs + file->i_ndefs++; michael@0: dest = file->i_defs + below + 1; michael@0: while (sp > dest) michael@0: { michael@0: *sp = sp[-1]; michael@0: sp--; michael@0: } michael@0: stab = (struct symtab *) malloc(sizeof (struct symtab)); michael@0: if (stab == NULL) michael@0: fatalerr("malloc()/realloc() failure in insert_defn()\n"); michael@0: michael@0: debug(1,("defining %s to %s in file %s\n", name, val, file->i_file)); michael@0: stab->s_name = copy(name); michael@0: stab->s_value = copy(val); michael@0: *sp = stab; michael@0: } michael@0: michael@0: void michael@0: define(char *def, struct inclist *file) michael@0: { michael@0: char *val; michael@0: michael@0: /* Separate symbol name and its value */ michael@0: val = def; michael@0: while (isalnum(*val) || *val == '_') michael@0: val++; michael@0: if (*val) michael@0: *val++ = '\0'; michael@0: while (*val == ' ' || *val == '\t') michael@0: val++; michael@0: michael@0: if (!*val) michael@0: val = "1"; michael@0: define2(def, val, file); michael@0: } michael@0: michael@0: struct symtab ** michael@0: slookup(char *symbol, struct inclist *file) michael@0: { michael@0: register int first = 0; michael@0: register int last = file->i_ndefs - 1; michael@0: michael@0: if (file) while (last >= first) michael@0: { michael@0: /* Fast inline binary search */ michael@0: register char *s1; michael@0: register char *s2; michael@0: register int middle = (first + last) / 2; michael@0: michael@0: /* Fast inline strchr() */ michael@0: s1 = symbol; michael@0: s2 = file->i_defs[middle]->s_name; michael@0: while (*s1++ == *s2++) michael@0: if (s2[-1] == '\0') break; michael@0: michael@0: /* If exact match, we're done */ michael@0: if (*--s1 == *--s2) michael@0: { michael@0: return file->i_defs + middle; michael@0: } michael@0: michael@0: /* If symbol > i_defs[middle] ... */ michael@0: if (*s1 > *s2) michael@0: { michael@0: first = middle + 1; michael@0: } michael@0: /* else ... */ michael@0: else michael@0: { michael@0: last = middle - 1; michael@0: } michael@0: } michael@0: return(NULL); michael@0: } michael@0: michael@0: static int michael@0: merge2defines(struct inclist *file1, struct inclist *file2) michael@0: { michael@0: int i; michael@0: michael@0: if ((file1==NULL) || (file2==NULL) || michael@0: !(file2->i_flags & FINISHED)) michael@0: return 0; michael@0: michael@0: for (i=0; i < file2->i_listlen; i++) michael@0: if (file2->i_merged[i]==FALSE) michael@0: return 0; michael@0: michael@0: { michael@0: int first1 = 0; michael@0: int last1 = file1->i_ndefs - 1; michael@0: michael@0: int first2 = 0; michael@0: int last2 = file2->i_ndefs - 1; michael@0: michael@0: int first=0; michael@0: struct symtab** i_defs = NULL; michael@0: int deflen=file1->i_ndefs+file2->i_ndefs; michael@0: michael@0: debug(2,("merging %s into %s\n", michael@0: file2->i_file, file1->i_file)); michael@0: michael@0: if (deflen>0) michael@0: { michael@0: /* make sure deflen % SYMTABINC == 0 is still true */ michael@0: deflen += (SYMTABINC - deflen % SYMTABINC) % SYMTABINC; michael@0: i_defs=(struct symtab**) michael@0: malloc(deflen*sizeof(struct symtab*)); michael@0: if (i_defs==NULL) return 0; michael@0: } michael@0: michael@0: while ((last1 >= first1) && (last2 >= first2)) michael@0: { michael@0: char *s1=file1->i_defs[first1]->s_name; michael@0: char *s2=file2->i_defs[first2]->s_name; michael@0: michael@0: if (strcmp(s1,s2) < 0) michael@0: i_defs[first++]=file1->i_defs[first1++]; michael@0: else if (strcmp(s1,s2) > 0) michael@0: i_defs[first++]=file2->i_defs[first2++]; michael@0: else /* equal */ michael@0: { michael@0: i_defs[first++]=file2->i_defs[first2++]; michael@0: first1++; michael@0: } michael@0: } michael@0: while (last1 >= first1) michael@0: { michael@0: i_defs[first++]=file1->i_defs[first1++]; michael@0: } michael@0: while (last2 >= first2) michael@0: { michael@0: i_defs[first++]=file2->i_defs[first2++]; michael@0: } michael@0: michael@0: if (file1->i_defs) free(file1->i_defs); michael@0: file1->i_defs=i_defs; michael@0: file1->i_ndefs=first; michael@0: michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: undefine(char *symbol, struct inclist *file) michael@0: { michael@0: register struct symtab **ptr; michael@0: struct inclist *srcfile; michael@0: while ((ptr = isdefined(symbol, file, &srcfile)) != NULL) michael@0: { michael@0: srcfile->i_ndefs--; michael@0: for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++) michael@0: *ptr = ptr[1]; michael@0: } michael@0: } michael@0: michael@0: int michael@0: find_includes(struct filepointer *filep, struct inclist *file, michael@0: struct inclist *file_red, int recursion, boolean failOK) michael@0: { michael@0: struct inclist *inclistp; michael@0: char **includedirsp; michael@0: register char *line; michael@0: register int type; michael@0: boolean recfailOK; michael@0: michael@0: while ((line = getnextline(filep))) { michael@0: switch(type = deftype(line, filep, file_red, file, TRUE)) { michael@0: case IF: michael@0: doif: michael@0: type = find_includes(filep, file, michael@0: file_red, recursion+1, failOK); michael@0: while ((type == ELIF) || (type == ELIFFALSE) || michael@0: (type == ELIFGUESSFALSE)) michael@0: type = gobble(filep, file, file_red); michael@0: if (type == ELSE) michael@0: gobble(filep, file, file_red); michael@0: break; michael@0: case IFFALSE: michael@0: case IFGUESSFALSE: michael@0: doiffalse: michael@0: if (type == IFGUESSFALSE || type == ELIFGUESSFALSE) michael@0: recfailOK = TRUE; michael@0: else michael@0: recfailOK = failOK; michael@0: type = gobble(filep, file, file_red); michael@0: if (type == ELSE) michael@0: find_includes(filep, file, michael@0: file_red, recursion+1, recfailOK); michael@0: else michael@0: if (type == ELIF) michael@0: goto doif; michael@0: else michael@0: if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE)) michael@0: goto doiffalse; michael@0: break; michael@0: case IFDEF: michael@0: case IFNDEF: michael@0: if ((type == IFDEF && isdefined(line, file_red, NULL)) michael@0: || (type == IFNDEF && !isdefined(line, file_red, NULL))) { michael@0: debug(1,(type == IFNDEF ? michael@0: "line %d: %s !def'd in %s via %s%s\n" : "", michael@0: filep->f_line, line, michael@0: file->i_file, file_red->i_file, ": doit")); michael@0: type = find_includes(filep, file, michael@0: file_red, recursion+1, failOK); michael@0: while (type == ELIF || type == ELIFFALSE || type == ELIFGUESSFALSE) michael@0: type = gobble(filep, file, file_red); michael@0: if (type == ELSE) michael@0: gobble(filep, file, file_red); michael@0: } michael@0: else { michael@0: debug(1,(type == IFDEF ? michael@0: "line %d: %s !def'd in %s via %s%s\n" : "", michael@0: filep->f_line, line, michael@0: file->i_file, file_red->i_file, ": gobble")); michael@0: type = gobble(filep, file, file_red); michael@0: if (type == ELSE) michael@0: find_includes(filep, file, michael@0: file_red, recursion+1, failOK); michael@0: else if (type == ELIF) michael@0: goto doif; michael@0: else if (type == ELIFFALSE || type == ELIFGUESSFALSE) michael@0: goto doiffalse; michael@0: } michael@0: break; michael@0: case ELSE: michael@0: case ELIFFALSE: michael@0: case ELIFGUESSFALSE: michael@0: case ELIF: michael@0: if (!recursion) michael@0: gobble(filep, file, file_red); michael@0: case ENDIF: michael@0: if (recursion) michael@0: return(type); michael@0: case DEFINE: michael@0: define(line, file); michael@0: break; michael@0: case UNDEF: michael@0: if (!*line) { michael@0: warning("%s", file_red->i_file); michael@0: if (file_red != file) michael@0: warning1(" (reading %s)", file->i_file); michael@0: warning1(", line %d: incomplete undef == \"%s\"\n", michael@0: filep->f_line, line); michael@0: break; michael@0: } michael@0: undefine(line, file_red); michael@0: break; michael@0: case INCLUDE: michael@0: case INCLUDEDOT: michael@0: case INCLUDENEXT: michael@0: case INCLUDENEXTDOT: michael@0: inclistp = inclistnext; michael@0: includedirsp = includedirsnext; michael@0: debug(2,("%s, reading %s, includes %s\n", michael@0: file_red->i_file, file->i_file, line)); michael@0: add_include(filep, file, file_red, line, type, failOK); michael@0: inclistnext = inclistp; michael@0: includedirsnext = includedirsp; michael@0: break; michael@0: case ERROR: michael@0: case WARNING: michael@0: warning("%s", file_red->i_file); michael@0: if (file_red != file) michael@0: warning1(" (reading %s)", file->i_file); michael@0: warning1(", line %d: %s\n", michael@0: filep->f_line, line); michael@0: break; michael@0: michael@0: case PRAGMA: michael@0: case IDENT: michael@0: case SCCS: michael@0: case EJECT: michael@0: break; michael@0: case -1: michael@0: warning("%s", file_red->i_file); michael@0: if (file_red != file) michael@0: warning1(" (reading %s)", file->i_file); michael@0: warning1(", line %d: unknown directive == \"%s\"\n", michael@0: filep->f_line, line); michael@0: break; michael@0: case -2: michael@0: warning("%s", file_red->i_file); michael@0: if (file_red != file) michael@0: warning1(" (reading %s)", file->i_file); michael@0: warning1(", line %d: incomplete include == \"%s\"\n", michael@0: filep->f_line, line); michael@0: break; michael@0: } michael@0: } michael@0: file->i_flags |= FINISHED; michael@0: debug(2,("finished with %s\n", file->i_file)); michael@0: return(-1); michael@0: }