/* File: gpp.c -- generic preprocessor ** Author: Denis Auroux, Tristan Miller ** Contact: tristan@logological.org ** ** Copyright (C) 1996, 1999, 2001 Denis Auroux ** Copyright (C) 2003-2023 Tristan Miller ** ** This program 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 3 of the ** License, or (at your option) any later version. ** ** This program 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 program. If not, see . */ /* To compile under MS VC++, one must define WIN_NT */ #if HAVE_CONFIG_H # include #endif #ifdef WIN_NT /* WIN NT settings */ #define popen _popen #define pclose _pclose #define my_strdup _strdup #define my_strcasecmp _stricmp #define SLASH '\\' #define DEFAULT_CRLF 1 #else /* UNIX settings */ #define SLASH '/' #define DEFAULT_CRLF 0 #endif #include #include #include #include #if HAVE_FNMATCH_H # include #endif #include #define STACKDEPTH 50 #define MAXARGS 100 #define MAXINCL 128 /* max # of include dirs */ #define MAX_GPP_NUM_SIZE 15 #define MAX_GPP_DATE_SIZE 1024 typedef struct MODE { char *mStart; /* before macro name */ char *mEnd; /* end macro without arg */ char *mArgS; /* start 1st argument */ char *mArgSep; /* separate arguments */ char *mArgE; /* end last argument */ char *mArgRef; /* how to refer to arguments in a def */ char quotechar; /* quote next char */ char *stackchar; /* characters to stack */ char *unstackchar; /* characters to unstack */ } MODE; /* translation for delimiters : \001 = \b = ' ' = one or more spaces \201 = \!b = non-space \002 = \w = zero or more spaces \003 = \B = one or more spaces or \n \203 = \!B = non-space nor \n \004 = \W = zero or more spaces or \n \005 = \a = alphabetic (a-z, A-Z) \205 = \!a = non-alphabetic \006 = \A = alphabetic or space/\n \206 = \!A \007 = \# = numeric (0-9) \207 = \!# \010 = \i = identifier (a-zA-Z0-9_) \210 = \!i \011 = \t, \012 = \n \211 = \!t, \212 = \!n \013 = \o = operator (+-*\/^<>=`~:.?@#&!%|) \213 = \!o \014 = \O = operator or ()[]{} \214 = \!O */ /* st end args sep arge ref quot stk unstk*/ struct MODE CUser = {"", "", "(", ",", ")", "#", '\\', "(", ")" }; struct MODE CMeta = {"#", "\n", "\001","\001","\n","#", '\\', "(", ")" }; struct MODE KUser = {"", "", "(", ",", ")", "#", 0, "(", ")" }; struct MODE KMeta = {"\n#\002","\n", "\001","\001","\n","#", 0, "", "" }; struct MODE Tex = {"\\", "", "{", "}{", "}", "#", '@', "{", "}" }; struct MODE Html = {"<#", ">", "\003","|", ">", "#", '\\', "<", ">" }; struct MODE XHtml = {"<#", "/>", "\003","|", "/>","#", '\\', "<", ">" }; #define DEFAULT_OP_STRING (unsigned char *)"+-*/\\^<>=`~:.?@#&!%|" #define PROLOG_OP_STRING (unsigned char *)"+-*/\\^<>=`~:.?@#&" #define DEFAULT_OP_PLUS (unsigned char *)"()[]{}" #define DEFAULT_ID_STRING (unsigned char *)"\005\007_" /* or equiv. "A-Za-z0-9_" */ /* here we assume that longs are at least 32 bit... if not, change this ! */ #define LOG_LONG_BITS 5 #define CHARSET_SUBSET_LEN (256 >> LOG_LONG_BITS) typedef unsigned long *CHARSET_SUBSET; CHARSET_SUBSET DefaultOp, DefaultExtOp, PrologOp, DefaultId; typedef struct COMMENT { char *start; /* how the comment/string starts */ char *end; /* how it ends */ char quote; /* how to prevent it from ending */ char warn; /* a character that shouldn't be in there */ int flags[3]; /* meta, user, text */ struct COMMENT *next; } COMMENT; #define OUTPUT_TEXT 0x1 /* what's inside will be output */ #define OUTPUT_DELIM 0x2 /* the delimiters will be output */ #define PARSE_MACROS 0x4 /* macros inside will be parsed */ #define FLAG_IGNORE 0x40 #define FLAG_STRING (OUTPUT_TEXT|OUTPUT_DELIM) #define FLAG_COMMENT 0 #define FLAG_META 0 #define FLAG_USER 1 #define FLAG_TEXT 2 /* Some stuff I removed because it made for some impossible situations : #define PARSE_COMMENTS 0x8 comments inside comments will not be parsed because nesting comments is too complicated (syntax conflicts, esp. to find a comment's end) -- of course, unless the comment is ignored. #define MACRO_FRIENDLY 0x20 a comment-end is to be processed even if an unfinished macro call has started inside the comment, otherwise it's too hard do decide in advance where a comment ends. In particular foo('bar((((') is valid. #define PREVENT_DELIM 0x10 all comments will prevent macro delimitation, i.e. foo('bar) is invalid. -- of course, unless the comment is ignored. Too bad, #define foo '... terminates only at following "'". Unless one adds quotechars like in #define foo \' ... ALSO NOTE : comments are not allowed before the end of the first argument to a meta-macro. E.g. this is legal : #define foo <* blah *> 3 This is not legal : #define <* blah *> foo 3 If a comment occurs here, the behavior depends on the actual meta-macro : most will yield an error and stop gpp (#define, #undef, #ifdef/ifndef, #defeval, #include, #mode) ; #exec, #if and #eval should be ok ; #ifeq will always fail while #ifneq will always succeed ; */ typedef struct SPECS { struct MODE User, Meta; struct COMMENT *comments; struct SPECS *stack_next; int preservelf; CHARSET_SUBSET op_set, ext_op_set, id_set; } SPECS; struct SPECS *S; typedef struct MACRO { char *username, *macrotext, **argnames; int macrolen, nnamedargs; struct SPECS *define_specs; int defined_in_comment; } MACRO; struct MACRO *macros; int nmacros, nalloced; char *includedir[MAXINCL]; int nincludedirs; int execallowed; int dosmode; int autoswitch; /* must be a format-like string that has % % % in it. The first % is replaced with line number, the second with "filename", and the third with 1, 2 or blank Can also use ? instead of %. */ char *include_directive_marker = NULL; short WarningLevel = 2; /* controls if standard dirs, like /usr/include, are to be searched for #include and whether the current dir is to be searched first or last. */ int NoStdInc = 0; int NoCurIncFirst = 0; int CurDirIncLast = 0; int file_and_stdout = 0; char *IncludeFile = NULL; typedef struct OUTPUTCONTEXT { char *buf; int len, bufsize; FILE *f; } OUTPUTCONTEXT; typedef struct INPUTCONTEXT { char *buf; char *malloced_buf; /* what was actually malloc-ed (buf may have shifted) */ int len, bufsize; int lineno; char *filename; FILE *in; int argc; char **argv; char **namedargs; struct OUTPUTCONTEXT *out; int eof; int in_comment; int ambience; /* FLAG_TEXT, FLAG_USER or FLAG_META */ int may_have_args; } INPUTCONTEXT; struct INPUTCONTEXT *C; int commented[STACKDEPTH], iflevel; /* commented = 0: output, 1: not output, 2: not output because we're in a #elif and we've already gone through the right case (so #else/#elif can't toggle back to output) */ int parselevel; void ProcessContext(void); /* the main loop */ int findIdent(const char *b, int l); void delete_macro(int i); /* various recent additions */ void usage(void); void display_version(void); void bug(const char *s); void warning(const char *s); static void getDirname(const char *fname, char *dirname); static FILE *openInCurrentDir(const char *incfile); char *ArithmEval(int pos1, int pos2); void replace_definition_with_blank_lines(const char *start, const char *end, int skip); void replace_directive_with_blank_line(FILE *file); void write_include_marker(FILE *f, int lineno, char *filename, const char *marker); void construct_include_directive_marker(char **include_directive_marker, const char *includemarker_input); void escape_backslashes(const char *instr, char **outstr); static void DoInclude(char *file_name, int ignore_nonexistent); /* ** strdup() and my_strcasecmp() are not ANSI C, so here we define our own ** versions in case the compiler does not support them */ #if ! HAVE_STRDUP inline char *my_strdup(const char *s); inline char *my_strdup(const char *s) { size_t len = strlen(s) + 1; char *newstr = malloc(len); return newstr ? (char *) memcpy(newstr, s, len) : NULL ; } #else # undef my_strdup # define my_strdup strdup #endif #if ! HAVE_STRCASECMP int my_strcasecmp(const char *s, const char *s2) { do { char c1 = tolower(*s); char c2 = tolower(*s2); if (c1 > c2) return 1; if (c1 < c2) return -1; } while (*s++ && *s2++); return 0; } #else # undef my_strcasecmp # define my_strcasecmp strcasecmp #endif void bug(const char *s) { fprintf(stderr, "%s:%d: error: %s\n", C->filename, C->lineno, s); exit(EXIT_FAILURE); } void warning(const char *s) { fprintf(stderr, "%s:%d: warning: %s\n", C->filename, C->lineno, s); } struct SPECS *CloneSpecs(const struct SPECS *Q) { struct SPECS *P; struct COMMENT *x, *y; P = malloc(sizeof *P); if (P == NULL ) bug("Out of memory."); memcpy(P, Q, sizeof(struct SPECS)); P->stack_next = NULL; if (Q->comments != NULL ) P->comments = malloc(sizeof *(P->comments)); for (x = Q->comments, y = P->comments; x != NULL ; x = x->next, y = y->next) { memcpy(y, x, sizeof(struct COMMENT)); y->start = my_strdup(x->start); y->end = my_strdup(x->end); if (x->next != NULL ) y->next = malloc(sizeof *(y->next)); } return P; } void FreeComments(struct SPECS *Q) { struct COMMENT *p; while (Q && Q->comments != NULL ) { p = Q->comments; Q->comments = p->next; free(p->start); free(p->end); free(p); } } void PushSpecs(const struct SPECS *X) { struct SPECS *P; P = CloneSpecs(X); P->stack_next = S; S = P; } void PopSpecs(void) { struct SPECS *P; P = S; S = P->stack_next; FreeComments(P); free(P); if (S == NULL ) bug("#mode restore without #mode save"); } void display_version(void) { printf("gpp\n"); printf("Copyright (C) 1996-2001 Denis Auroux\n"); printf("Copyright (C) 2003-2020 Tristan Miller\n"); printf("This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); } void usage(void) { printf("Usage : gpp [-{o|O} outfile] [-I/include/path] [-Dname=val ...] [-z] [-x] [-m]\n"); printf(" [-n] [-C | -T | -H | -X | -P | -U ... [-M ...]] [+c str1 str2]\n"); printf(" [+s str1 str2 c] [long options] [infile]\n\n"); printf(" default: #define x y macro(arg,...)\n"); printf(" -C : maximum cpp compatibility (includes -n, +c, +s, ...)\n"); printf(" -T : TeX-like \\define{x}{y} \\macro{arg}{...}\n"); printf(" -H : HTML-like <#define x|y> <#macro arg|...>\n"); printf(" -X : XHTML-like <#define x|y/> <#macro arg|.../>\n"); printf(" -P : prolog compatible cpp-like mode\n"); printf(" -U : user-defined syntax (specified in 9 following args; see manual)\n"); printf(" -M : user-defined syntax for meta-macros (specified in 7 following args)\n\n"); printf(" -o : output to outfile\n"); printf(" -O : output to outfile and stdout\n"); printf(" -z : line terminator is CR-LF (MS-DOS style)\n"); printf(" -x : enable #exec built-in macro\n"); printf(" -m : enable automatic mode switching upon including .h/.c files\n"); printf(" -n : send LF characters serving as macro terminators to output\n"); printf(" +c : use next 2 args as comment start and comment end sequences\n"); printf(" +s : use next 3 args as string start, end and quote character\n\n"); printf(" Long options:\n"); printf(" --include file : process file before infile\n"); printf(" --nostdinc : don't search standard directories for files to include\n"); printf(" --nocurinc : don't search the current directory for files to include\n"); printf(" --curdirinclast : search the current directory last\n"); printf(" --warninglevel n : set warning level\n"); printf(" --includemarker formatstring : keep track of #include directives in output\n\n"); printf(" --version : display version information and exit\n"); printf(" -h, --help : display this message and exit\n\n"); } int isDelim(unsigned char c) { if (c >= 128) return 0; if ((c >= '0') && (c <= '9')) return 0; if ((c >= 'A') && (c <= 'Z')) return 0; if ((c >= 'a') && (c <= 'z')) return 0; if (c == '_') return 0; return 1; } int isWhite(char c) { if (c == ' ') return 1; if (c == '\t') return 1; if (c == '\n') return 1; return 0; } void newmacro(const char *s, int len, int hasspecs) { if (nmacros == nalloced) { nalloced = 2 * nalloced + 1; macros = realloc(macros, nalloced * sizeof *macros); if (macros == NULL ) bug("Out of memory"); } macros[nmacros].username = malloc(len + 1); strncpy(macros[nmacros].username, s, len); macros[nmacros].username[len] = 0; macros[nmacros].argnames = NULL; macros[nmacros].nnamedargs = 0; macros[nmacros].defined_in_comment = 0; if (hasspecs) macros[nmacros].define_specs = CloneSpecs(S); else macros[nmacros].define_specs = NULL; } void lookupArgRefs(int n) { int i, l; char *p; if (macros[n].argnames != NULL ) return; /* don't mess with those */ macros[n].nnamedargs = -1; l = strlen(S->User.mArgRef); for (i = 0, p = macros[n].macrotext; i < macros[n].macrolen; i++, p++) { if ((*p != 0) && (*p == S->User.quotechar)) { i++; p++; } else if (!strncmp(p, S->User.mArgRef, l)) if ((p[l] >= '1') && (p[l] <= '9')) { macros[n].nnamedargs = 0; return; } } } char *strNl0(const char *s) /* replace "\\n" by "\n" in a cmd-line arg */ { char *t, *u; t = malloc(strlen(s) + 1); u = t; while (*s != 0) { if ((*s == '\\') && (s[1] == 'n')) { *u = '\n'; s++; } else *u = *s; s++; u++; } *u = 0; return t; } char *strNl(const char *s) /* the same but with whitespace specifier handling */ { char *t, *u; int neg; t = malloc(strlen(s) + 1); u = t; if (!isDelim(*s)) bug("character not allowed to start a syntax specifier"); while (*s != 0) { if (((*s & 0x60) == 0) && (*s != '\n') && (*s != '\t')) bug("character not allowed in syntax specifier"); if (*s == '\\') { neg = (s[1] == '!'); switch (s[neg + 1]) { case 'n': case 'r': *u = '\n'; break; case 't': *u = '\t'; break; case 'b': /* one or more spaces */ *u = '\001'; break; case 'w': /* zero or more spaces */ if (neg) bug("\\w and \\W cannot be negated"); *u = '\002'; break; case 'B': /* one or more spaces or \n */ *u = '\003'; break; case 'W': /* zero or more spaces or \n */ if (neg) bug("\\w and \\W cannot be negated"); *u = '\004'; break; case 'a': /* alphabetic */ *u = '\005'; break; case 'A': /* alphabetic + space */ *u = '\006'; break; case '#': /* numeric */ *u = '\007'; break; case 'i': /* identifier */ *u = '\010'; break; case 'o': /* operator */ *u = '\013'; break; case 'O': /* operator/parenthesis */ *u = '\014'; break; default: *u = '\\'; neg = -1; } if (neg > 0) *u += (char) 128; s += neg + 1; } else if (*s == ' ') *u = '\001'; else *u = *s; s++; u++; } *u = 0; return t; } /* same as strnl() but for C strings & in-place */ char *strNl2(char *s, int check_delim) { char *u; int neg; u = s; if (check_delim && !isDelim(*s)) bug("character not allowed to start a syntax specifier"); while (*s != '"') { if (((*s & 0x60) == 0) && (*s != '\n') && (*s != '\t')) bug("character not allowed in syntax specifier"); if (*s == '\\') { neg = (s[1] == '!'); switch (s[neg + 1]) { case 'n': case 'r': *u = '\n'; break; case 't': *u = '\t'; break; case 'b': /* one or more spaces */ *u = '\001'; break; case 'w': /* zero or more spaces */ if (neg) bug("\\w and \\W cannot be negated"); *u = '\002'; break; case 'B': /* one or more spaces or \n */ *u = '\003'; break; case 'W': /* zero or more spaces or \n */ if (neg) bug("\\w and \\W cannot be negated"); *u = '\004'; break; case 'a': /* alphabetic */ *u = '\005'; break; case 'A': /* alphabetic + space */ *u = '\006'; break; case '#': /* numeric */ *u = '\007'; break; case 'i': /* identifier */ *u = '\010'; break; case 'o': /* operator */ *u = '\013'; break; case 'O': /* operator/parenthesis */ *u = '\014'; break; case '"': case '\\': if (!neg) { *u = s[1]; break; } default: bug("unknown escape sequence in syntax specifier"); } if (neg > 0) *u += (char) 128; s += neg + 1; } else if (*s == ' ') *u = '\001'; else *u = *s; if (*s == 0) bug("unterminated string in #mode command"); s++; u++; } *u = 0; return (s + 1); } int isWhitesep(const char *s) { while (isWhite(*s) || (*s == '\001') || (*s == '\002') || (*s == '\003') || (*s == '\004')) s++; return (*s == 0); } int nowhite_strcmp(char *s, char *t) { char *p; while (isWhite(*s)) s++; while (isWhite(*t)) t++; if ((*s == 0) || (*t == 0)) return strcmp(s, t); p = s + strlen(s) - 1; while (isWhite(*p)) *(p--) = 0; p = t + strlen(t) - 1; while (isWhite(*p)) *(p--) = 0; return strcmp(s, t); } void parseCmdlineDefine(const char *s) { int l, i, argc; for (l = 0; s[l] && (s[l] != '=') && (s[l] != '('); l++) ; i = findIdent(s, l); if (i >= 0) delete_macro(i); newmacro(s, l, 0); /* possibly allow named arguments: -Dmacro(arg1,arg2)=... (no spaces) */ if (s[l] == '(') { argc = 0; do { l++; i = l; while (!isDelim(s[i])) i++; if (s[i] != ',' && s[i] != ')') bug("invalid syntax in -D declaration"); if (i > l) argc++; macros[nmacros].argnames = realloc(macros[nmacros].argnames, (argc + 1) * sizeof(char *)); if (i > l) { macros[nmacros].argnames[argc - 1] = malloc(i - l + 1); memcpy(macros[nmacros].argnames[argc - 1], s + l, i - l); macros[nmacros].argnames[argc - 1][i - l] = 0; } l = i; } while (s[l] != ')'); l++; macros[nmacros].nnamedargs = argc; macros[nmacros].argnames[argc] = NULL; } /* the macro definition afterwards ! */ if (s[l] == '=') l++; else if (s[l] != 0) bug("invalid syntax in -D declaration"); macros[nmacros].macrolen = strlen(s + l); macros[nmacros++].macrotext = my_strdup(s + l); } int readModeDescription(char **args, struct MODE *mode, int ismeta) { if (!(*(++args))) return 0; mode->mStart = strNl(*args); if (!(*(++args))) return 0; mode->mEnd = strNl(*args); if (!(*(++args))) return 0; mode->mArgS = strNl(*args); if (!(*(++args))) return 0; mode->mArgSep = strNl(*args); if (!(*(++args))) return 0; mode->mArgE = strNl(*args); if (!(*(++args))) return 0; mode->stackchar = strNl(*args); if (!(*(++args))) return 0; mode->unstackchar = strNl(*args); if (ismeta) return 1; if (!(*(++args))) return 0; mode->mArgRef = strNl(*args); if (!(*(++args))) return 0; mode->quotechar = **args; return 1; } int parse_comment_specif(char c) { switch (c) { case 'I': case 'i': return FLAG_IGNORE; case 'c': return FLAG_COMMENT; case 's': return FLAG_STRING; case 'q': return OUTPUT_TEXT; case 'S': return FLAG_STRING | PARSE_MACROS; case 'Q': return OUTPUT_TEXT | PARSE_MACROS; case 'C': return FLAG_COMMENT | PARSE_MACROS; default: bug("Invalid comment/string modifier"); return 0; } } void add_comment(struct SPECS *S, const char *specif, char *start, char *end, char quote, char warn) { struct COMMENT *p; if (*start == 0) bug("Comment/string start delimiter must be non-empty"); for (p = S->comments; p != NULL ; p = p->next) if (!strcmp(p->start, start)) { if (strcmp(p->end, end)) /* already exists with a different end */ bug("Conflicting comment/string delimiter specifications"); free(p->start); free(p->end); break; } if (p == NULL ) { p = malloc(sizeof *p); p->next = S->comments; S->comments = p; } p->start = start; p->end = end; p->quote = quote; p->warn = warn; if (strlen(specif) != 3) bug("Invalid comment/string modifier"); p->flags[FLAG_META] = parse_comment_specif(specif[0]); p->flags[FLAG_USER] = parse_comment_specif(specif[1]); p->flags[FLAG_TEXT] = parse_comment_specif(specif[2]); } void delete_comment(struct SPECS *S, char *start) { struct COMMENT *p, *q; q = NULL; for (p = S->comments; p != NULL ; p = p->next) { if (!strcmp(p->start, start)) { if (q == NULL ) S->comments = p->next; else q->next = p->next; free(p->start); free(p->end); free(p); free(start); return; } else q = p; } free(start); } void outchar(char c) { if (C->out->bufsize) { if (C->out->len + 1 == C->out->bufsize) { C->out->bufsize = C->out->bufsize * 2; C->out->buf = realloc(C->out->buf, C->out->bufsize); if (C->out->buf == NULL ) bug("Out of memory"); } C->out->buf[C->out->len++] = c; } else { if (dosmode && (c == 10)) { fputc(13, C->out->f); if (file_and_stdout) fputc(13, stdout); } if (c != 13) { fputc(c, C->out->f); if (file_and_stdout) fputc(c, stdout); } } } void sendout(const char *s, int l, int proc) /* only process the quotechar, that's all */ { int i; if (!commented[iflevel]) for (i = 0; i < l; i++) { if (proc && (s[i] != 0) && (s[i] == S->User.quotechar)) { i++; if (i == l) return; } if (s[i] != 0) outchar(s[i]); } else replace_definition_with_blank_lines(s, s + l - 1, 0); } void extendBuf(int pos) { char *p; if (C->bufsize <= pos) { C->bufsize += pos; /* approx double */ p = malloc(C->bufsize); memcpy(p, C->buf, C->len); free(C->malloced_buf); C->malloced_buf = C->buf = p; if (C->buf == NULL ) bug("Out of memory"); } } char getChar(int pos) { static int lastchar = -666; int c; if (lastchar == -666 && !strcmp(S->Meta.mEnd, "\n")) lastchar = '\n'; if (C->in == NULL ) { if (pos >= C->len) return 0; else return C->buf[pos]; } extendBuf(pos); while (pos >= C->len) { do { c = fgetc(C->in); } while (c == 13); if (lastchar == '\n') C->lineno++; lastchar = c; if (c == EOF) c = 0; C->buf[C->len++] = (char) c; } return C->buf[pos]; } int whiteout(int *pos1, int *pos2) /* remove whitespace on both sides */ { while ((*pos1 < *pos2) && isWhite(getChar(*pos1))) (*pos1)++; while ((*pos1 < *pos2) && isWhite(getChar(*pos2 - 1))) (*pos2)--; return (*pos1 < *pos2); } int identifierEnd(int start) { char c; c = getChar(start); if (c == 0) return start; if (c == S->User.quotechar) { c = getChar(start + 1); if (c == 0) return (start + 1); if (isDelim(c)) return (start + 2); start += 2; c = getChar(start); } while (!isDelim(c)) c = getChar(++start); return start; } int iterIdentifierEnd(int start) { int x; while (1) { x = identifierEnd(start); if (x == start) return x; start = x; } } int IsInCharset(CHARSET_SUBSET x, int c) { return (x[c >> LOG_LONG_BITS] & 1L << (c & ((1 << LOG_LONG_BITS) - 1))) != 0; } int matchSequence(const char *s, int *pos) { int i = *pos; int match; char c; while (*s != 0) { if (!((*s) & 0x60)) { /* special sequences */ match = 1; switch ((*s) & 0x1f) { case '\001': c = getChar(i++); if ((c != ' ') && (c != '\t')) { match = 0; break; } case '\002': i--; do { c = getChar(++i); } while ((c == ' ') || (c == '\t')); break; case '\003': c = getChar(i++); if ((c != ' ') && (c != '\t') && (c != '\n')) { match = 0; break; } case '\004': i--; do { c = getChar(++i); } while ((c == ' ') || (c == '\t') || (c == '\n')); break; case '\006': c = getChar(i++); match = ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || (c == ' ') || (c == '\t') || (c == '\n'); break; case '\005': c = getChar(i++); match = ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); break; case '\007': c = getChar(i++); match = ((c >= '0') && (c <= '9')); break; case '\010': c = getChar(i++); match = IsInCharset(S->id_set, c); break; case '\011': c = getChar(i++); match = (c == '\t'); break; case '\012': c = getChar(i++); match = (c == '\n'); break; case '\013': c = getChar(i++); match = IsInCharset(S->op_set, c); break; case '\014': c = getChar(i++); match = IsInCharset(S->ext_op_set, c) || IsInCharset(S->op_set, c); break; } if ((*s) & 0x80) match = !match; if (!match) return 0; } else if (getChar(i++) != *s) return 0; s++; } *pos = i; return 1; } int matchEndSequence(const char *s, int *pos) { if (*s == 0) return 1; /* if terminator is \n and we're at end of input, let it be... */ if (getChar(*pos) == 0 && s[0] == '\n' && s[1] == 0) return 1; if (!matchSequence(s, pos)) return 0; if (S->preservelf && isWhite(getChar(*pos - 1))) (*pos)--; return 1; } int matchStartSequence(const char *s, int *pos) { char c; int match; if (!((*s) & 0x60)) { /* special sequences from prev. context */ c = getChar(*pos - 1); match = 1; if (*s == 0) return 1; switch ((*s) & 0x1f) { case '\001': if ((c != ' ') && (c != '\t')) { match = 0; break; } case '\002': break; case '\003': if ((c != ' ') && (c != '\t') && (c != '\n')) { match = 0; break; } case '\004': break; case '\006': if ((c == ' ') || (c == '\t') || (c == '\n')) break; case '\005': match = ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); break; case '\007': match = ((c >= '0') && (c <= '9')); break; case '\010': match = IsInCharset(S->id_set, c); break; case '\011': match = (c == '\t'); break; case '\012': match = (c == '\n'); break; case '\013': match = IsInCharset(S->op_set, c); break; case '\014': match = IsInCharset(S->ext_op_set, c) || IsInCharset(S->op_set, c); break; } if ((*s) & 0x80) match = !match; if (!match) return 0; s++; } return matchSequence(s, pos); } void AddToCharset(CHARSET_SUBSET x, int c) { x[c >> LOG_LONG_BITS] |= 1L << (c & ((1 << LOG_LONG_BITS) - 1)); } CHARSET_SUBSET MakeCharsetSubset(unsigned char *s) { CHARSET_SUBSET x; int i; unsigned char c; x = malloc(CHARSET_SUBSET_LEN * sizeof(unsigned long)); for (i = 0; i < CHARSET_SUBSET_LEN; i++) x[i] = 0; while (*s != 0) { if (!((*s) & 0x60)) { /* special sequences */ if ((*s) & 0x80) bug( "negated special sequences not allowed in charset specifications"); switch ((*s) & 0x1f) { case '\002': /* \w, \W, \i, \o, \O not allowed */ case '\004': case '\010': case '\013': case '\014': bug("special sequence not allowed in charset specification"); case '\003': AddToCharset(x, '\n'); case '\001': AddToCharset(x, ' '); case '\011': AddToCharset(x, '\t'); break; case '\006': AddToCharset(x, '\n'); AddToCharset(x, ' '); AddToCharset(x, '\t'); case '\005': for (c = 'A'; c <= 'Z'; c++) AddToCharset(x, c); for (c = 'a'; c <= 'z'; c++) AddToCharset(x, c); break; case '\007': for (c = '0'; c <= '9'; c++) AddToCharset(x, c); break; case '\012': AddToCharset(x, '\n'); break; } } else if ((s[1] == '-') && ((s[2] & 0x60) != 0) && (s[2] >= *s)) { for (c = *s; c <= s[2]; c++) AddToCharset(x, c); s += 2; } else AddToCharset(x, *s); s++; } return x; } int idequal(const char *b, int l, const char *s) { int i; if ((int) strlen(s) != l) return 0; for (i = 0; i < l; i++) if (b[i] != s[i]) return 0; return 1; } int findIdent(const char *b, int l) { int i; for (i = 0; i < nmacros; i++) if (idequal(b, l, macros[i].username)) return i; return -1; } int findNamedArg(const char *b, int l) { char *s; int i; for (i = 0;; i++) { s = C->namedargs[i]; if (s == NULL ) return -1; if (idequal(b, l, s)) return i; } } void shiftIn(int l) { int i; if (l <= 1) return; l--; if (l >= C->len) C->len = 0; else { if (C->len - l > 100) { /* we want to shrink that buffer */ C->buf += l; C->bufsize -= l; } else for (i = l; i < C->len; i++) C->buf[i - l] = C->buf[i]; C->len -= l; C->eof = (C->buf[0] == 0); } if (C->len <= 1) { if (C->in == NULL ) C->eof = 1; else C->eof = feof(C->in); } } void initthings(int argc, char **argv) { char **arg, *s; int i, isinput, isoutput, ishelp, ismode, hasmeta, usrmode; DefaultOp = MakeCharsetSubset(DEFAULT_OP_STRING); PrologOp = MakeCharsetSubset(PROLOG_OP_STRING); DefaultExtOp = MakeCharsetSubset(DEFAULT_OP_PLUS); DefaultId = MakeCharsetSubset(DEFAULT_ID_STRING); nmacros = 0; nalloced = 31; macros = malloc(nalloced * sizeof *macros); S = malloc(sizeof *S); S->User = CUser; S->Meta = CMeta; S->comments = NULL; S->stack_next = NULL; S->preservelf = 0; S->op_set = DefaultOp; S->ext_op_set = DefaultExtOp; S->id_set = DefaultId; C = malloc(sizeof *C); C->in = stdin; C->argc = 0; C->argv = NULL; C->filename = my_strdup("stdin"); C->out = malloc(sizeof *(C->out)); C->out->f = stdout; C->out->bufsize = 0; C->lineno = 1; isinput = isoutput = ismode = ishelp = hasmeta = usrmode = 0; nincludedirs = 0; C->bufsize = 80; C->len = 0; C->buf = C->malloced_buf = malloc(C->bufsize); C->eof = 0; C->namedargs = NULL; C->in_comment = 0; C->ambience = FLAG_TEXT; C->may_have_args = 0; commented[0] = 0; iflevel = 0; execallowed = 0; autoswitch = 0; dosmode = DEFAULT_CRLF; for (arg = argv + 1; *arg; arg++) { if (strcmp(*arg, "--help") == 0 || strcmp(*arg, "-h") == 0) { usage(); exit(EXIT_SUCCESS); } if (strcmp(*arg, "--version") == 0) { display_version(); exit(EXIT_SUCCESS); } #define DEPRECATED_WARNING fprintf(stderr, "gpp: warning: deprecated option `%s'; use `-%s' instead\n", *arg, *arg) if (strcmp(*arg, "-nostdinc") == 0) { DEPRECATED_WARNING; NoStdInc = 1; continue; } if (strcmp(*arg, "-nocurinc") == 0) { DEPRECATED_WARNING; NoCurIncFirst = 1; continue; } if (strcmp(*arg, "-curdirinclast") == 0) { DEPRECATED_WARNING; CurDirIncLast = 1; NoCurIncFirst = 1; continue; } if (strcmp(*arg, "-includemarker") == 0) { DEPRECATED_WARNING; if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } construct_include_directive_marker(&include_directive_marker, *arg); continue; } if (strcmp(*arg, "--include") == 0) { if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } IncludeFile = *arg; continue; } if (strcmp(*arg, "-warninglevel") == 0) { DEPRECATED_WARNING; if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } WarningLevel = atoi(*arg); continue; } if (strcmp(*arg, "--nostdinc") == 0) { NoStdInc = 1; continue; } if (strcmp(*arg, "--nocurinc") == 0) { NoCurIncFirst = 1; continue; } if (strcmp(*arg, "--curdirinclast") == 0) { CurDirIncLast = 1; NoCurIncFirst = 1; continue; } if (strcmp(*arg, "--includemarker") == 0) { if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } construct_include_directive_marker(&include_directive_marker, *arg); continue; } if (strcmp(*arg, "--warninglevel") == 0) { if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } WarningLevel = atoi(*arg); continue; } if (**arg == '+') { switch ((*arg)[1]) { case 'c': s = (*arg) + 2; if (*s == 0) s = "ccc"; if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } add_comment(S, s, strNl(*(arg - 1)), strNl(*arg), 0, 0); break; case 's': s = (*arg) + 2; if (*s == 0) s = "sss"; if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } add_comment(S, s, strNl(*(arg - 2)), strNl(*(arg - 1)), **arg, 0); break; case 'z': dosmode = 0; break; case 'n': S->preservelf = 0; break; default: ishelp = 1; } } else if (**arg != '-') { ishelp |= isinput; isinput = 1; C->in = fopen(*arg, "r"); free(C->filename); C->filename = my_strdup(*arg); if (C->in == NULL ) bug("Cannot open input file"); } else switch ((*arg)[1]) { case 'I': if (nincludedirs == MAXINCL) bug("too many include directories"); if ((*arg)[2] == 0) { if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } includedir[nincludedirs++] = my_strdup(*arg); } else includedir[nincludedirs++] = my_strdup((*arg) + 2); break; case 'C': ishelp |= ismode | hasmeta | usrmode; ismode = 1; S->User = KUser; S->Meta = KMeta; S->preservelf = 1; add_comment(S, "ccc", my_strdup("/*"), my_strdup("*/"), 0, 0); add_comment(S, "ccc", my_strdup("//"), my_strdup("\n"), 0, 0); add_comment(S, "ccc", my_strdup("\\\n"), my_strdup(""), 0, 0); add_comment(S, "sss", my_strdup("\""), my_strdup("\""), '\\', '\n'); add_comment(S, "sss", my_strdup("'"), my_strdup("'"), '\\', '\n'); break; case 'P': ishelp |= ismode | hasmeta | usrmode; ismode = 1; S->User = KUser; S->Meta = KMeta; S->preservelf = 1; S->op_set = PrologOp; add_comment(S, "css", my_strdup("\213/*"), my_strdup("*/"), 0, 0); /* \!o */ add_comment(S, "cii", my_strdup("\\\n"), my_strdup(""), 0, 0); add_comment(S, "css", my_strdup("%"), my_strdup("\n"), 0, 0); add_comment(S, "sss", my_strdup("\""), my_strdup("\""), 0, '\n'); add_comment(S, "sss", my_strdup("\207'"), my_strdup("'"), 0, '\n'); /* \!# */ break; case 'T': ishelp |= ismode | hasmeta | usrmode; ismode = 1; S->User = S->Meta = Tex; break; case 'H': ishelp |= ismode | hasmeta | usrmode; ismode = 1; S->User = S->Meta = Html; break; case 'X': ishelp |= ismode | hasmeta | usrmode; ismode = 1; S->User = S->Meta = XHtml; break; case 'U': ishelp |= ismode | usrmode; usrmode = 1; if (!readModeDescription(arg, &(S->User), 0)) { usage(); exit(EXIT_FAILURE); } arg += 9; if (!hasmeta) S->Meta = S->User; break; case 'M': ishelp |= ismode | hasmeta; hasmeta = 1; if (!readModeDescription(arg, &(S->Meta), 1)) { usage(); exit(EXIT_FAILURE); } arg += 7; break; case 'O': file_and_stdout = 1; case 'o': if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } ishelp |= isoutput; isoutput = 1; C->out->f = fopen(*arg, "w"); if (C->out->f == NULL ) bug("Cannot create output file"); break; case 'D': if ((*arg)[2] == 0) { if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } s = strNl0(*arg); } else s = strNl0((*arg) + 2); parseCmdlineDefine(s); free(s); break; case 'x': execallowed = 1; break; case 'n': S->preservelf = 1; break; case 'z': dosmode = 1; break; case 'c': case 's': if (!(*(++arg))) { usage(); exit(EXIT_FAILURE); } delete_comment(S, strNl(*arg)); break; case 'm': autoswitch = 1; break; default: ishelp = 1; } if (hasmeta && !usrmode) { usage(); exit(EXIT_FAILURE); } if (ishelp) { usage(); exit(EXIT_FAILURE); } } #ifndef WIN_NT if ((nincludedirs == 0) && !NoStdInc) { includedir[0] = my_strdup("/usr/include"); nincludedirs = 1; } #endif for (i = 0; i < nmacros; i++) { if (macros[i].define_specs == NULL ) macros[i].define_specs = CloneSpecs(S); lookupArgRefs(i); /* for macro aliasing */ } } int findCommentEnd(const char *endseq, char quote, char warn, int pos, int flags) { int i; char c; while (1) { c = getChar(pos); i = pos; if (matchEndSequence(endseq, &i)) return pos; if (c == 0) bug("Input ended while scanning a comment/string"); if (c == warn) { warn = 0; if (WarningLevel > 1) warning("possible comment/string termination problem"); } if (c == quote) pos += 2; else if ((flags & PARSE_MACROS) && (c == S->User.quotechar)) pos += 2; else pos++; } } void SkipPossibleComments(int *pos, int cmtmode, int silentonly) { int found; struct COMMENT *c; if (C->in_comment) return; do { found = 0; if (getChar(*pos) == 0) return; /* EOF */ for (c = S->comments; c != NULL ; c = c->next) if (!(c->flags[cmtmode] & FLAG_IGNORE)) if (!silentonly || (c->flags[cmtmode] == FLAG_COMMENT)) if (matchStartSequence(c->start, pos)) { *pos = findCommentEnd(c->end, c->quote, c->warn, *pos, c->flags[cmtmode]); matchEndSequence(c->end, pos); found = 1; break; } } while (found); } /* look for a possible user macro. Input : idstart = scan start idcheck = check id for long macro forms before splicing args ? cmtmode = comment mode (FLAG_META or FLAG_USER) Output : idstart/idend = macro name location sh_end/lg_end = macro form end (-1 if no match) argb/arge = argument locations for long form argc = argument count for long form id = macro id, if idcheck was set at input */ int SplicePossibleUser(int *idstart, int *idend, int *sh_end, int *lg_end, int *argb, int *arge, int *argc, int idcheck, int *id, int cmtmode) { int match, k, pos; if (!matchStartSequence(S->User.mStart, idstart)) return 0; *idend = identifierEnd(*idstart); if ((*idend) && !getChar(*idend - 1)) return 0; /* look for args or no args */ *sh_end = *idend; if (!matchEndSequence(S->User.mEnd, sh_end)) *sh_end = -1; pos = *idend; match = matchSequence(S->User.mArgS, &pos); if (idcheck) { *id = findIdent(C->buf + *idstart, *idend - *idstart); if (*id < 0) match = 0; } *lg_end = -1; if (match) { *argc = 0; while (1) { if (*argc >= MAXARGS) bug("too many macro parameters"); argb[*argc] = pos; k = 0; while (1) { /* look for mArgE, mArgSep, or comment-start */ pos = iterIdentifierEnd(pos); SkipPossibleComments(&pos, cmtmode, 0); if (getChar(pos) == 0) return (*sh_end >= 0); /* EOF */ if (strchr(S->User.stackchar, getChar(pos))) k++; if (k) { if (strchr(S->User.unstackchar, getChar(pos))) k--; } else { arge[*argc] = pos; if (matchSequence(S->User.mArgSep, &pos)) { match = 0; break; } if (matchEndSequence(S->User.mArgE, &pos)) { match = 1; break; } } pos++; /* nothing matched, go forward */ } (*argc)++; if (match) { /* no more args */ *lg_end = pos; break; } } } return ((*lg_end >= 0) || (*sh_end >= 0)); } int findMetaArgs(int start, int *p1b, int *p1e, int *p2b, int *p2e, int *endm, int *argc, int *argb, int *arge) { int pos, k; int hyp_end1, hyp_end2; /* look for mEnd or mArgS */ pos = start; if (!matchSequence(S->Meta.mArgS, &pos)) { if (!matchEndSequence(S->Meta.mEnd, &pos)) return -1; *endm = pos; return 0; } *p1b = pos; /* special syntax for #define : 1st arg is a macro call */ if ((*argc) && SplicePossibleUser(&pos, p1e, &hyp_end1, &hyp_end2, argb, arge, argc, 0, NULL, FLAG_META)) { *p1b = pos; if (hyp_end2 >= 0) pos = hyp_end2; else { pos = hyp_end1; *argc = 0; } if (!matchSequence(S->Meta.mArgSep, &pos)) { if (!matchEndSequence(S->Meta.mArgE, &pos)) bug( "#define/#defeval requires an identifier or a single macro call"); *endm = pos; return 1; } } else { *argc = 0; k = 0; while (1) { /* look for mArgE, mArgSep, or comment-start */ pos = iterIdentifierEnd(pos); SkipPossibleComments(&pos, FLAG_META, 0); if (getChar(pos) != 0 && strchr(S->Meta.stackchar, getChar(pos))) k++; if (k) { if (getChar(pos) != 0 && strchr(S->Meta.unstackchar, getChar(pos))) k--; } else { *p1e = pos; if (matchSequence(S->Meta.mArgSep, &pos)) break; if (matchEndSequence(S->Meta.mArgE, &pos)) { *endm = pos; return 1; } } if (getChar(pos) == 0) bug("unfinished macro argument"); pos++; /* nothing matched, go forward */ } } *p2b = pos; k = 0; while (1) { /* look for mArgE or comment-start */ pos = iterIdentifierEnd(pos); SkipPossibleComments(&pos, FLAG_META, 0); if (getChar(pos) != 0 && strchr(S->Meta.stackchar, getChar(pos))) k++; if (k) { if (getChar(pos) != 0 && strchr(S->Meta.unstackchar, getChar(pos))) k--; } else { *p2e = pos; if (matchEndSequence(S->Meta.mArgE, &pos)) break; } if (getChar(pos) == 0) bug("unfinished macro"); pos++; /* nothing matched, go forward */ } *endm = pos; return 2; } char *ProcessText(const char *buf, int l, int ambience) { char *s; struct INPUTCONTEXT *T; if (l == 0) { s = malloc(1); s[0] = 0; return s; } s = malloc(l + 2); s[0] = '\n'; memcpy(s + 1, buf, l); s[l + 1] = 0; T = C; C = malloc(sizeof *C); C->out = malloc(sizeof *(C->out)); C->in = NULL; C->argc = T->argc; C->argv = T->argv; C->filename = T->filename; C->out->buf = malloc(80); C->out->len = 0; C->out->bufsize = 80; C->out->f = NULL; C->lineno = T->lineno; C->bufsize = l + 2; C->len = l + 1; C->buf = C->malloced_buf = s; C->eof = 0; C->namedargs = T->namedargs; C->in_comment = T->in_comment; C->ambience = ambience; C->may_have_args = T->may_have_args; ProcessContext(); outchar(0); /* note that outchar works with the half-destroyed context ! */ s = C->out->buf; free(C->out); free(C); C = T; return s; } int SpliceInfix(const char *buf, int pos1, int pos2, char *sep, int *spl1, int *spl2) { int pos, numpar, l; const char *p; numpar = 0; l = strlen(sep); for (pos = pos2 - 1, p = buf + pos; pos >= pos1; pos--, p--) { if (*p == ')') numpar++; if (*p == '(') numpar--; if (numpar < 0) return 0; if ((numpar == 0) && (pos2 - pos >= l) && !strncmp(p, sep, l)) { *spl1 = pos; *spl2 = pos + l; return 1; } } return 0; } int DoArithmEval(char *buf, int pos1, int pos2, int *result) { int spl1, spl2, result1, result2, l; char c, *p; while ((pos1 < pos2) && isWhite(buf[pos1])) pos1++; while ((pos1 < pos2) && isWhite(buf[pos2 - 1])) pos2--; if (pos1 == pos2) return 0; /* look for C operators starting with lowest precedence */ if (SpliceInfix(buf, pos1, pos2, "||", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; *result = result1 || result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "&&", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; *result = result1 && result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "|", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; *result = result1 | result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "^", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; *result = result1 ^ result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "&", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; *result = result1 & result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "!=", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) { /* revert to string comparison */ while ((pos1 < spl1) && isWhite(buf[spl1 - 1])) spl1--; while ((pos2 > spl2) && isWhite(buf[spl2])) spl2++; if (spl1 - pos1 != pos2 - spl2) *result = 1; else *result = (strncmp(buf + pos1, buf + spl2, spl1 - pos1) != 0); } else *result = (result1 != result2); return 1; } if (SpliceInfix(buf, pos1, pos2, "==", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) { /* revert to string comparison */ while ((pos1 < spl1) && isWhite(buf[spl1 - 1])) spl1--; while ((pos2 > spl2) && isWhite(buf[spl2])) spl2++; if (spl1 - pos1 != pos2 - spl2) *result = 0; else *result = (strncmp(buf + pos1, buf + spl2, spl1 - pos1) == 0); } else *result = (result1 == result2); return 1; } if (SpliceInfix(buf, pos1, pos2, "=~", &spl1, &spl2)) { #if ! HAVE_FNMATCH_H bug("globbing support has not been compiled in"); #endif if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) { char *str1, *str2; /* revert to string comparison */ while ((pos1 < spl1) && isWhite(buf[spl1 - 1])) spl1--; while ((pos2 > spl2) && isWhite(buf[spl2])) spl2++; str1 = strdup(buf + pos1); str1[spl1 - pos1] = '\0'; str2 = strdup(buf + spl2); str2[pos2 - spl2] = '\0'; *result = (fnmatch(str2, str1, 0) == 0); free(str1); free(str2); } else *result = (result1 == result2); return 1; } if (SpliceInfix(buf, pos1, pos2, ">=", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) { /* revert to string comparison */ while ((pos1 < spl1) && isWhite(buf[spl1 - 1])) spl1--; while ((pos2 > spl2) && isWhite(buf[spl2])) spl2++; l = spl1 - pos1; if (l > pos2 - spl2) l = pos2 - spl2; result1 = strncmp(buf + pos1, buf + spl2, l); *result = (result1 > 0) || ((result1 == 0) && (spl1 - pos1 >= pos2 - spl2)); } else *result = (result1 >= result2); return 1; } if (SpliceInfix(buf, pos1, pos2, ">", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) { /* revert to string comparison */ while ((pos1 < spl1) && isWhite(buf[spl1 - 1])) spl1--; while ((pos2 > spl2) && isWhite(buf[spl2])) spl2++; l = spl1 - pos1; if (l > pos2 - spl2) l = pos2 - spl2; result1 = strncmp(buf + pos1, buf + spl2, l); *result = (result1 > 0) || ((result1 == 0) && (spl1 - pos1 > pos2 - spl2)); } else *result = (result1 > result2); return 1; } if (SpliceInfix(buf, pos1, pos2, "<=", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) { /* revert to string comparison */ while ((pos1 < spl1) && isWhite(buf[spl1 - 1])) spl1--; while ((pos2 > spl2) && isWhite(buf[spl2])) spl2++; l = spl1 - pos1; if (l > pos2 - spl2) l = pos2 - spl2; result1 = strncmp(buf + pos1, buf + spl2, l); *result = (result1 < 0) || ((result1 == 0) && (spl1 - pos1 <= pos2 - spl2)); } else *result = (result1 <= result2); return 1; } if (SpliceInfix(buf, pos1, pos2, "<", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) { /* revert to string comparison */ while ((pos1 < spl1) && isWhite(buf[spl1 - 1])) spl1--; while ((pos2 > spl2) && isWhite(buf[spl2])) spl2++; l = spl1 - pos1; if (l > pos2 - spl2) l = pos2 - spl2; result1 = strncmp(buf + pos1, buf + spl2, l); *result = (result1 < 0) || ((result1 == 0) && (spl1 - pos1 < pos2 - spl2)); } else *result = (result1 < result2); return 1; } if (SpliceInfix(buf, pos1, pos2, "+", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; *result = result1 + result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "-", &spl1, &spl2)) if (spl1 != pos1) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; *result = result1 - result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "*", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; *result = result1 * result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "/", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; if (result2 == 0) bug("Division by zero in expression"); *result = result1 / result2; return 1; } if (SpliceInfix(buf, pos1, pos2, "%", &spl1, &spl2)) { if (!DoArithmEval(buf, pos1, spl1, &result1) || !DoArithmEval(buf, spl2, pos2, &result2)) return 0; if (result2 == 0) bug("Division by zero in expression"); *result = result1 % result2; return 1; } if (buf[pos1] == '~') { if (!DoArithmEval(buf, pos1 + 1, pos2, &result1)) return 0; *result = ~result1; return 1; } if (buf[pos1] == '!') { if (!DoArithmEval(buf, pos1 + 1, pos2, &result1)) return 0; *result = !result1; return 1; } if (buf[pos1] == '-') { if (!DoArithmEval(buf, pos1 + 1, pos2, &result1)) return 0; *result = -result1; return 1; } /* add the length() builtin to measure the length of the macro expansion */ if (strncmp(buf + pos1, "length(", strlen("length(")) == 0) { if (buf[pos2 - 1] != ')') return 0; *result = pos2 - pos1 - strlen("length()"); return 1; } if (buf[pos1] == '(') { if (buf[pos2 - 1] != ')') return 0; return DoArithmEval(buf, pos1 + 1, pos2 - 1, result); } c = buf[pos2]; buf[pos2] = 0; *result = (int) strtol(buf + pos1, &p, 0); buf[pos2] = c; return (p == buf + pos2); } void delete_macro(int i) { int j; nmacros--; free(macros[i].username); free(macros[i].macrotext); if (macros[i].argnames != NULL ) { for (j = 0; j < macros[i].nnamedargs; j++) free(macros[i].argnames[j]); free(macros[i].argnames); macros[i].argnames = NULL; } FreeComments(macros[i].define_specs); free(macros[i].define_specs); memcpy(macros + i, macros + nmacros, sizeof(struct MACRO)); } char *ArithmEval(int pos1, int pos2) { char *s, *t; int i; /* first define the defined(...) operator */ i = findIdent("defined", strlen("defined")); if (i >= 0) warning("the defined(...) macro is already defined"); else { newmacro("defined", strlen("defined"), 1); macros[nmacros].macrolen = 0; macros[nmacros].macrotext = malloc(1); macros[nmacros].macrotext[0] = 0; macros[nmacros].nnamedargs = -2; /* trademark of the defined(...) macro */ nmacros++; } /* process the text in a usual way */ s = ProcessText(C->buf + pos1, pos2 - pos1, FLAG_META); /* undefine the defined(...) operator */ if (i < 0) { i = findIdent("defined", strlen("defined")); if ((i < 0) || (macros[i].nnamedargs != -2)) warning("the defined(...) macro was redefined in expression"); else delete_macro(i); } if (!DoArithmEval(s, 0, strlen(s), &i)) return s; /* couldn't compute */ t = malloc(MAX_GPP_NUM_SIZE); sprintf(t, "%d", i); free(s); return t; } int comment_or_white(int start, int end, int cmtmode) { char c; while (start < end) { SkipPossibleComments(&start, cmtmode, 1); if (start < end) { c = getChar(start++); if ((c != ' ') && (c != '\n') && (c != '\t')) return 0; } } return 1; } char *remove_comments(int start, int end, int cmtmode) { char *s, *t; t = s = malloc(end - start + 1); while (start < end) { SkipPossibleComments(&start, cmtmode, 1); if (start < end) { *t = getChar(start++); if ((*t == S->User.quotechar) && (start < end)) { *(++t) = getChar(start++); } t++; } } *t = 0; return s; } void SetStandardMode(struct SPECS *P, const char *opt) { P->op_set = DefaultOp; P->ext_op_set = DefaultExtOp; P->id_set = DefaultId; FreeComments(P); if (!strcmp(opt, "C") || !strcmp(opt, "cpp")) { P->User = KUser; P->Meta = KMeta; P->preservelf = 1; add_comment(P, "ccc", my_strdup("/*"), my_strdup("*/"), 0, 0); add_comment(P, "ccc", my_strdup("//"), my_strdup("\n"), 0, 0); add_comment(P, "ccc", my_strdup("\\\n"), my_strdup(""), 0, 0); add_comment(P, "sss", my_strdup("\""), my_strdup("\""), '\\', '\n'); add_comment(P, "sss", my_strdup("'"), my_strdup("'"), '\\', '\n'); } else if (!strcmp(opt, "TeX") || !strcmp(opt, "tex")) { P->User = Tex; P->Meta = Tex; P->preservelf = 0; } else if (!strcmp(opt, "HTML") || !strcmp(opt, "html")) { P->User = Html; P->Meta = Html; P->preservelf = 0; } else if (!strcmp(opt, "XHTML") || !strcmp(opt, "xhtml")) { P->User = XHtml; P->Meta = XHtml; P->preservelf = 0; } else if (!strcmp(opt, "default")) { P->User = CUser; P->Meta = CMeta; P->preservelf = 0; } else if (!strcmp(opt, "Prolog") || !strcmp(opt, "prolog")) { P->User = KUser; P->Meta = KMeta; P->preservelf = 1; P->op_set = PrologOp; add_comment(P, "css", my_strdup("\213/*"), my_strdup("*/"), 0, 0); /* \!o */ add_comment(P, "cii", my_strdup("\\\n"), my_strdup(""), 0, 0); add_comment(P, "css", my_strdup("%"), my_strdup("\n"), 0, 0); add_comment(P, "sss", my_strdup("\""), my_strdup("\""), 0, '\n'); add_comment(P, "sss", my_strdup("\207'"), my_strdup("'"), 0, '\n'); /* \!# */ } else bug("unknown standard mode"); } void ProcessModeCommand(int p1start, int p1end, int p2start, int p2end) { struct SPECS *P; char *s, *p, *opt; int nargs, check_isdelim; char *args[10]; /* can't have more than 10 arguments */ whiteout(&p1start, &p1end); if ((p1start == p1end) || (identifierEnd(p1start) != p1end)) bug("invalid #mode syntax"); if (p2start < 0) s = my_strdup(""); else s = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META); /* argument parsing */ p = s; opt = NULL; while (isWhite(*p)) p++; if ((*p != '"') && (*p != 0)) { opt = p; while ((*p != 0) && !isWhite(*p)) p++; if (*p != 0) { *(p++) = 0; while (isWhite(*p)) p++; } } nargs = 0; check_isdelim = !idequal(C->buf + p1start, p1end - p1start, "charset"); while (*p != 0) { if (nargs == 10) bug("too many arguments in #mode command"); if (*(p++) != '"') bug("syntax error in #mode command (missing \" or trailing data)"); args[nargs++] = p; p = strNl2(p, check_isdelim); while (isWhite(*p)) p++; } if (idequal(C->buf + p1start, p1end - p1start, "quote")) { if (opt || (nargs > 1)) bug("syntax error in #mode quote command"); if (nargs == 0) args[0] = ""; S->stack_next->User.quotechar = args[0][0]; } else if (idequal(C->buf + p1start, p1end - p1start, "comment")) { if ((nargs < 2) || (nargs > 4)) bug("syntax error in #mode comment command"); if (!opt) opt = "ccc"; if (nargs < 3) args[2] = ""; if (nargs < 4) args[3] = ""; add_comment(S->stack_next, opt, my_strdup(args[0]), my_strdup(args[1]), args[2][0], args[3][0]); } else if (idequal(C->buf + p1start, p1end - p1start, "string")) { if ((nargs < 2) || (nargs > 4)) bug("syntax error in #mode string command"); if (!opt) opt = "sss"; if (nargs < 3) args[2] = ""; if (nargs < 4) args[3] = ""; add_comment(S->stack_next, opt, my_strdup(args[0]), my_strdup(args[1]), args[2][0], args[3][0]); } else if (idequal(C->buf + p1start, p1end - p1start, "save") || idequal(C->buf + p1start, p1end - p1start, "push")) { if ((opt != NULL )||nargs) bug("too many arguments to #mode save"); P = CloneSpecs(S->stack_next); P->stack_next = S->stack_next; S->stack_next = P; } else if (idequal(C->buf + p1start, p1end - p1start, "restore") || idequal(C->buf + p1start, p1end - p1start, "pop")) { if ((opt != NULL )||nargs) bug("too many arguments to #mode restore"); P = S->stack_next->stack_next; if (P == NULL ) bug("#mode restore without #mode save"); FreeComments(S->stack_next); free(S->stack_next); S->stack_next = P; } else if (idequal(C->buf + p1start, p1end - p1start, "standard")) { if ((opt == NULL )||nargs) bug("syntax error in #mode standard"); SetStandardMode(S->stack_next, opt); } else if (idequal(C->buf + p1start, p1end - p1start, "user")) { if ((opt != NULL )||(nargs!=9))bug("#mode user requires 9 arguments"); S->stack_next->User.mStart=my_strdup(args[0]); S->stack_next->User.mEnd=my_strdup(args[1]); S->stack_next->User.mArgS=my_strdup(args[2]); S->stack_next->User.mArgSep=my_strdup(args[3]); S->stack_next->User.mArgE=my_strdup(args[4]); S->stack_next->User.stackchar=my_strdup(args[5]); S->stack_next->User.unstackchar=my_strdup(args[6]); S->stack_next->User.mArgRef=my_strdup(args[7]); S->stack_next->User.quotechar=args[8][0]; } else if (idequal(C->buf+p1start,p1end-p1start,"meta")) { if ((opt!=NULL)&&!nargs&&!strcmp(opt,"user")) S->stack_next->Meta=S->stack_next->User; else { if ((opt!=NULL)||(nargs!=7)) bug("#mode meta requires 7 arguments"); S->stack_next->Meta.mStart=my_strdup(args[0]); S->stack_next->Meta.mEnd=my_strdup(args[1]); S->stack_next->Meta.mArgS=my_strdup(args[2]); S->stack_next->Meta.mArgSep=my_strdup(args[3]); S->stack_next->Meta.mArgE=my_strdup(args[4]); S->stack_next->Meta.stackchar=my_strdup(args[5]); S->stack_next->Meta.unstackchar=my_strdup(args[6]); } } else if (idequal(C->buf+p1start,p1end-p1start,"preservelf")) { if ((opt==NULL)||nargs) bug("syntax error in #mode preservelf"); if (!strcmp(opt,"1")||!my_strcasecmp(opt,"on")) S->stack_next->preservelf=1; else if (!strcmp(opt,"0")||!my_strcasecmp(opt,"off")) S->stack_next->preservelf=0; else bug("#mode preservelf requires on/off argument"); } else if (idequal(C->buf+p1start,p1end-p1start,"nocomment") ||idequal(C->buf+p1start,p1end-p1start,"nostring")) { if ((opt!=NULL)||(nargs>1)) bug("syntax error in #mode nocomment/nostring"); if (nargs==0) FreeComments(S->stack_next); else delete_comment(S->stack_next,my_strdup(args[0])); } else if (idequal(C->buf+p1start,p1end-p1start,"charset")) { if ((opt==NULL)||(nargs!=1)) bug("syntax error in #mode charset"); if (!my_strcasecmp(opt,"op")) S->stack_next->op_set=MakeCharsetSubset((unsigned char *)args[0]); else if (!my_strcasecmp(opt,"par")) S->stack_next->ext_op_set=MakeCharsetSubset((unsigned char *)args[0]); else if (!my_strcasecmp(opt,"id")) S->stack_next->id_set=MakeCharsetSubset((unsigned char *)args[0]); else bug("unknown charset subset name in #mode charset"); } else bug("unrecognized #mode command"); free(s); } static void DoInclude(char *file_name, int ignore_nonexistent) { struct INPUTCONTEXT *N; char *incfile_name = NULL; FILE *f = NULL; int j; int len = strlen(file_name); /* if absolute path name is specified */ if (file_name[0] == SLASH #ifdef WIN_NT || (isalpha(file_name[0]) && file_name[1]==':') #endif ) f = fopen(file_name, "r"); else /* search current dir, if this search isn't turned off */ if (!NoCurIncFirst) { f = openInCurrentDir(file_name); } for (j = 0; (f == NULL )&&(jin = f; C->argc = 0; C->argv = NULL; C->filename = file_name; C->out = N->out; C->lineno = 1; C->bufsize = 80; C->len = 0; C->buf = C->malloced_buf = malloc(C->bufsize); C->eof = 0; C->namedargs = NULL; C->in_comment = 0; C->ambience = FLAG_TEXT; C->may_have_args = 0; PushSpecs(S); if (autoswitch) { if (!strcmp(file_name + strlen(file_name) - 2, ".h") || !strcmp(file_name + strlen(file_name) - 2, ".c")) SetStandardMode(S, "C"); } /* Include marker before the included contents */ write_include_marker(N->out->f, 1, C->filename, "1"); ProcessContext(); /* Include marker after the included contents */ write_include_marker(N->out->f, N->lineno, N->filename, "2"); /* Need to leave the blank line in lieu of #include, like cpp does */ replace_directive_with_blank_line(N->out->f); free(C); PopSpecs(); C = N; } int ParsePossibleMeta(void) { int cklen, nameend; int id, expparams, nparam, i, j; int p1start, p1end, p2start, p2end, macend; int argc, argb[MAXARGS], arge[MAXARGS]; char *tmpbuf; cklen = 1; if (!matchStartSequence(S->Meta.mStart, &cklen)) return -1; nameend = identifierEnd(cklen); if (nameend && !getChar(nameend - 1)) return -1; argc = 0; /* for #define with named args */ if (idequal(C->buf + cklen, nameend - cklen, "define")) /* check identifier */ { id = 1; expparams = 2; argc = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "undef")) { id = 2; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "ifdef")) { id = 3; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "ifndef")) { id = 4; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "else")) { id = 5; expparams = 0; } else if (idequal(C->buf + cklen, nameend - cklen, "endif")) { id = 6; expparams = 0; } else if (idequal(C->buf + cklen, nameend - cklen, "include")) { id = 7; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "exec")) { id = 8; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "defeval")) { id = 9; expparams = 2; argc = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "ifeq")) { id = 10; expparams = 2; } else if (idequal(C->buf + cklen, nameend - cklen, "ifneq")) { id = 11; expparams = 2; } else if (idequal(C->buf + cklen, nameend - cklen, "eval")) { id = 12; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "if")) { id = 13; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "mode")) { id = 14; expparams = 2; } else if (idequal(C->buf + cklen, nameend - cklen, "line")) { id = 15; expparams = 0; } else if (idequal(C->buf + cklen, nameend - cklen, "file")) { id = 16; expparams = 0; } else if (idequal(C->buf + cklen, nameend - cklen, "elif")) { id = 17; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "error")) { id = 18; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "warning")) { id = 19; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "date")) { id = 20; expparams = 1; } else if (idequal(C->buf + cklen, nameend - cklen, "sinclude")) { id = 21; expparams = 1; } else return -1; /* #MODE magic : define "..." to be C-style strings */ if (id == 14) { PushSpecs(S); S->preservelf = 1; delete_comment(S, my_strdup("\"")); add_comment(S, "sss", my_strdup("\""), my_strdup("\""), '\\', '\n'); } nparam = findMetaArgs(nameend, &p1start, &p1end, &p2start, &p2end, &macend, &argc, argb, arge); if (nparam == -1) return -1; if ((nparam == 2) && isWhitesep(S->Meta.mArgSep)) if (comment_or_white(p2start, p2end, FLAG_META)) nparam = 1; if ((nparam == 1) && isWhitesep(S->Meta.mArgS)) if (comment_or_white(p1start, p1end, FLAG_META)) nparam = 0; if (expparams && !nparam) bug("Missing argument in meta-macro"); switch (id) { case 1: /* DEFINE */ if (!commented[iflevel]) { whiteout(&p1start, &p1end); /* recall comments are not allowed here */ if ((p1start == p1end) || (identifierEnd(p1start) != p1end)) bug("#define requires an identifier (A-Z,a-z,0-9,_ only)"); /* buf starts 1 char before the macro */ i = findIdent(C->buf + p1start, p1end - p1start); if (i >= 0) delete_macro(i); newmacro(C->buf + p1start, p1end - p1start, 1); if (nparam == 1) { p2end = p2start = p1end; } replace_definition_with_blank_lines(C->buf + 1, C->buf + p2end, S->preservelf); macros[nmacros].macrotext = remove_comments(p2start, p2end, FLAG_META); macros[nmacros].macrolen = strlen(macros[nmacros].macrotext); macros[nmacros].defined_in_comment = C->in_comment; if (argc) { for (j = 0; j < argc; j++) whiteout(argb + j, arge + j); /* define with one empty argument */ if ((argc == 1) && (arge[0] == argb[0])) argc = 0; macros[nmacros].argnames = malloc((argc + 1) * sizeof(char *)); macros[nmacros].argnames[argc] = NULL; } macros[nmacros].nnamedargs = argc; for (j = 0; j < argc; j++) { if ((argb[j] == arge[j]) || (identifierEnd(argb[j]) != arge[j])) bug( "#define with named args needs identifiers as arg names"); macros[nmacros].argnames[j] = malloc(arge[j] - argb[j] + 1); memcpy(macros[nmacros].argnames[j], C->buf + argb[j], arge[j] - argb[j]); macros[nmacros].argnames[j][arge[j] - argb[j]] = 0; } lookupArgRefs(nmacros++); } else replace_directive_with_blank_line(C->out->f); break; case 2: /* UNDEF */ replace_directive_with_blank_line(C->out->f); if (!commented[iflevel]) { if (nparam == 2 && WarningLevel > 0) warning("Extra argument to #undef ignored"); whiteout(&p1start, &p1end); if ((p1start == p1end) || (identifierEnd(p1start) != p1end)) bug("#undef requires an identifier (A-Z,a-z,0-9,_ only)"); i = findIdent(C->buf + p1start, p1end - p1start); if (i >= 0) delete_macro(i); } break; case 3: /* IFDEF */ replace_directive_with_blank_line(C->out->f); iflevel++; if (iflevel == STACKDEPTH) bug("Too many nested #ifdefs"); commented[iflevel] = commented[iflevel - 1]; if (!commented[iflevel]) { if (nparam == 2 && WarningLevel > 0) warning("Extra argument to #ifdef ignored"); whiteout(&p1start, &p1end); if ((p1start == p1end) || (identifierEnd(p1start) != p1end)) bug("#ifdef requires an identifier (A-Z,a-z,0-9,_ only)"); i = findIdent(C->buf + p1start, p1end - p1start); commented[iflevel] = (i == -1); } break; case 4: /* IFNDEF */ replace_directive_with_blank_line(C->out->f); iflevel++; if (iflevel == STACKDEPTH) bug("Too many nested #ifdefs"); commented[iflevel] = commented[iflevel - 1]; if (!commented[iflevel]) { if (nparam == 2 && WarningLevel > 0) warning("Extra argument to #ifndef ignored"); whiteout(&p1start, &p1end); if ((p1start == p1end) || (identifierEnd(p1start) != p1end)) bug("#ifndef requires an identifier (A-Z,a-z,0-9,_ only)"); i = findIdent(C->buf + p1start, p1end - p1start); commented[iflevel] = (i != -1); } break; case 5: /* ELSE */ replace_directive_with_blank_line(C->out->f); if (!commented[iflevel] && (nparam > 0) && WarningLevel > 0) warning("Extra argument to #else ignored"); if (iflevel == 0) bug("#else without #if"); if (!commented[iflevel - 1] && commented[iflevel] != 2) commented[iflevel] = !commented[iflevel]; break; case 6: /* ENDIF */ replace_directive_with_blank_line(C->out->f); if (!commented[iflevel] && (nparam > 0) && WarningLevel > 0) warning("Extra argument to #endif ignored"); if (iflevel == 0) bug("#endif without #if"); iflevel--; break; case 7: /* INCLUDE */ if (!commented[iflevel]) { char *incfile_name; if (nparam == 2 && WarningLevel > 0) warning("Extra argument to #include ignored"); if (!whiteout(&p1start, &p1end)) bug("Missing file name in #include"); /* user may put "" or <> */ if (((getChar(p1start) == '\"') && (getChar(p1end - 1) == '\"')) || ((getChar(p1start) == '<') && (getChar(p1end - 1) == '>'))) { p1start++; p1end--; } if (p1start >= p1end) bug("Missing file name in #include"); incfile_name = malloc(p1end - p1start + 1); /* extract the orig include filename */ for (i = 0; i < p1end - p1start; i++) incfile_name[i] = getChar(p1start + i); incfile_name[p1end - p1start] = 0; DoInclude(incfile_name, 0); } else replace_directive_with_blank_line(C->out->f); break; case 8: /* EXEC */ if (!commented[iflevel]) { if (!execallowed) warning( "Not allowed to #exec. Command output will be left blank"); else { char *s, *t; int c; FILE *f; s = ProcessText(C->buf + p1start, p1end - p1start, FLAG_META); if (nparam == 2) { t = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META); i = strlen(s); s = realloc(s, i + strlen(t) + 2); s[i] = ' '; strcpy(s + i + 1, t); free(t); } f = popen(s, "r"); free(s); if (f == NULL ) warning("Cannot #exec. Command not found(?)"); else { while ((c = fgetc(f)) != EOF) outchar((char) c); pclose(f); } } } break; case 9: /* DEFEVAL */ if (!commented[iflevel]) { whiteout(&p1start, &p1end); if ((p1start == p1end) || (identifierEnd(p1start) != p1end)) bug("#defeval requires an identifier (A-Z,a-z,0-9,_ only)"); tmpbuf = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META); i = findIdent(C->buf + p1start, p1end - p1start); if (i >= 0) delete_macro(i); newmacro(C->buf + p1start, p1end - p1start, 1); if (nparam == 1) { p2end = p2start = p1end; } replace_definition_with_blank_lines(C->buf + 1, C->buf + p2end, S->preservelf); macros[nmacros].macrotext = tmpbuf; macros[nmacros].macrolen = strlen(macros[nmacros].macrotext); macros[nmacros].defined_in_comment = C->in_comment; if (argc) { for (j = 0; j < argc; j++) whiteout(argb + j, arge + j); /* define with one empty argument */ if ((argc == 1) && (arge[0] == argb[0])) argc = 0; macros[nmacros].argnames = malloc((argc + 1) * sizeof(char *)); macros[nmacros].argnames[argc] = NULL; } macros[nmacros].nnamedargs = argc; for (j = 0; j < argc; j++) { if ((argb[j] == arge[j]) || (identifierEnd(argb[j]) != arge[j])) bug( "#defeval with named args needs identifiers as arg names"); macros[nmacros].argnames[j] = malloc(arge[j] - argb[j] + 1); memcpy(macros[nmacros].argnames[j], C->buf + argb[j], arge[j] - argb[j]); macros[nmacros].argnames[j][arge[j] - argb[j]] = 0; } lookupArgRefs(nmacros++); } else replace_directive_with_blank_line(C->out->f); break; case 10: /* IFEQ */ replace_directive_with_blank_line(C->out->f); iflevel++; if (iflevel == STACKDEPTH) bug("Too many nested #ifeqs"); commented[iflevel] = commented[iflevel - 1]; if (!commented[iflevel]) { char *s, *t; if (nparam != 2) bug("#ifeq requires two arguments"); s = ProcessText(C->buf + p1start, p1end - p1start, FLAG_META); t = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META); commented[iflevel] = (nowhite_strcmp(s, t) != 0); free(s); free(t); } break; case 11: /* IFNEQ */ replace_directive_with_blank_line(C->out->f); iflevel++; if (iflevel == STACKDEPTH) bug("Too many nested #ifeqs"); commented[iflevel] = commented[iflevel - 1]; if (!commented[iflevel]) { char *s, *t; if (nparam != 2) bug("#ifneq requires two arguments"); s = ProcessText(C->buf + p1start, p1end - p1start, FLAG_META); t = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META); commented[iflevel] = (nowhite_strcmp(s, t) == 0); free(s); free(t); } break; case 12: /* EVAL */ if (!commented[iflevel]) { char *s, *t; if (nparam == 2) p1end = p2end; /* we really want it all ! */ s = ArithmEval(p1start, p1end); for (t = s; *t; t++) outchar(*t); free(s); } break; case 13: /* IF */ replace_directive_with_blank_line(C->out->f); iflevel++; if (iflevel == STACKDEPTH) bug("Too many nested #ifs"); commented[iflevel] = commented[iflevel - 1]; if (!commented[iflevel]) { char *s; if (nparam == 2) p1end = p2end; /* we really want it all ! */ s = ArithmEval(p1start, p1end); commented[iflevel] = ((s[0] == '0') && (s[1] == 0)); free(s); } break; case 14: /* MODE */ replace_directive_with_blank_line(C->out->f); if (nparam == 1) p2start = -1; if (!commented[iflevel]) ProcessModeCommand(p1start, p1end, p2start, p2end); PopSpecs(); break; case 15: { /* LINE */ char buf[MAX_GPP_NUM_SIZE]; sprintf(buf, "%d", C->lineno); replace_directive_with_blank_line(C->out->f); sendout(buf, strlen(buf), 0); } break; case 16: /* FILE */ replace_directive_with_blank_line(C->out->f); sendout(C->filename, strlen(C->filename), 0); break; case 17: /* ELIF */ replace_directive_with_blank_line(C->out->f); if (iflevel == 0) bug("#elif without #if"); if (!commented[iflevel - 1]) { if (commented[iflevel] != 1) commented[iflevel] = 2; else { char *s; commented[iflevel] = 0; if (nparam == 2) p1end = p2end; /* we really want it all ! */ s = ArithmEval(p1start, p1end); commented[iflevel] = ((s[0] == '0') && (s[1] == 0)); free(s); } } break; case 18: /* ERROR */ replace_directive_with_blank_line(C->out->f); if (!commented[iflevel]) bug( ProcessText(C->buf + p1start, (nparam == 2 ? p2end : p1end) - p1start, FLAG_META)); break; case 19: /* WARNING */ replace_directive_with_blank_line(C->out->f); if (!commented[iflevel]) { char *s; s = ProcessText(C->buf + p1start, (nparam == 2 ? p2end : p1end) - p1start, FLAG_META); warning(s); free(s); } break; case 20: { /* DATE */ char buf[MAX_GPP_DATE_SIZE]; char *fmt; time_t now = time(NULL ); fmt = ProcessText(C->buf + p1start, (nparam == 2 ? p2end : p1end) - p1start, FLAG_META); if (!strftime(buf, MAX_GPP_DATE_SIZE, fmt, localtime(&now))) bug("date buffer exceeded"); replace_directive_with_blank_line(C->out->f); sendout(buf, strlen(buf), 0); free(fmt); } break; case 21: /* SINCLUDE */ if (!commented[iflevel]) { char *incfile_name; if (nparam == 2 && WarningLevel > 0) warning("Extra argument to #sinclude ignored"); if (!whiteout(&p1start, &p1end)) bug("Missing file name in #sinclude"); /* user may put "" or <> */ if (((getChar(p1start) == '\"') && (getChar(p1end - 1) == '\"')) || ((getChar(p1start) == '<') && (getChar(p1end - 1) == '>'))) { p1start++; p1end--; } if (p1start >= p1end) bug("Missing file name in #sinclude"); incfile_name = malloc(p1end - p1start + 1); /* extract the orig include filename */ for (i = 0; i < p1end - p1start; i++) incfile_name[i] = getChar(p1start + i); incfile_name[p1end - p1start] = 0; DoInclude(incfile_name, 1); } else replace_directive_with_blank_line(C->out->f); break; default: bug("Internal meta-macro identification error"); } shiftIn(macend); return 0; } int ParsePossibleUser(void) { int idstart, idend, sh_end, lg_end, macend; int argc, id, i, l; char *argv[MAXARGS]; int argb[MAXARGS], arge[MAXARGS]; struct INPUTCONTEXT *T; idstart = 1; id = 0; if (!SplicePossibleUser(&idstart, &idend, &sh_end, &lg_end, argb, arge, &argc, 1, &id, FLAG_USER)) return -1; if ((sh_end >= 0) && (C->namedargs != NULL )) { i = findNamedArg(C->buf + idstart, idend - idstart); if (i >= 0) { if (i < C->argc) sendout(C->argv[i], strlen(C->argv[i]), 0); shiftIn(sh_end); return 0; } } if (id < 0) return -1; if (lg_end >= 0) macend = lg_end; else { macend = sh_end; argc = 0; } if (macros[id].nnamedargs == -2) { /* defined(...) macro for arithmetic */ char *s, *t; if (argc != 1) return -1; s = remove_comments(argb[0], arge[0], FLAG_USER); t = s + strlen(s) - 1; if (*s != 0) while ((t != s) && isWhite(*t)) *(t--) = 0; t = s; while (isWhite(*t)) t++; if (findIdent(t, strlen(t)) >= 0) outchar('1'); else outchar('0'); free(s); shiftIn(macend); return 0; } if (!macros[id].macrotext[0]) { /* the empty macro */ shiftIn(macend); return 0; } for (i = 0; i < argc; i++) argv[i] = ProcessText(C->buf + argb[i], arge[i] - argb[i], FLAG_USER); /* process macro text */ T = C; C = malloc(sizeof *C); C->out = T->out; C->in = NULL; C->argc = argc; C->argv = argv; C->filename = T->filename; C->lineno = T->lineno; C->may_have_args = 1; if ((macros[id].nnamedargs == -1) && (lg_end >= 0) && (macros[id].define_specs->User.mEnd[0] == 0)) { /* build an aliased macro call */ l = strlen(macros[id].macrotext) + 2 + strlen(macros[id].define_specs->User.mArgS) + strlen(macros[id].define_specs->User.mArgE) + (argc - 1) * strlen(macros[id].define_specs->User.mArgSep); for (i = 0; i < argc; i++) l += strlen(argv[i]); C->buf = C->malloced_buf = malloc(l); l = strlen(macros[id].macrotext) + 1; C->buf[0] = '\n'; strcpy(C->buf + 1, macros[id].macrotext); while ((l > 1) && isWhite(C->buf[l - 1])) l--; strcpy(C->buf + l, macros[id].define_specs->User.mArgS); for (i = 0; i < argc; i++) { if (i > 0) strcat(C->buf, macros[id].define_specs->User.mArgSep); strcat(C->buf, argv[i]); } strcat(C->buf, macros[id].define_specs->User.mArgE); C->may_have_args = 0; } else { C->buf = C->malloced_buf = malloc(strlen(macros[id].macrotext) + 2); C->buf[0] = '\n'; strcpy(C->buf + 1, macros[id].macrotext); } C->len = strlen(C->buf); C->bufsize = C->len + 1; C->eof = 0; C->namedargs = macros[id].argnames; C->in_comment = macros[id].defined_in_comment; C->ambience = FLAG_META; PushSpecs(macros[id].define_specs); ProcessContext(); PopSpecs(); free(C); C = T; for (i = 0; i < argc; i++) free(argv[i]); shiftIn(macend); return 0; } void ParseText(void) { int l, cs, ce; char c, *s; struct COMMENT *p; if (++parselevel == STACKDEPTH) bug("Stack depth exceeded during parse"); /* look for comments first */ if (!C->in_comment) { cs = 1; for (p = S->comments; p != NULL ; p = p->next) if (!(p->flags[C->ambience] & FLAG_IGNORE)) if (matchStartSequence(p->start, &cs)) { l = ce = findCommentEnd(p->end, p->quote, p->warn, cs, p->flags[C->ambience]); matchEndSequence(p->end, &l); if (p->flags[C->ambience] & OUTPUT_DELIM) sendout(C->buf + 1, cs - 1, 0); if (!(p->flags[C->ambience] & OUTPUT_TEXT)) replace_definition_with_blank_lines(C->buf + 1, C->buf + ce - 1, 0); if (p->flags[C->ambience] & PARSE_MACROS) { C->in_comment = 1; s = ProcessText(C->buf + cs, ce - cs, C->ambience); if (p->flags[C->ambience] & OUTPUT_TEXT) sendout(s, strlen(s), 0); C->in_comment = 0; free(s); } else if (p->flags[C->ambience] & OUTPUT_TEXT) sendout(C->buf + cs, ce - cs, 0); if (p->flags[C->ambience] & OUTPUT_DELIM) sendout(C->buf + ce, l - ce, 0); shiftIn(l); parselevel--; return; } } if (ParsePossibleMeta() >= 0) { parselevel--; return; } if (ParsePossibleUser() >= 0) { parselevel--; return; } l = 1; /* If matching numbered macro argument and inside a macro */ if (matchSequence(S->User.mArgRef, &l) && C->may_have_args) { /* Process macro arguments referenced as #1,#2,... */ c = getChar(l); if ((c >= '1') && (c <= '9')) { c = c - '1'; if (c < C->argc) sendout(C->argv[(int) c], strlen(C->argv[(int) c]), 0); shiftIn(l + 1); parselevel--; return; } } l = identifierEnd(1); if (l == 1) l = 2; sendout(C->buf + 1, l - 1, 1); shiftIn(l); parselevel--; } void ProcessContext(void) { if (C->len == 0) { C->buf[0] = '\n'; C->len++; } while (!C->eof) ParseText(); if (C->in != NULL ) fclose(C->in); free(C->malloced_buf); } /* additions by M. Kifer - revised D.A. 12/16/01 */ /* copy SLASH-terminated name of the directory of fname */ static void getDirname(const char *fname, char *dirname) { int i; for (i = strlen(fname) - 1; i >= 0; i--) { if (fname[i] == SLASH) break; } if (i >= 0) { strncpy(dirname, fname, i); dirname[i] = SLASH; } else /* just a precaution: i must be -1 in this case anyway */ i = -1; dirname[i + 1] = '\0'; } static FILE *openInCurrentDir(const char *incfile) { FILE *f; char *absfile; if (IncludeFile) { return fopen(incfile, "r"); } absfile = calloc(strlen(C->filename) + strlen(incfile) + 1, 1); getDirname(C->filename, absfile); strcat(absfile, incfile); f = fopen(absfile, "r"); free(absfile); return f; } /* skip = # of \n's already output by other mechanisms, to be skipped */ void replace_definition_with_blank_lines(const char *start, const char *end, int skip) { if ((include_directive_marker != NULL )&& (C->out->f != NULL)){ while (start <= end) { if (*start == '\n') { if (skip) skip--; else fprintf(C->out->f,"\n"); } start++; } } } /* insert blank line where the metas IFDEF,ELSE,INCLUDE, etc., stood in the input text */ void replace_directive_with_blank_line(FILE *f) { if ((include_directive_marker != NULL )&& (f != NULL) && (!S->preservelf) && (S->Meta.mArgE[0]=='\n')){ fprintf(f,"\n"); } } /* If lineno is > 15 digits - the number won't be printed correctly */ void write_include_marker(FILE *f, int lineno, char *filename, const char *marker) { static char lineno_buf[MAX_GPP_NUM_SIZE]; static char *escapedfilename = NULL; if ((include_directive_marker != NULL )&& (f != NULL)){ #ifdef WIN_NT escape_backslashes(filename,&escapedfilename); #else escapedfilename = filename; #endif sprintf(lineno_buf,"%d", lineno); fprintf(f, include_directive_marker, lineno_buf, escapedfilename, marker); } } /* Under windows, files can have backslashes in them. These should be escaped. */ void escape_backslashes(const char *instr, char **outstr) { int out_idx = 0; if (*outstr != NULL ) free(*outstr); *outstr = malloc(2 * strlen(instr)); while (*instr != '\0') { if (*instr == '\\') { *(*outstr + out_idx) = '\\'; out_idx++; } *(*outstr + out_idx) = *instr; out_idx++; instr++; } *(*outstr + out_idx) = '\0'; } /* includemarker_input should have 3 ?-marks, which are replaced with %s. Also, @ is replaced with a space. These symbols can be escaped with a backslash. */ void construct_include_directive_marker(char **include_directive_marker, const char *includemarker_input) { int len = strlen(includemarker_input); char ch; int in_idx = 0, out_idx = 0; int quoted = 0, num_repl = 0; /* only 6 extra chars are needed: 3 for the three %'s, 2 for \n, 1 for \0 */ *include_directive_marker = malloc(len + 18); ch = *includemarker_input; while (ch != '\0' && in_idx < len) { if (quoted) { *(*include_directive_marker + out_idx) = ch; out_idx++; quoted = 0; } else { switch (ch) { case '\\': quoted = 1; break; case '@': *(*include_directive_marker + out_idx) = ' '; out_idx++; break; case '%': case '?': *(*include_directive_marker + out_idx) = '%'; out_idx++; *(*include_directive_marker + out_idx) = 's'; out_idx++; if (++num_repl > 3) bug("only 3 substitutions allowed in -includemarker"); break; default: *(*include_directive_marker + out_idx) = ch; out_idx++; } } in_idx++; ch = *(includemarker_input + in_idx); } *(*include_directive_marker + out_idx) = '\n'; out_idx++; *(*include_directive_marker + out_idx) = '\0'; } int main(int argc, char **argv) { initthings(argc, argv); /* The include marker at the top of the file */ if (IncludeFile) DoInclude(IncludeFile, 0); IncludeFile = NULL; write_include_marker(C->out->f, 1, C->filename, ""); ProcessContext(); fclose(C->out->f); return EXIT_SUCCESS; }