[PATCH REPOST] dtc: integrate gpp

Stephen Warren swarren at wwwdotorg.org
Wed Aug 22 14:11:07 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.
---
Note: This patch is based on a slightly stale copy of dtc, plus some local
patches for other work I've sent, some of which has been accepted upstream
already, and some note. If this approach appeals to anyone, I will of course
rebase this.

 Makefile.dtc |    1 +
 dtc-lexer.l  |   14 +
 gpp.c        | 2748 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 srcpos.c     |    6 +
 srcpos.h     |    2 +
 treesource.c |   17 +
 6 files changed, 2788 insertions(+)
 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.9.5



More information about the devicetree-discuss mailing list