[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