/* File: gpp.c -- generic preprocessor ** Author: Denis Auroux, Tristan Miller ** Contact: psychonaut@nothingisreal.com ** ** Copyright (C) 1996, 1999, 2001 Denis Auroux ** Copyright (C) 2003, 2004 Tristan Miller ** ** GPP 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. ** ** GPP 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 software; if not, write to the Free Software Foundation, ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ** ** $Id: gpp.c,v 1.10 2004/09/19 20:19:16 psy Exp $ ** */ /* 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 10 /* 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) */ 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); /* ** 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 (c1filename,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) { fprintf(stderr, PACKAGE_STRING "\n"); fprintf(stderr,"Copyright (C) 1996-2001 Denis Auroux\n"); fprintf(stderr,"Copyright (C) 2003, 2004 Tristan Miller\n"); fprintf(stderr, "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) { fprintf(stderr,"Usage : gpp [-{o|O} outfile] [-I/include/path] [-Dname=val ...] [-z] [-x] [-m]\n"); fprintf(stderr," [-n] [-C | -T | -H | -X | -P | -U ... [-M ...]] [+c str1 str2]\n"); fprintf(stderr," [+s str1 str2 c] [long options] [infile]\n\n"); fprintf(stderr," default: #define x y macro(arg,...)\n"); fprintf(stderr," -C : maximum cpp compatibility (includes -n, +c, +s, ...)\n"); fprintf(stderr," -T : TeX-like \\define{x}{y} \\macro{arg}{...}\n"); fprintf(stderr," -H : HTML-like <#define x|y> <#macro arg|...>\n"); fprintf(stderr," -X : XHTML-like <#define x|y/> <#macro arg|.../>\n"); fprintf(stderr," -P : prolog compatible cpp-like mode\n"); fprintf(stderr," -U : user-defined syntax (specified in 9 following args; see manual)\n"); fprintf(stderr," -M : user-defined syntax for meta-macros (specified in 7 following args)\n\n"); fprintf(stderr," -o : output to outfile\n"); fprintf(stderr," -O : output to outfile and stdout\n"); fprintf(stderr," -z : line terminator is CR-LF (MS-DOS style)\n"); fprintf(stderr," -x : enable #exec built-in macro\n"); fprintf(stderr," -m : enable automatic mode switching upon including .h/.c files\n"); fprintf(stderr," -n : send LF characters serving as macro terminators to output\n"); fprintf(stderr," +c : use next 2 args as comment start and comment end sequences\n"); fprintf(stderr," +s : use next 3 args as string start, end and quote character\n\n"); fprintf(stderr," Long options:\n"); fprintf(stderr," --include file : process file before infile\n"); fprintf(stderr," --nostdinc : don't search standard directories for files to include\n"); fprintf(stderr," --nocurinc : don't search the current directory for files to include\n"); fprintf(stderr," --curdirinclast : search the current directory last\n"); fprintf(stderr," --warninglevel n : set warning level\n"); fprintf(stderr," --includemarker formatstring : keep track of #include directives in output\n\n"); fprintf(stderr," --version : display version information and exit\n"); fprintf(stderr," -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;iUser.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': *u='\001'; break; /* one or more spaces */ case 'w': if (neg) bug("\\w and \\W cannot be negated"); *u='\002'; break; /* zero or more spaces */ case 'B': *u='\003'; break; /* one or more spaces or \n */ case 'W': if (neg) bug("\\w and \\W cannot be negated"); *u='\004'; break; /* zero or more spaces or \n */ case 'a': *u='\005'; break; /* alphabetic */ case 'A': *u='\006'; break; /* alphabetic + space */ case '#': *u='\007'; break; /* numeric */ case 'i': *u='\010'; break; /* identifier */ case 'o': *u='\013'; break; /* operator */ case 'O': *u='\014'; break; /* operator/parenthese */ 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': *u='\001'; break; /* one or more spaces */ case 'w': if (neg) bug("\\w and \\W cannot be negated"); *u='\002'; break; /* zero or more spaces */ case 'B': *u='\003'; break; /* one or more spaces or \n */ case 'W': if (neg) bug("\\w and \\W cannot be negated"); *u='\004'; break; /* zero or more spaces or \n */ case 'a': *u='\005'; break; /* alphabetic */ case 'A': *u='\006'; break; /* alphabetic + space */ case '#': *u='\007'; break; /* numeric */ case 'i': *u='\010'; break; /* identifier */ case 'o': *u='\013'; break; /* operator */ case 'O': *u='\014'; break; /* operator/parenthese */ 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;iUser.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<='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<=*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;inamedargs[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;ilen;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 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 ((pos1spl2)&&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 ((pos1spl2)&&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 ((pos1spl2)&&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 ((pos1spl2)&&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 ((pos1spl2)&&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=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 (startUser.quotechar)&&(startop_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) { struct INPUTCONTEXT *N; char *incfile_name = NULL; FILE *f = NULL; int i, 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; id=0; 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 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;jbuf+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;iout->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;jbuf+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; 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 (iargc) 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;ibuf+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;ibuf=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;i0) 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;iin_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); return; } } if (ParsePossibleMeta()>=0) return; if (ParsePossibleUser()>=0) 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 (cargc) sendout(C->argv[(int)c],strlen(C->argv[(int)c]),0); shiftIn(l+1); return; } } l=identifierEnd(1); if (l==1) l=2; sendout(C->buf+1,l-1,1); shiftIn(l); } 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) { char *absfile = calloc(strlen(C->filename)+strlen(incfile)+1, 1); FILE *f; 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); write_include_marker(C->out->f, 1, C->filename, ""); ProcessContext(); fclose(C->out->f); return EXIT_SUCCESS; }