| /* | |
| * 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 "wine/port.h" | |
| #include <assert.h> | |
| #include <fcntl.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <stdarg.h> | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> | |
| #endif | |
| #ifdef HAVE_IO_H | |
| # include <io.h> | |
| #endif | |
| #include "wine/wpp.h" | |
| #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; | |
| int len; | |
| assert(str != NULL); | |
| len = strlen(str)+1; | |
| s = pp_xmalloc(len); | |
| return memcpy(s, str, len); | |
| } | |
| /* 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_xstrdup(pp_status.input ? pp_status.input : "<internal or cmdline>"); | |
| 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_xstrdup(pp_status.input ? pp_status.input : "<internal or cmdline>"); | |
| 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) | |
| { | |
| if(*tok) { | |
| char *dir; | |
| char *cptr; | |
| 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; | |
| char *cptr; | |
| const char *ccptr; | |
| int i, fd; | |
| cpy = pp_xmalloc(strlen(name)+1); | |
| cptr = cpy; | |
| for(ccptr = name; *ccptr; ccptr++) | |
| { | |
| /* Convert to forward slash */ | |
| if(*ccptr == '\\') { | |
| /* kill double backslash */ | |
| if(ccptr[1] == '\\') | |
| ccptr++; | |
| *cptr = '/'; | |
| }else { | |
| *cptr = *ccptr; | |
| } | |
| cptr++; | |
| } | |
| *cptr = '\0'; | |
| 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 const char * const 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); | |
| } |