[RFC PATCH] dtc: integrate gpp
Stephen Warren
swarren at wwwdotorg.org
Sat Jun 16 04:53:52 EST 2012
From: Stephen Warren <swarren at nvidia.com>
I figured the easiest way to get named constants into dtc would be to
re-use an existing C pre-processor implemntation. Surprisingly, I could
not find many /good/ possibilities for this. I eventually found one called
gpp. This patch is an extremely quick-and-dirty integration of gpp into
dtc in order to solicit some feedback. What do people think of this
approach in general?
Issues:
* Fails some test-cases, related to dependency file generation, reading from
stdin and include search paths.
TODO:
* Needs to be split into many smaller patches, such as:
+ Add unmodified upstream gpp.c.
+ Various different modifications to gpp.c to make it compile within dtc.
Some of these might be able to go back upstream to gpp.
* Need a way to call gpp as a library, rather than faking a command-line for
it. Perhaps this could go upstream too.
* Need to use a pipe instead of a temp file to pass the result from gpp to
dtc. Or, if we rework gpp as a library, some intra-process way of streaming
the output between processing steps. Perhaps this could go upstream too, if
they accept the gpp-as-a-library concept.
* Need some better fixes for the const issues instead of xstrdup().
* Need to implement search paths for gpp includes.
* Probably more I forget; I originally prototyped this a while back.
* Test suite failure fixes, although at least some would be addressed by the
points above.
---
Makefile.dtc | 1 +
dtc-lexer.l | 14 +
gpp.c | 2748 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
srcpos.c | 6 +
srcpos.h | 2 +
treesource.c | 17 +
6 files changed, 2788 insertions(+), 0 deletions(-)
create mode 100644 gpp.c
diff --git a/Makefile.dtc b/Makefile.dtc
index bece49b..179c60f 100644
--- a/Makefile.dtc
+++ b/Makefile.dtc
@@ -9,6 +9,7 @@ DTC_SRCS = \
dtc.c \
flattree.c \
fstree.c \
+ gpp.c \
livetree.c \
srcpos.c \
treesource.c \
diff --git a/dtc-lexer.l b/dtc-lexer.l
index 0d54b60..230fcec 100644
--- a/dtc-lexer.l
+++ b/dtc-lexer.l
@@ -71,6 +71,20 @@ static int pop_input_file(void);
push_input_file(name);
}
+<*>"#line"{WS}*[0-9]+{WS}*{STRING} {
+ char *fn = strchr(yytext, '\"') + 1;
+ char *line = yytext + 5;
+ char *spc;
+ while (isspace(*line))
+ line++;
+ spc = line;
+ while (!isspace(*spc))
+ spc++;
+ *spc = '\0';
+ yytext[yyleng-1] = '\0';
+ srcpos_set_line(fn, atoi(line) - 1);
+ }
+
<*><<EOF>> {
if (!pop_input_file()) {
yyterminate();
diff --git a/gpp.c b/gpp.c
new file mode 100644
index 0000000..b7bfaf6
--- /dev/null
+++ b/gpp.c
@@ -0,0 +1,2748 @@
+/* File: gpp.c -- generic preprocessor
+** Author: Denis Auroux, Tristan Miller
+** Contact: psychonaut at 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 */
+
+#ifdef WIN_NT /* WIN NT settings */
+#define popen _popen
+#define pclose _pclose
+#define strdup _strdup
+#define strcasecmp _stricmp
+#define SLASH '\\'
+#define DEFAULT_CRLF 1
+#else /* UNIX settings */
+#define SLASH '/'
+#define DEFAULT_CRLF 0
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fnmatch.h>
+#include <time.h>
+
+/* FIXME: Move to a header file */
+int gpp_main(int argc,char **argv);
+
+#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*/
+static struct MODE CUser = {"", "", "(", ",", ")", "#", '\\', "(", ")" };
+static struct MODE CMeta = {"#", "\n", "\001","\001","\n","#", '\\', "(", ")" };
+static struct MODE KUser = {"", "", "(", ",", ")", "#", 0, "(", ")" };
+static struct MODE KMeta = {"\n#\002","\n", "\001","\001","\n","#", 0, "", "" };
+static struct MODE Tex = {"\\", "", "{", "}{", "}", "#", '@', "{", "}" };
+static struct MODE Html = {"<#", ">", "\003","|", ">", "#", '\\', "<", ">" };
+static 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;
+
+static 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;
+
+static struct SPECS *S;
+
+typedef struct MACRO {
+ char *username,*macrotext,**argnames;
+ int macrolen,nnamedargs;
+ struct SPECS *define_specs;
+ int defined_in_comment;
+} MACRO;
+
+static struct MACRO *macros;
+static int nmacros,nalloced;
+static char *includedir[MAXINCL];
+static int nincludedirs;
+static int execallowed;
+static int dosmode;
+static 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 %.
+*/
+static char *include_directive_marker = NULL;
+static 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. */
+static int NoStdInc = 0;
+static int NoCurIncFirst = 0;
+static int CurDirIncLast = 0;
+static int file_and_stdout = 0;
+static 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;
+
+static struct INPUTCONTEXT *C;
+
+static 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) */
+
+static void ProcessContext(void); /* the main loop */
+
+static int findIdent(const char *b,int l);
+static void delete_macro(int i);
+
+/* various recent additions */
+static void usagex(void);
+static void bug(const char *s);
+static void warning(const char *s);
+static void getDirname(const char *fname, char *dirname);
+static FILE *openInCurrentDir(const char *incfile);
+static char *ArithmEval(int pos1,int pos2);
+static void replace_definition_with_blank_lines(const char *start, const char *end, int skip);
+static void replace_directive_with_blank_line(FILE *file);
+static void write_include_marker(FILE *f, int lineno, char *filename, const char *marker);
+static void construct_include_directive_marker(char **include_directive_marker,
+ const char *includemarker_input);
+#ifdef WIN_NT
+static void escape_backslashes(const char *instr, char **outstr);
+#endif
+static void DoInclude(char *file_name);
+
+static void bug(const char *s)
+{
+ fprintf(stderr,"%s:%d: error: %s\n",C->filename,C->lineno,s);
+ exit(EXIT_FAILURE);
+}
+
+static void warning(const char *s)
+{
+ fprintf(stderr,"%s:%d: warning: %s\n",C->filename,C->lineno,s);
+}
+
+static 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=strdup(x->start);
+ y->end=strdup(x->end);
+ if (x->next!=NULL)
+ y->next=malloc(sizeof *(y->next));
+ }
+ return P;
+}
+
+static 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);
+ }
+}
+
+static void PushSpecs(const struct SPECS *X)
+{
+ struct SPECS *P;
+
+ P=CloneSpecs(X);
+ P->stack_next=S;
+ S=P;
+}
+
+static 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");
+}
+
+#define usage() do { fprintf(stderr, "%s:%d\n", __FILE__, __LINE__); usagex(); } while (0)
+
+static void usagex(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<n> str1 str2]\n");
+ fprintf(stderr," [+s<n> 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");
+}
+
+static 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;
+}
+
+static int iswhite(char c)
+{
+ if (c==' ') return 1;
+ if (c=='\t') return 1;
+ if (c=='\n') return 1;
+ return 0;
+}
+
+static 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;
+}
+
+static 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; }
+ }
+}
+
+static 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;
+}
+
+static 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 */
+static 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);
+}
+
+static int iswhitesep(const char *s)
+{
+ while (iswhite(*s)||(*s=='\001')||(*s=='\002')||(*s=='\003')||(*s=='\004'))
+ s++;
+ return (*s==0);
+}
+
+static 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);
+}
+
+static 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=strdup(s+l);
+}
+
+static 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;
+}
+
+static 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;
+ }
+}
+
+static 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]);
+}
+
+static 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);
+}
+
+static 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);
+ }
+ }
+}
+
+static 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);
+}
+
+static 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");
+ }
+}
+
+static 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];
+}
+
+static 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);
+}
+
+static 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;
+}
+
+static int iterIdentifierEnd(int start)
+{
+ int x;
+ while(1) {
+ x=identifierEnd(start);
+ if (x==start) return x;
+ start=x;
+ }
+}
+
+static int IsInCharset(CHARSET_SUBSET x,int c)
+{
+ return (x[c>>LOG_LONG_BITS] & 1L<<(c&((1<<LOG_LONG_BITS)-1)))!=0;
+}
+
+static 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;
+}
+
+static 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;
+}
+
+static 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);
+}
+
+static void AddToCharset(CHARSET_SUBSET x,int c)
+{
+ x[c>>LOG_LONG_BITS] |= 1L<<(c&((1<<LOG_LONG_BITS)-1));
+}
+
+static 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;
+}
+
+
+static 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;
+}
+
+static 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;
+}
+
+static 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;
+ }
+}
+
+static 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);
+ }
+}
+
+static 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=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);
+ }
+#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=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++]=strdup(*arg);
+ }
+ else includedir[nincludedirs++]=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",strdup("/*"),strdup("*/"),0,0);
+ add_comment(S,"ccc",strdup("//"),strdup("\n"),0,0);
+ add_comment(S,"ccc",strdup("\\\n"),strdup(""),0,0);
+ add_comment(S,"sss",strdup("\""),strdup("\""),'\\','\n');
+ add_comment(S,"sss",strdup("'"),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",strdup("\213/*"),strdup("*/"),0,0); /* \!o */
+ add_comment(S,"cii",strdup("\\\n"),strdup(""),0,0);
+ add_comment(S,"css",strdup("%"),strdup("\n"),0,0);
+ add_comment(S,"sss",strdup("\""),strdup("\""),0,'\n');
+ add_comment(S,"sss",strdup("\207'"),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]=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 */
+ }
+}
+
+static 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++;
+ }
+}
+
+static 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
+*/
+
+static 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));
+}
+
+static 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;
+
+ *p2b = 0;
+ *p2e = 0;
+
+ /* 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;
+}
+
+static 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;
+}
+
+static 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;
+}
+
+static 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 (!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);
+}
+
+static 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));
+}
+
+static 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;
+}
+
+static 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;
+}
+
+static 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;
+}
+
+static 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",strdup("/*"),strdup("*/"),0,0);
+ add_comment(P,"ccc",strdup("//"),strdup("\n"),0,0);
+ add_comment(P,"ccc",strdup("\\\n"),strdup(""),0,0);
+ add_comment(P,"sss",strdup("\""),strdup("\""),'\\','\n');
+ add_comment(P,"sss",strdup("'"),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",strdup("\213/*"),strdup("*/"),0,0); /* \!o */
+ add_comment(P,"cii",strdup("\\\n"),strdup(""),0,0);
+ add_comment(P,"css",strdup("%"),strdup("\n"),0,0);
+ add_comment(P,"sss",strdup("\""),strdup("\""),0,'\n');
+ add_comment(P,"sss",strdup("\207'"),strdup("'"),0,'\n'); /* \!# */
+ }
+ else bug("unknown standard mode");
+}
+
+static 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=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,strdup(args[0]),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,strdup(args[0]),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=strdup(args[0]);
+ S->stack_next->User.mEnd=strdup(args[1]);
+ S->stack_next->User.mArgS=strdup(args[2]);
+ S->stack_next->User.mArgSep=strdup(args[3]);
+ S->stack_next->User.mArgE=strdup(args[4]);
+ S->stack_next->User.stackchar=strdup(args[5]);
+ S->stack_next->User.unstackchar=strdup(args[6]);
+ S->stack_next->User.mArgRef=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=strdup(args[0]);
+ S->stack_next->Meta.mEnd=strdup(args[1]);
+ S->stack_next->Meta.mArgS=strdup(args[2]);
+ S->stack_next->Meta.mArgSep=strdup(args[3]);
+ S->stack_next->Meta.mArgE=strdup(args[4]);
+ S->stack_next->Meta.stackchar=strdup(args[5]);
+ S->stack_next->Meta.unstackchar=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")||!strcasecmp(opt,"on")) S->stack_next->preservelf=1;
+ else if (!strcmp(opt,"0")||!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,strdup(args[0]));
+ }
+ else if (idequal(C->buf+p1start,p1end-p1start,"charset")) {
+ if ((opt==NULL)||(nargs!=1)) bug("syntax error in #mode charset");
+ if (!strcasecmp(opt,"op"))
+ S->stack_next->op_set=MakeCharsetSubset((unsigned char *)args[0]);
+ else if (!strcasecmp(opt,"par"))
+ S->stack_next->ext_op_set=MakeCharsetSubset((unsigned char *)args[0]);
+ else if (!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 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)&&(j<nincludedirs);j++) {
+ incfile_name =
+ realloc(incfile_name,len+strlen(includedir[j])+2);
+ strcpy(incfile_name,includedir[j]);
+ incfile_name[strlen(includedir[j])]=SLASH;
+ /* extract the orig include filename */
+ strcpy(incfile_name+strlen(includedir[j])+1, file_name);
+ f=fopen(incfile_name,"r");
+ }
+ if (incfile_name!=NULL)
+ free(incfile_name);
+
+ /* If didn't find the file and "." is said to be searched last */
+ if (f==NULL && CurDirIncLast) {
+ f = openInCurrentDir(file_name);
+ }
+
+ if (f==NULL)
+ bug("Requested include file not found");
+
+ N=C;
+ C=malloc(sizeof *C);
+ C->in=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 - 1, 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;
+}
+
+static 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,strdup("\""));
+ add_comment(S,"sss",strdup("\""),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);
+ } 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;
+
+ default: bug("Internal meta-macro identification error");
+ }
+ shiftIn(macend);
+ return 0;
+}
+
+static 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;
+}
+
+static void ParseText(void)
+{
+ int l,cs,ce;
+ char c,*s;
+ struct COMMENT *p;
+
+ /* 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);
+ 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 (c<C->argc)
+ 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);
+}
+
+static 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 */
+static 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
+*/
+static 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 */
+static 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);
+ }
+}
+
+
+#ifdef WIN_NT
+/* Under windows, files can have backslashes in them.
+ These should be escaped.
+*/
+static 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';
+}
+#endif
+
+/* 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.
+*/
+static 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 gpp_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;
+}
diff --git a/srcpos.c b/srcpos.c
index 3ee523d..444a876 100644
--- a/srcpos.c
+++ b/srcpos.c
@@ -328,3 +328,9 @@ srcpos_warn(struct srcpos *pos, char const *fmt, ...)
va_end(va);
}
+
+void srcpos_set_line(const char *f, int l)
+{
+ current_srcfile->name = xstrdup(f); /* dup to get rid of const */
+ current_srcfile->lineno = l;
+}
diff --git a/srcpos.h b/srcpos.h
index 5617916..0e47f9f 100644
--- a/srcpos.h
+++ b/srcpos.h
@@ -113,4 +113,6 @@ extern void srcpos_error(struct srcpos *pos, char const *, ...)
extern void srcpos_warn(struct srcpos *pos, char const *, ...)
__attribute__((format(printf, 2, 3)));
+extern void srcpos_set_line(const char *f, int l);
+
#endif /* _SRCPOS_H_ */
diff --git a/treesource.c b/treesource.c
index 33eeba5..48fb6bf 100644
--- a/treesource.c
+++ b/treesource.c
@@ -28,8 +28,25 @@ extern YYLTYPE yylloc;
struct boot_info *the_boot_info;
int treesource_error;
+extern int gpp_main(int argc,char **argv);
+
struct boot_info *dt_from_source(const char *fname)
{
+ /* FIXME: Need to add -I directives here */
+ char *argv[] = {
+ "gpp",
+ "--includemarker",
+ "#line % \"%\"",
+ "-C",
+ "-o",
+ "tmpfile", /* FIXME: fork and make this a pipe */
+ xstrdup(fname), /* dup to get rid of const */
+ NULL
+ };
+
+ gpp_main(ARRAY_SIZE(argv), argv);
+ fname = "tmpfile";
+
the_boot_info = NULL;
treesource_error = 0;
--
1.7.0.4
More information about the devicetree-discuss
mailing list