| /* $XConsortium: parse.c,v 1.30 94/04/17 20:10:38 gildea Exp $ */ | 
 | /* | 
 |  | 
 | Copyright (c) 1993, 1994  X Consortium | 
 |  | 
 | Permission is hereby granted, free of charge, to any person obtaining a copy | 
 | of this software and associated documentation files (the "Software"), to deal | 
 | in the Software without restriction, including without limitation the rights | 
 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 | copies of the Software, and to permit persons to whom the Software is | 
 | furnished to do so, subject to the following conditions: | 
 |  | 
 | The above copyright notice and this permission notice shall be included in | 
 | all copies or substantial portions of the Software. | 
 |  | 
 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
 | X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | 
 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
 |  | 
 | Except as contained in this notice, the name of the X Consortium shall not be | 
 | used in advertising or otherwise to promote the sale, use or other dealings | 
 | in this Software without prior written authorization from the X Consortium. | 
 |  | 
 | */ | 
 |  | 
 | #include "def.h" | 
 | char *hash_lookup( char *symbol, struct symhash *symbols ); | 
 | void hash_undefine( char *symbol, struct symhash *symbols ); | 
 | int gobble( register struct filepointer *filep, struct inclist *file, | 
 |     struct inclist *file_red, struct symhash *symbols ); | 
 | int deftype ( register char *line, register struct filepointer *filep, | 
 |     register struct inclist *file_red, register struct inclist *file, | 
 |     int parse_it, struct symhash *symbols); | 
 | int zero_value(register char *exp, register struct filepointer *filep, | 
 |     register struct inclist *file_red, register struct symhash *symbols); | 
 |  | 
 | extern char	*directives[]; | 
 | extern struct symhash *maininclist; | 
 |  | 
 | int find_includes(filep, file, file_red, recursion, failOK, incCollection, symbols) | 
 | 	struct filepointer	*filep; | 
 | 	struct inclist		*file, *file_red; | 
 | 	int			recursion; | 
 | 	boolean			failOK; | 
 |     struct IncludesCollection* incCollection;  | 
 | 	struct symhash		*symbols; | 
 | { | 
 | 	register char	*line; | 
 | 	register int	type; | 
 | 	boolean recfailOK; | 
 |  | 
 | 	while ((line = get_line(filep))) { | 
 | 		switch(type = deftype(line, filep, file_red, file, TRUE, symbols)) { | 
 | 		case IF: | 
 | 		doif: | 
 | 			type = find_includes(filep, file, | 
 | 				file_red, recursion+1, failOK, incCollection, symbols); | 
 | 			while ((type == ELIF) || (type == ELIFFALSE) || | 
 | 			       (type == ELIFGUESSFALSE)) | 
 | 				type = gobble(filep, file, file_red, symbols); | 
 | 			if (type == ELSE) | 
 | 				gobble(filep, file, file_red, symbols); | 
 | 			break; | 
 | 		case IFFALSE: | 
 | 		case IFGUESSFALSE: | 
 | 		    doiffalse: | 
 | 			if (type == IFGUESSFALSE || type == ELIFGUESSFALSE) | 
 | 			    recfailOK = TRUE; | 
 | 			else | 
 | 			    recfailOK = failOK; | 
 | 			type = gobble(filep, file, file_red, symbols); | 
 | 			if (type == ELSE) | 
 | 			    find_includes(filep, file, | 
 | 					  file_red, recursion+1, recfailOK, incCollection, symbols); | 
 | 			else | 
 | 			if (type == ELIF) | 
 | 			    goto doif; | 
 | 			else | 
 | 			if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE)) | 
 | 			    goto doiffalse; | 
 | 			break; | 
 | 		case IFDEF: | 
 | 		case IFNDEF: | 
 | 			if ((type == IFDEF && hash_lookup(line, symbols)) | 
 | 			 || (type == IFNDEF && !hash_lookup(line, symbols))) { | 
 | 				debug(1,(type == IFNDEF ? | 
 | 				    "line %d: %s !def'd in %s via %s%s\n" : "", | 
 | 				    filep->f_line, line, | 
 | 				    file->i_file, file_red->i_file, ": doit")); | 
 | 				type = find_includes(filep, file, | 
 | 					file_red, recursion+1, failOK, incCollection, symbols); | 
 | 				while (type == ELIF || type == ELIFFALSE || type == ELIFGUESSFALSE) | 
 | 					type = gobble(filep, file, file_red, symbols); | 
 | 				if (type == ELSE) | 
 | 					gobble(filep, file, file_red, symbols); | 
 | 			} | 
 | 			else { | 
 | 				debug(1,(type == IFDEF ? | 
 | 				    "line %d: %s !def'd in %s via %s%s\n" : "", | 
 | 				    filep->f_line, line, | 
 | 				    file->i_file, file_red->i_file, ": gobble")); | 
 | 				type = gobble(filep, file, file_red, symbols); | 
 | 				if (type == ELSE) | 
 | 					find_includes(filep, file, | 
 | 						file_red, recursion + 1, failOK, incCollection, symbols); | 
 | 				else if (type == ELIF) | 
 | 				    	goto doif; | 
 | 				else if (type == ELIFFALSE || type == ELIFGUESSFALSE) | 
 | 				    	goto doiffalse; | 
 | 			} | 
 | 			break; | 
 | 		case ELSE: | 
 | 		case ELIFFALSE: | 
 | 		case ELIFGUESSFALSE: | 
 | 		case ELIF: | 
 | 			if (!recursion) | 
 | 				gobble(filep, file, file_red, symbols); | 
 | 		case ENDIF: | 
 | 			if (recursion) | 
 | 				return(type); | 
 | 		case DEFINE: | 
 | 			define(line, &symbols); | 
 | 			break; | 
 | 		case UNDEF: | 
 | 			if (!*line) { | 
 | 			    warning("%s, line %d: incomplete undef == \"%s\"\n", | 
 | 				file_red->i_file, filep->f_line, line); | 
 | 			    break; | 
 | 			} | 
 | 			hash_undefine(line, symbols); | 
 | 			break; | 
 | 		case INCLUDE: | 
 | 			add_include(filep, file, file_red, line, FALSE, failOK, incCollection, symbols); | 
 | 			break; | 
 | 		case INCLUDEDOT: | 
 | 			add_include(filep, file, file_red, line, TRUE, failOK, incCollection, symbols); | 
 | 			break; | 
 | 		case ERROR: | 
 | 		    	warning("%s: %d: %s\n", file_red->i_file, | 
 | 				 filep->f_line, line); | 
 | 		    	break; | 
 | 		     | 
 | 		case PRAGMA: | 
 | 		case IDENT: | 
 | 		case SCCS: | 
 | 		case EJECT: | 
 | 			break; | 
 | 		case -1: | 
 | 			warning("%s", file_red->i_file); | 
 | 			if (file_red != file) | 
 | 			    warning1(" (reading %s)", file->i_file); | 
 | 			warning1(", line %d: unknown directive == \"%s\"\n", | 
 | 				 filep->f_line, line); | 
 | 			break; | 
 | 		case -2: | 
 | 			warning("%s", file_red->i_file); | 
 | 			if (file_red != file) | 
 | 			    warning1(" (reading %s)", file->i_file); | 
 | 			warning1(", line %d: incomplete include == \"%s\"\n", | 
 | 				 filep->f_line, line); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	return(-1); | 
 | } | 
 |  | 
 | int gobble(filep, file, file_red, symbols) | 
 | 	register struct filepointer *filep; | 
 | 	struct inclist		*file, *file_red; | 
 | 	struct symhash		*symbols; | 
 | { | 
 | 	register char	*line; | 
 | 	register int	type; | 
 |  | 
 | 	while ((line = get_line(filep))) { | 
 | 		switch(type = deftype(line, filep, file_red, file, FALSE, symbols)) { | 
 | 		case IF: | 
 | 		case IFFALSE: | 
 | 		case IFGUESSFALSE: | 
 | 		case IFDEF: | 
 | 		case IFNDEF: | 
 | 			type = gobble(filep, file, file_red, symbols); | 
 | 			while ((type == ELIF) || (type == ELIFFALSE) || | 
 | 			       (type == ELIFGUESSFALSE)) | 
 | 			    type = gobble(filep, file, file_red, symbols); | 
 | 			if (type == ELSE) | 
 | 			        (void)gobble(filep, file, file_red, symbols); | 
 | 			break; | 
 | 		case ELSE: | 
 | 		case ENDIF: | 
 | 			debug(0,("%s, line %d: #%s\n", | 
 | 				file->i_file, filep->f_line, | 
 | 				directives[type])); | 
 | 			return(type); | 
 | 		case DEFINE: | 
 | 		case UNDEF: | 
 | 		case INCLUDE: | 
 | 		case INCLUDEDOT: | 
 | 		case PRAGMA: | 
 | 		case ERROR: | 
 | 		case IDENT: | 
 | 		case SCCS: | 
 | 		case EJECT: | 
 | 			break; | 
 | 		case ELIF: | 
 | 		case ELIFFALSE: | 
 | 		case ELIFGUESSFALSE: | 
 | 			return(type); | 
 | 		case -1: | 
 | 			warning("%s, line %d: unknown directive == \"%s\"\n", | 
 | 				file_red->i_file, filep->f_line, line); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	return(-1); | 
 | } | 
 |  | 
 | /* | 
 |  * Decide what type of # directive this line is. | 
 |  */ | 
 | int deftype (line, filep, file_red, file, parse_it, symbols) | 
 | 	register char	*line; | 
 | 	register struct filepointer *filep; | 
 | 	register struct inclist *file_red, *file; | 
 | 	int	parse_it; | 
 | 	struct symhash	*symbols; | 
 | { | 
 | 	register char	*p; | 
 | 	char	*directive, savechar; | 
 | 	register int	ret; | 
 |  | 
 | 	/* | 
 | 	 * Parse the directive... | 
 | 	 */ | 
 | 	directive=line+1; | 
 | 	while (*directive == ' ' || *directive == '\t') | 
 | 		directive++; | 
 |  | 
 | 	p = directive; | 
 | 	while (*p >= 'a' && *p <= 'z') | 
 | 		p++; | 
 | 	savechar = *p; | 
 | 	*p = '\0'; | 
 | 	ret = match(directive, directives); | 
 | 	*p = savechar; | 
 |  | 
 | 	/* If we don't recognize this compiler directive or we happen to just | 
 | 	 * be gobbling up text while waiting for an #endif or #elif or #else | 
 | 	 * in the case of an #elif we must check the zero_value and return an | 
 | 	 * ELIF or an ELIFFALSE. | 
 | 	 */ | 
 |  | 
 | 	if (ret == ELIF && !parse_it) | 
 | 	{ | 
 | 	    while (*p == ' ' || *p == '\t') | 
 | 		p++; | 
 | 	    /* | 
 | 	     * parse an expression. | 
 | 	     */ | 
 | 	    debug(0,("%s, line %d: #elif %s ", | 
 | 		   file->i_file, filep->f_line, p)); | 
 | 	    ret = zero_value(p, filep, file_red, symbols); | 
 | 	    if (ret != IF) | 
 | 	    { | 
 | 		debug(0,("false...\n")); | 
 | 		if (ret == IFFALSE) | 
 | 		    return(ELIFFALSE); | 
 | 		else | 
 | 		    return(ELIFGUESSFALSE); | 
 | 	    } | 
 | 	    else | 
 | 	    { | 
 | 		debug(0,("true...\n")); | 
 | 		return(ELIF); | 
 | 	    } | 
 | 	} | 
 |  | 
 | 	if (ret < 0 || ! parse_it) | 
 | 		return(ret); | 
 |  | 
 | 	/* | 
 | 	 * now decide how to parse the directive, and do it. | 
 | 	 */ | 
 | 	while (*p == ' ' || *p == '\t') | 
 | 		p++; | 
 | 	switch (ret) { | 
 | 	case IF: | 
 | 		/* | 
 | 		 * parse an expression. | 
 | 		 */ | 
 | 		ret = zero_value(p, filep, file_red, symbols); | 
 | 		debug(0,("%s, line %d: %s #if %s\n", | 
 | 			 file->i_file, filep->f_line, ret?"false":"true", p)); | 
 | 		break; | 
 | 	case IFDEF: | 
 | 	case IFNDEF: | 
 | 		debug(0,("%s, line %d: #%s %s\n", | 
 | 			file->i_file, filep->f_line, directives[ret], p)); | 
 | 	case UNDEF: | 
 | 		/* | 
 | 		 * separate the name of a single symbol. | 
 | 		 */ | 
 | 		while (isalnum(*p) || *p == '_') | 
 | 			*line++ = *p++; | 
 | 		*line = '\0'; | 
 | 		break; | 
 | 	case INCLUDE: | 
 | 		debug(2,("%s, line %d: #include %s\n", | 
 | 			file->i_file, filep->f_line, p)); | 
 |  | 
 | 		/* Support ANSI macro substitution */ | 
 | 		{ | 
 | 			char *sym = hash_lookup(p, symbols); | 
 | 			while (sym) | 
 | 			{ | 
 | 				p = sym; | 
 | 				debug(3,("%s : #includes SYMBOL %s\n", | 
 | 							file->i_incstring, | 
 | 							sym)); | 
 | 				/* mark file as having included a 'soft include' */ | 
 | 				file->i_included_sym = TRUE;  | 
 | 				sym = hash_lookup(p, symbols); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * Separate the name of the include file. | 
 | 		 */ | 
 | 		while (*p && *p != '"' && *p != '<') | 
 | 			p++; | 
 | 		if (! *p) | 
 | 			return(-2); | 
 | 		if (*p++ == '"') { | 
 | 			ret = INCLUDEDOT; | 
 | 			while (*p && *p != '"') | 
 | 				*line++ = *p++; | 
 | 		} else | 
 | 			while (*p && *p != '>') | 
 | 				*line++ = *p++; | 
 | 		*line = '\0'; | 
 | 		break; | 
 | 	case DEFINE: | 
 | 		/* | 
 | 		 * copy the definition back to the beginning of the line. | 
 | 		 */ | 
 | 		strcpy (line, p); | 
 | 		break; | 
 | 	case ELSE: | 
 | 	case ENDIF: | 
 | 	case ELIF: | 
 | 	case PRAGMA: | 
 | 	case ERROR: | 
 | 	case IDENT: | 
 | 	case SCCS: | 
 | 	case EJECT: | 
 | 		debug(0,("%s, line %d: #%s\n", | 
 | 			file->i_file, filep->f_line, directives[ret])); | 
 | 		/* | 
 | 		 * nothing to do. | 
 | 		 */ | 
 | 		break; | 
 | 	} | 
 | 	return(ret); | 
 | } | 
 |  | 
 | /* | 
 |  * HACK! - so that we do not have to introduce 'symbols' in each cppsetup.c | 
 |  * function...  It's safe, functions from cppsetup.c don't return here. | 
 |  */ | 
 | struct symhash *global_symbols = NULL; | 
 |  | 
 | char * isdefined( symbol ) | 
 | 	register char *symbol; | 
 | { | 
 | 	return hash_lookup( symbol, global_symbols ); | 
 | } | 
 |  | 
 | /* | 
 |  * Return type based on if the #if expression evaluates to 0 | 
 |  */ | 
 | int zero_value(exp, filep, file_red, symbols) | 
 | 	register char	*exp; | 
 | 	register struct filepointer *filep; | 
 | 	register struct inclist *file_red; | 
 | 	register struct symhash *symbols; | 
 | { | 
 | 	global_symbols = symbols; /* HACK! see above */ | 
 | 	if (cppsetup(exp, filep, file_red)) | 
 | 	    return(IFFALSE); | 
 | 	else | 
 | 	    return(IF); | 
 | } | 
 |  | 
 | void define( def, symbols ) | 
 |     char	        *def; | 
 |     struct symhash **symbols; | 
 | { | 
 |     char *val; | 
 |  | 
 |     /* Separate symbol name and its value */ | 
 |     val = def; | 
 |     while (isalnum(*val) || *val == '_') | 
 | 	val++; | 
 |     if (*val) | 
 | 	*val++ = '\0'; | 
 |     while (*val == ' ' || *val == '\t') | 
 | 	val++; | 
 |  | 
 |     if (!*val) | 
 | 	val = "1"; | 
 |     hash_define( def, val, symbols ); | 
 | } | 
 |  | 
 | static int hash( str ) | 
 |     register char *str; | 
 | { | 
 |     /* Hash (Kernighan and Ritchie) */ | 
 |     register unsigned int hashval = 0; | 
 |     //char *s = str; | 
 |  | 
 |     for ( ; *str; str++ ) | 
 |     { | 
 |         hashval = ( hashval * SYMHASHSEED ) + ( *str ); | 
 |     } | 
 |  | 
 |     //fprintf( stderr, "hash: %s, %d\n", s, hashval & ( SYMHASHMEMBERS - 1 ) ); | 
 |     return hashval & ( SYMHASHMEMBERS - 1 ); | 
 | } | 
 |  | 
 | struct symhash *hash_copy( symbols ) | 
 |     struct symhash *symbols; | 
 | { | 
 |     int i; | 
 |     struct symhash *newsym; | 
 |     if ( !symbols ) | 
 |         return NULL; | 
 |  | 
 |     newsym = (struct symhash *) malloc( sizeof( struct symhash ) ); | 
 |      | 
 |     for ( i = 0; i < SYMHASHMEMBERS; ++i ) | 
 |     { | 
 |         if ( !symbols->s_pairs[ i ] ) | 
 |             newsym->s_pairs[ i ] = NULL; | 
 |         else | 
 |         { | 
 |             struct pair *it = symbols->s_pairs[ i ]; | 
 |             struct pair *nw = newsym->s_pairs[ i ] = (struct pair*) malloc( sizeof( struct pair ) ); | 
 |             nw->p_name = it->p_name; | 
 |             nw->p_value = it->p_value; | 
 |             nw->p_next = NULL; | 
 |              | 
 |             while ( it->p_next ) | 
 |             { | 
 |                 nw->p_next = (struct pair*) malloc( sizeof( struct pair ) ); | 
 |                 it = it->p_next; | 
 |                 nw = nw->p_next; | 
 |                 nw->p_name = it->p_name; | 
 |                 nw->p_value = it->p_value; | 
 |                 nw->p_next = NULL; | 
 |             } | 
 |         } | 
 |     } | 
 |     return newsym; | 
 | } | 
 |  | 
 | void hash_free( symbols ) | 
 |     struct symhash *symbols; | 
 | { | 
 |     int i; | 
 |  | 
 |     if ( !symbols ) | 
 |         return; | 
 |  | 
 |     for ( i = 0; i < SYMHASHMEMBERS; ++i ) | 
 |     { | 
 |         struct pair *it = symbols->s_pairs[ i ]; | 
 |         struct pair *next; | 
 |         while ( it ) | 
 |         { | 
 |             next = it->p_next; | 
 |             free( it ); | 
 |             it = next; | 
 |         } | 
 |     } | 
 |     free( symbols->s_pairs ); | 
 | } | 
 |  | 
 | void hash_define( name, val, symbols ) | 
 |     char            *name, *val; | 
 |     struct symhash **symbols; | 
 | { | 
 |     int hashval; | 
 |     struct pair *it; | 
 |  | 
 |     if ( !symbols ) | 
 |         return; | 
 |  | 
 |     /* Make space if it's needed */ | 
 |     if ( *symbols == NULL ) | 
 |     { | 
 |         int i; | 
 |  | 
 |         *symbols = (struct symhash *) malloc( sizeof( struct symhash ) ); | 
 |         if ( *symbols == NULL ) | 
 |             fatalerr( "malloc()/realloc() failure in insert_defn()\n" ); | 
 |  | 
 |         for ( i = 0; i < SYMHASHMEMBERS; ++i ) | 
 |             (*symbols)->s_pairs[i] = NULL; | 
 |     } | 
 |  | 
 |     hashval = hash( name ); | 
 |     it = (*symbols)->s_pairs[ hashval ]; | 
 |      | 
 |     /* Replace/insert the symbol */ | 
 |     if ( it == NULL ) | 
 |     { | 
 |         it = (*symbols)->s_pairs[ hashval ] = (struct pair*) malloc( sizeof( struct pair ) ); | 
 |         it->p_name = copy( name ); | 
 |         it->p_value = copy( val ); | 
 |         it->p_next = NULL; | 
 |     } | 
 |     else if ( strcmp( it->p_name, name ) == 0 ) | 
 |     { | 
 |         it->p_value = copy( val ); | 
 |     } | 
 |     else | 
 |     { | 
 |         while ( it->p_next && ( strcmp( it->p_next->p_name, name ) != 0 ) ) | 
 |         { | 
 |             it = it->p_next; | 
 |         } | 
 |         if ( it->p_next ) | 
 |             it->p_next->p_name = copy( name ); | 
 |         else | 
 |         { | 
 |             it->p_next = (struct pair*) malloc( sizeof( struct pair ) ); | 
 |             it->p_next->p_name = copy( name ); | 
 |             it->p_next->p_value = copy( val ); | 
 |             it->p_next->p_next = NULL; | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | char *hash_lookup( symbol, symbols ) | 
 |     char           *symbol; | 
 |     struct symhash *symbols; | 
 | { | 
 |     struct pair *it; | 
 |  | 
 |     if ( !symbols ) | 
 |         return NULL; | 
 |      | 
 |     it = symbols->s_pairs[ hash( symbol ) ]; | 
 |  | 
 |     while ( it && ( strcmp( it->p_name, symbol ) != 0 ) ) | 
 |     { | 
 |         it = it->p_next; | 
 |     } | 
 |     if ( it ) | 
 |         return it->p_value; | 
 |  | 
 |     return NULL; | 
 | } | 
 |  | 
 | void hash_undefine( symbol, symbols ) | 
 |     char           *symbol; | 
 |     struct symhash *symbols; | 
 | { | 
 |     int hashval; | 
 |     struct pair *it; | 
 |  | 
 |     if ( !symbols ) | 
 |         return; | 
 |      | 
 |     hashval = hash( symbol ); | 
 |     it = symbols->s_pairs[ hashval ]; | 
 |      | 
 |     /* Replace/insert the symbol */ | 
 |     if ( it == NULL ) | 
 |         return; | 
 |     else if ( strcmp( it->p_name, symbol ) == 0 ) | 
 |     { | 
 |         if ( it->p_next ) | 
 |         { | 
 |             struct pair *tmp; | 
 |             it->p_name = it->p_next->p_name; | 
 |             it->p_value = it->p_next->p_value; | 
 |             tmp = it->p_next->p_next; | 
 |             free( it->p_next ); | 
 |             it->p_next = tmp; | 
 |         } | 
 |         else | 
 |         { | 
 |             free( it ); | 
 |             symbols->s_pairs[ hashval ] = NULL; | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         while ( it->p_next && ( strcmp( it->p_next->p_name, symbol ) != 0 ) ) | 
 |         { | 
 |             it = it->p_next; | 
 |         } | 
 |         if ( it->p_next ) | 
 |         { | 
 |             struct pair *tmp = it->p_next; | 
 |             it->p_next = it->p_next->p_next; | 
 |             free( tmp ); | 
 |         } | 
 |     } | 
 | } |