/* * Copyright 1998 Bertho A. Stultiens (BS) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include "wpp_private.h" struct pp_status pp_status; #define HASHKEY 2039 typedef struct pp_def_state { struct pp_def_state *next; pp_entry_t *defines[HASHKEY]; } pp_def_state_t; static pp_def_state_t *pp_def_state; #define MAXIFSTACK 64 static pp_if_state_t if_stack[MAXIFSTACK]; static int if_stack_idx = 0; #if 0 void pp_print_status(void) __attribute__((destructor)); void pp_print_status(void) { int i; int sum; int total = 0; pp_entry_t *ppp; fprintf(stderr, "Defines statistics:\n"); for(i = 0; i < HASHKEY; i++) { sum = 0; for(ppp = pp_def_state->defines[i]; ppp; ppp = ppp->next) sum++; total += sum; if (sum) fprintf(stderr, "%4d, %3d\n", i, sum); } fprintf(stderr, "Total defines: %d\n", total); } #endif void *pp_xmalloc(size_t size) { void *res; assert(size > 0); res = malloc(size); if(res == NULL) { fprintf(stderr, "Virtual memory exhausted.\n"); exit(2); } return res; } void *pp_xrealloc(void *p, size_t size) { void *res; assert(size > 0); res = realloc(p, size); if(res == NULL) { fprintf(stderr, "Virtual memory exhausted.\n"); exit(2); } return res; } char *pp_xstrdup(const char *str) { char *s; assert(str != NULL); s = pp_xmalloc(strlen(str)+1); return strcpy(s, str); } /* Don't comment on the hash, its primitive but functional... */ static int pphash(const char *str) { int sum = 0; while(*str) sum += *str++; return sum % HASHKEY; } pp_entry_t *pplookup(const char *ident) { int idx = pphash(ident); pp_entry_t *ppp; for(ppp = pp_def_state->defines[idx]; ppp; ppp = ppp->next) { if(!strcmp(ident, ppp->ident)) return ppp; } return NULL; } static void free_pp_entry( pp_entry_t *ppp, int idx ) { if(ppp->iep) { if(ppp->iep == pp_includelogiclist) { pp_includelogiclist = ppp->iep->next; if(pp_includelogiclist) pp_includelogiclist->prev = NULL; } else { ppp->iep->prev->next = ppp->iep->next; if(ppp->iep->next) ppp->iep->next->prev = ppp->iep->prev; } free(ppp->iep->filename); free(ppp->iep); } if(pp_def_state->defines[idx] == ppp) { pp_def_state->defines[idx] = ppp->next; if(pp_def_state->defines[idx]) pp_def_state->defines[idx]->prev = NULL; } else { ppp->prev->next = ppp->next; if(ppp->next) ppp->next->prev = ppp->prev; } free(ppp); } /* push a new (empty) define state */ void pp_push_define_state(void) { pp_def_state_t *state = pp_xmalloc( sizeof(*state) ); memset( state->defines, 0, sizeof(state->defines) ); state->next = pp_def_state; pp_def_state = state; } /* pop the current define state */ void pp_pop_define_state(void) { int i; pp_entry_t *ppp; pp_def_state_t *state; for (i = 0; i < HASHKEY; i++) { while ((ppp = pp_def_state->defines[i]) != NULL) free_pp_entry( ppp, i ); } state = pp_def_state; pp_def_state = state->next; free( state ); } void pp_del_define(const char *name) { pp_entry_t *ppp; if((ppp = pplookup(name)) == NULL) { if(pp_status.pedantic) ppwarning("%s was not defined", name); return; } free_pp_entry( ppp, pphash(name) ); if(pp_status.debug) printf("Deleted (%s, %d) <%s>\n", pp_status.input, pp_status.line_number, name); } pp_entry_t *pp_add_define(char *def, char *text) { int len; char *cptr; int idx = pphash(def); pp_entry_t *ppp; if((ppp = pplookup(def)) != NULL) { if(pp_status.pedantic) ppwarning("Redefinition of %s\n\tPrevious definition: %s:%d", def, ppp->filename, ppp->linenumber); pp_del_define(def); } ppp = pp_xmalloc(sizeof(pp_entry_t)); memset( ppp, 0, sizeof(*ppp) ); ppp->ident = def; ppp->type = def_define; ppp->subst.text = text; ppp->filename = pp_status.input ? pp_xstrdup(pp_status.input) : ""; ppp->linenumber = pp_status.input ? pp_status.line_number : 0; ppp->next = pp_def_state->defines[idx]; pp_def_state->defines[idx] = ppp; if(ppp->next) ppp->next->prev = ppp; if(text) { /* Strip trailing white space from subst text */ len = strlen(text); while(len && strchr(" \t\r\n", text[len-1])) { text[--len] = '\0'; } /* Strip leading white space from subst text */ for(cptr = text; *cptr && strchr(" \t\r", *cptr); cptr++) ; if(text != cptr) memmove(text, cptr, strlen(cptr)+1); } if(pp_status.debug) printf("Added define (%s, %d) <%s> to <%s>\n", pp_status.input, pp_status.line_number, ppp->ident, text ? text : "(null)"); return ppp; } pp_entry_t *pp_add_macro(char *id, marg_t *args[], int nargs, mtext_t *exp) { int idx = pphash(id); pp_entry_t *ppp; if((ppp = pplookup(id)) != NULL) { if(pp_status.pedantic) ppwarning("Redefinition of %s\n\tPrevious definition: %s:%d", id, ppp->filename, ppp->linenumber); pp_del_define(id); } ppp = pp_xmalloc(sizeof(pp_entry_t)); memset( ppp, 0, sizeof(*ppp) ); ppp->ident = id; ppp->type = def_macro; ppp->margs = args; ppp->nargs = nargs; ppp->subst.mtext= exp; ppp->filename = pp_status.input ? pp_xstrdup(pp_status.input) : ""; ppp->linenumber = pp_status.input ? pp_status.line_number : 0; ppp->next = pp_def_state->defines[idx]; pp_def_state->defines[idx] = ppp; if(ppp->next) ppp->next->prev = ppp; if(pp_status.debug) { fprintf(stderr, "Added macro (%s, %d) <%s(%d)> to <", pp_status.input, pp_status.line_number, ppp->ident, nargs); for(; exp; exp = exp->next) { switch(exp->type) { case exp_text: fprintf(stderr, " \"%s\" ", exp->subst.text); break; case exp_stringize: fprintf(stderr, " #(%d) ", exp->subst.argidx); break; case exp_concat: fprintf(stderr, "##"); break; case exp_subst: fprintf(stderr, " <%d> ", exp->subst.argidx); break; } } fprintf(stderr, ">\n"); } return ppp; } /* *------------------------------------------------------------------------- * Include management *------------------------------------------------------------------------- */ #if defined(_Windows) || defined(__MSDOS__) #define INCLUDESEPARATOR ";" #else #define INCLUDESEPARATOR ":" #endif static char **includepath; static int nincludepath = 0; void wpp_add_include_path(const char *path) { char *tok; char *cpy = pp_xstrdup(path); tok = strtok(cpy, INCLUDESEPARATOR); while(tok) { char *dir; char *cptr; if(strlen(tok) == 0) continue; dir = pp_xstrdup(tok); for(cptr = dir; *cptr; cptr++) { /* Convert to forward slash */ if(*cptr == '\\') *cptr = '/'; } /* Kill eventual trailing '/' */ if(*(cptr = dir + strlen(dir)-1) == '/') *cptr = '\0'; /* Add to list */ nincludepath++; includepath = pp_xrealloc(includepath, nincludepath * sizeof(*includepath)); includepath[nincludepath-1] = dir; tok = strtok(NULL, INCLUDESEPARATOR); } free(cpy); } char *wpp_find_include(const char *name, int search) { char *cpy = pp_xstrdup(name); char *cptr; int i, fd; for(cptr = cpy; *cptr; cptr++) { /* kill double backslash */ if(*cptr == '\\' && *(cptr+1) == '\\') memmove(cptr, cptr+1, strlen(cptr)); /* Convert to forward slash */ if(*cptr == '\\') *cptr = '/'; } if(search) { /* Search current dir and then -I path */ fd = open( cpy, O_RDONLY ); if (fd != -1) { close( fd ); return cpy; } } /* Search -I path */ for(i = 0; i < nincludepath; i++) { char *path; path = pp_xmalloc(strlen(includepath[i]) + strlen(cpy) + 2); strcpy(path, includepath[i]); strcat(path, "/"); strcat(path, cpy); fd = open( path, O_RDONLY ); if (fd != -1) { close( fd ); free( cpy ); return path; } free( path ); } free( cpy ); return NULL; } FILE *pp_open_include(const char *name, int search, char **newpath) { char *path; FILE *fp; if (!(path = wpp_find_include( name, search ))) return NULL; fp = fopen(path, "rt"); if (fp) { if (pp_status.debug) printf("Going to include <%s>\n", path); if (newpath) *newpath = path; else free( path ); return fp; } free( path ); return NULL; } /* *------------------------------------------------------------------------- * #if, #ifdef, #ifndef, #else, #elif and #endif state management * * #if state transitions are made on basis of the current TOS and the next * required state. The state transitions are required to housekeep because * #if:s can be nested. The ignore case is activated to prevent output from * within a false clause. * Some special cases come from the fact that the #elif cases are not * binary, but three-state. The problem is that all other elif-cases must * be false when one true one has been found. A second problem is that the * #else clause is a final clause. No extra #else:s may follow. * * The states mean: * if_true Process input to output * if_false Process input but no output * if_ignore Process input but no output * if_elif Process input but no output * if_elsefalse Process input but no output * if_elsettrue Process input to output * * The possible state-sequences are [state(stack depth)] (rest can be deduced): * TOS #if 1 #else #endif * if_true(n) if_true(n+1) if_elsefalse(n+1) * if_false(n) if_ignore(n+1) if_ignore(n+1) * if_elsetrue(n) if_true(n+1) if_elsefalse(n+1) * if_elsefalse(n) if_ignore(n+1) if_ignore(n+1) * if_elif(n) if_ignore(n+1) if_ignore(n+1) * if_ignore(n) if_ignore(n+1) if_ignore(n+1) * * TOS #if 1 #elif 0 #else #endif * if_true(n) if_true(n+1) if_elif(n+1) if_elif(n+1) * if_false(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) * if_elsetrue(n) if_true(n+1) if_elif(n+1) if_elif(n+1) * if_elsefalse(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) * if_elif(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) * if_ignore(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) * * TOS #if 0 #elif 1 #else #endif * if_true(n) if_false(n+1) if_true(n+1) if_elsefalse(n+1) * if_false(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) * if_elsetrue(n) if_false(n+1) if_true(n+1) if_elsefalse(n+1) * if_elsefalse(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) * if_elif(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) * if_ignore(n) if_ignore(n+1) if_ignore(n+1) if_ignore(n+1) * *------------------------------------------------------------------------- */ static char *pp_if_state_str[] = { "if_false", "if_true", "if_elif", "if_elsefalse", "if_elsetrue", "if_ignore" }; void pp_push_if(pp_if_state_t s) { if(if_stack_idx >= MAXIFSTACK) pp_internal_error(__FILE__, __LINE__, "#if-stack overflow; #{if,ifdef,ifndef} nested too deeply (> %d)", MAXIFSTACK); if(pp_flex_debug) fprintf(stderr, "Push if %s:%d: %s(%d) -> %s(%d)\n", pp_status.input, pp_status.line_number, pp_if_state_str[pp_if_state()], if_stack_idx, pp_if_state_str[s], if_stack_idx+1); if_stack[if_stack_idx++] = s; switch(s) { case if_true: case if_elsetrue: break; case if_false: case if_elsefalse: case if_elif: case if_ignore: pp_push_ignore_state(); break; } } pp_if_state_t pp_pop_if(void) { if(if_stack_idx <= 0) pperror("#{endif,else,elif} without #{if,ifdef,ifndef} (#if-stack underflow)"); switch(pp_if_state()) { case if_true: case if_elsetrue: break; case if_false: case if_elsefalse: case if_elif: case if_ignore: pp_pop_ignore_state(); break; } if(pp_flex_debug) fprintf(stderr, "Pop if %s:%d: %s(%d) -> %s(%d)\n", pp_status.input, pp_status.line_number, pp_if_state_str[pp_if_state()], if_stack_idx, pp_if_state_str[if_stack[if_stack_idx <= 1 ? if_true : if_stack_idx-2]], if_stack_idx-1); return if_stack[--if_stack_idx]; } pp_if_state_t pp_if_state(void) { if(!if_stack_idx) return if_true; else return if_stack[if_stack_idx-1]; } void pp_next_if_state(int i) { switch(pp_if_state()) { case if_true: case if_elsetrue: pp_push_if(i ? if_true : if_false); break; case if_false: case if_elsefalse: case if_elif: case if_ignore: pp_push_if(if_ignore); break; default: pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #{if,ifdef,ifndef} directive", (int)pp_if_state()); } } int pp_get_if_depth(void) { return if_stack_idx; } /* #define WANT_NEAR_INDICATION */ static void generic_msg(const char *s, const char *t, const char *n, va_list ap) { fprintf(stderr, "%s:%d:%d: %s: ", pp_status.input ? pp_status.input : "stdin", pp_status.line_number, pp_status.char_number, t); vfprintf(stderr, s, ap); #ifdef WANT_NEAR_INDICATION { char *cpy, *p; if(n) { cpy = pp_xstrdup(n); for (p = cpy; *p; p++) if(!isprint(*p)) *p = ' '; fprintf(stderr, " near '%s'", cpy); free(cpy); } } #endif fprintf(stderr, "\n"); } int pperror(const char *s, ...) { va_list ap; va_start(ap, s); generic_msg(s, "Error", pptext, ap); va_end(ap); exit(1); return 1; } int ppwarning(const char *s, ...) { va_list ap; va_start(ap, s); generic_msg(s, "Warning", pptext, ap); va_end(ap); return 0; } void pp_internal_error(const char *file, int line, const char *s, ...) { va_list ap; va_start(ap, s); fprintf(stderr, "Internal error (please report) %s %d: ", file, line); vfprintf(stderr, s, ap); fprintf(stderr, "\n"); va_end(ap); exit(3); }