gpp.c

00001 /* File:      gpp.c  -- generic preprocessor
00002 ** Author:    Denis Auroux
00003 ** Contact:   auroux@math.polytechnique.fr
00004 ** Version:   2.1.2
00005 ** 
00006 ** Copyright (C) Denis Auroux 1996, 1999, 2001, 2002
00007 ** 
00008 ** GPP is free software; you can redistribute it and/or modify it under the
00009 ** terms of the GNU Library General Public License as published by the Free
00010 ** Software Foundation; either version 2 of the License, or (at your option)
00011 ** any later version.
00012 ** 
00013 ** GPP is distributed in the hope that it will be useful, but WITHOUT ANY
00014 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00015 ** FOR A PARTICULAR PURPOSE.  See the GNU Library General Public License for
00016 ** more details.
00017 ** 
00018 ** You should have received a copy of the GNU Library General Public License
00019 ** along with this software; if not, write to the Free Software Foundation,
00020 ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00021 **
00022 ** $Id: gpp.c,v 1.33 2004/11/30 19:25:08 kifer Exp $
00023 ** 
00024 */
00025 
00026 /* To compile under MS VC++, one must define WIN_NT */
00027 
00028 #ifdef WIN_NT              /* WIN NT settings */
00029 #define popen   _popen
00030 #define pclose  _pclose
00031 #define strdup  _strdup
00032 #define strcasecmp _stricmp
00033 #define SLASH '\\'
00034 #define DEFAULT_CRLF 1
00035 #else                      /* UNIX settings */
00036 #define SLASH '/'
00037 #define DEFAULT_CRLF 0
00038 #endif
00039 
00040 #include <stdio.h>
00041 #include <stdlib.h>
00042 #include <string.h>
00043 #include <ctype.h>
00044 
00045 #define STACKDEPTH 50
00046 #define MAXARGS 100
00047 #define MAXINCL 14   /* max # of include dirs */
00048 
00049 #define MAX_GPP_NUM_SIZE 15
00050 
00051 typedef struct MODE {
00052   char *mStart;         /* before macro name */
00053   char *mEnd;           /* end macro without arg */
00054   char *mArgS;          /* start 1st argument */
00055   char *mArgSep;        /* separate arguments */
00056   char *mArgE;          /* end last argument */
00057   char *mArgRef;        /* how to refer to arguments in a def */
00058   char quotechar;       /* quote next char */
00059   char *stackchar;      /* characters to stack */
00060   char *unstackchar ;   /* characters to unstack */
00061 } MODE;
00062 
00063 /* translation for delimiters :
00064    \001 = \b = ' ' = one or more spaces    \201 = \!b = non-space
00065    \002 = \w = zero or more spaces 
00066    \003 = \B = one or more spaces or \n    \203 = \!B = non-space nor \n
00067    \004 = \W = zero or more spaces or \n 
00068    \005 = \a = alphabetic (a-z, A-Z)       \205 = \!a = non-alphabetic
00069    \006 = \A = alphabetic or space/\n      \206 = \!A
00070    \007 = \# = numeric (0-9)               \207 = \!#
00071    \010 = \i = identifier (a-zA-Z0-9_)     \210 = \!i
00072    \011 = \t, \012 = \n                    \211 = \!t, \212 = \!n
00073    \013 = \o = operator (+-*\/^<>=`~:.?@#&!%|) \213 = \!o
00074    \014 = \O = operator or ()[]{}              \214 = \!O
00075 */
00076 /*                   st        end   args   sep    arge ref  quot  stk  unstk*/
00077 struct MODE CUser = {"",       "",   "(",   ",",   ")", "#", '\\', "(", ")" };
00078 struct MODE CMeta = {"#",      "\n", "\001","\001","\n","#", '\\', "(", ")" };
00079 struct MODE KUser = {"",       "",   "(",   ",",   ")", "#",  0,   "(", ")" };
00080 struct MODE KMeta = {"\n#\002","\n", "\001","\001","\n","#",  0,   "",  ""  }; 
00081 struct MODE Tex   = {"\\",     "",   "{",   "}{",  "}", "#", '@',  "{", "}" };
00082 struct MODE Html  = {"<#",     ">",  "\003","|",   ">", "#", '\\', "<", ">" };
00083 
00084 #define DEFAULT_OP_STRING (unsigned char *)"+-*/\\^<>=`~:.?@#&!%|"
00085 #define PROLOG_OP_STRING  (unsigned char *)"+-*/\\^<>=`~:.?@#&"
00086 #define FLORA_OP_STRING   (unsigned char *)"+-*/\\^<>=`~:.?@#&%"
00087 #define DEFAULT_OP_PLUS   (unsigned char *)"()[]{}"
00088 #define DEFAULT_ID_STRING (unsigned char *)"\005\007_" /* or equiv. "A-Za-z0-9_" */
00089 
00090 /* here we assume that longs are at least 32 bit... if not, change this ! */
00091 #define LOG_LONG_BITS 5
00092 #define CHARSET_SUBSET_LEN (256>>LOG_LONG_BITS)
00093 typedef unsigned long *CHARSET_SUBSET;
00094 
00095 CHARSET_SUBSET DefaultOp,DefaultExtOp,PrologOp,FloraOp,DefaultId;
00096 
00097 typedef struct COMMENT {
00098   char *start;          /* how the comment/string starts */
00099   char *end;            /* how it ends */
00100   char quote;           /* how to prevent it from ending */
00101   char warn;            /* a character that shouldn't be in there */
00102   int flags[3];         /* meta, user, text */
00103   struct COMMENT *next;
00104 } COMMENT;
00105 
00106 #define OUTPUT_TEXT     0x1   /* what's inside will be output */
00107 #define OUTPUT_DELIM    0x2   /* the delimiters will be output */
00108 #define PARSE_MACROS    0x4   /* macros inside will be parsed */
00109 #define FLAG_IGNORE     0x40 
00110 
00111 #define FLAG_STRING    (OUTPUT_TEXT|OUTPUT_DELIM)
00112 #define FLAG_COMMENT   0
00113 
00114 #define FLAG_META 0
00115 #define FLAG_USER 1
00116 #define FLAG_TEXT 2
00117 
00118 /* Some stuff I removed because it made for some impossible situations :
00119 
00120  #define PARSE_COMMENTS  0x8   
00121    comments inside comments will not be parsed because nesting comments is 
00122    too complicated (syntax conflicts, esp. to find a comment's end)
00123    -- of course, unless the comment is ignored.
00124    
00125  #define MACRO_FRIENDLY  0x20  
00126    a comment-end is to be processed even if an unfinished macro call has 
00127    started inside the comment, otherwise it's too hard do decide in advance 
00128    where a comment ends. In particular foo('bar((((') is valid.
00129 
00130  #define PREVENT_DELIM   0x10 
00131    all comments will prevent macro delimitation, i.e. foo('bar) is invalid.
00132    -- of course, unless the comment is ignored.
00133    Too bad, #define foo '...    terminates only at following "'".
00134    Unless one adds quotechars like in #define foo \' ...
00135    
00136  ALSO NOTE : comments are not allowed before the end of the first argument
00137  to a meta-macro. E.g. this is legal :   #define foo <* blah *> 3
00138  This is not legal :                     #define <* blah *> foo 3
00139  If a comment occurs here, the behavior depends on the actual meta-macro :
00140  most will yield an error and stop gpp (#define, #undef, #ifdef/ifndef, 
00141  #defeval, #include, #mode) ; #exec, #if and #eval should be ok ; 
00142  #ifeq will always fail while #ifneq will always succeed ;
00143 */ 
00144 
00145 typedef struct SPECS {
00146   struct MODE User,Meta;
00147   struct COMMENT *comments;
00148   struct SPECS *stack_next;
00149   int preservelf;
00150   CHARSET_SUBSET op_set,ext_op_set,id_set;
00151   int readonly; /* we're a copy stored in a macro definition */
00152 } SPECS;
00153 
00154 struct SPECS *S;
00155 
00156 typedef struct MACRO {
00157   char *username,*macrotext,**argnames;
00158   int macrolen,nnamedargs;
00159   struct SPECS *define_specs;
00160   int defined_in_comment;
00161 } MACRO;
00162 
00163 #define HASH_SIZE 256
00164 #define HASH_CONST 37
00165 
00166 struct MACRO *macros[HASH_SIZE];
00167 int nmacros[HASH_SIZE],nalloced[HASH_SIZE];
00168 char *includedir[MAXINCL];
00169 int nincludedirs;
00170 int execallowed;
00171 int dosmode;
00172 int autoswitch;
00173 /* must be a format-like string that has % % % in it.
00174    The first % is replaced with line number, the second with "filename", and
00175    the third with 1, 2 or blank
00176    Can also use ? instead of %.
00177 */
00178 char *include_directive_marker = NULL;
00179 short WarningLevel = 2;
00180 
00181 /* controls if standard dirs, like /usr/include, are to be searched for
00182    #include and whether the current dir is to be searched first or last. */
00183 int NoStdInc        = 0;
00184 int NoCurIncFirst   = 0;
00185 int CurDirIncLast   = 0;
00186 int file_and_stdout = 0;
00187 
00188 typedef struct OUTPUTCONTEXT {
00189   char *buf;
00190   int len,bufsize;
00191   FILE *f;
00192 } OUTPUTCONTEXT;
00193 
00194 typedef struct INPUTCONTEXT {
00195   char *buf;
00196   char *malloced_buf; /* what was actually malloc-ed (buf may have shifted) */
00197   int len,bufsize;
00198   int lineno;
00199   char *filename;
00200   FILE *in;
00201   int argc;
00202   char **argv;
00203   char **namedargs;
00204   struct OUTPUTCONTEXT *out;
00205   int eof;
00206   int in_comment;
00207   int ambience; /* FLAG_TEXT, FLAG_USER or FLAG_META */
00208   int may_have_args;
00209 } INPUTCONTEXT;
00210 
00211 struct INPUTCONTEXT *C;
00212   
00213 int commented[STACKDEPTH],iflevel;
00214 /* commented = 0: output, 1: not output, 
00215    2: not output because we're in a #elif and we've already gone through
00216       the right case (so #else/#elif can't toggle back to output) */
00217 
00218 void ProcessContext(void); /* the main loop */
00219 
00220 int findIdent(char *b,int l,int *h);
00221 void delete_macro(int h,int i);
00222 
00223 /* various recent additions */
00224 static void getDirname(char *fname, char *dirname);
00225 static FILE *openInCurrentDir(char *incfile);
00226 char *ArithmEval(int pos1,int pos2);
00227 void replace_definition_with_blank_lines(char *start, char *end, int skip);
00228 void replace_directive_with_blank_line(FILE *file);
00229 void write_include_marker(FILE *f, int lineno, char *filename, char *marker);
00230 void construct_include_directive_marker(char **include_directive_marker,
00231                                         char *includemarker_input);
00232 void escape_backslashes(char *instr, char **outstr);
00233 
00234 void bug(char *s)
00235 {
00236   fprintf(stderr,"++Error[GPP]: %s:%d: %s.\n",C->filename,C->lineno,s);
00237   exit(1);
00238 }
00239 
00240 void warning(char *s)
00241 {
00242   fprintf(stderr,"++Warning[GPP]: %s:%d: %s.\n",C->filename,C->lineno,s);
00243 }
00244 
00245 int hash_str(char *s,int l)
00246 {
00247   int h=0;
00248   while (l--) { h = HASH_CONST*h+*(s++); }
00249   return h&(HASH_SIZE-1);
00250 }
00251 
00252 struct SPECS *CloneSpecs(struct SPECS *Q)
00253 {
00254   struct SPECS *P;
00255   struct COMMENT *x,*y;
00256   
00257   P=(struct SPECS *)malloc(sizeof(struct SPECS));
00258   if (P==NULL) bug("Out of memory.");
00259   memcpy((char *)P,(char *)Q,sizeof(struct SPECS));
00260   P->stack_next=NULL;
00261   P->readonly=0;
00262   if (Q->comments!=NULL) 
00263     P->comments=(struct COMMENT *)malloc(sizeof(struct COMMENT));
00264   for (x=Q->comments,y=P->comments;x!=NULL;x=x->next,y=y->next) {
00265     memcpy((char *)y,(char *)x,sizeof(struct COMMENT));
00266     y->start=strdup(x->start);
00267     y->end=strdup(x->end);
00268     if (x->next!=NULL)
00269       y->next=(struct COMMENT *)malloc(sizeof(struct COMMENT));
00270   }
00271   return P;
00272 }
00273 
00274 void FreeComments(struct SPECS *Q)
00275 {
00276   struct COMMENT *p;
00277   
00278   while (Q && Q->comments!=NULL) {
00279     p=Q->comments;
00280     Q->comments=p->next;
00281     free(p->start);
00282     free(p->end);
00283     free(p);
00284   }
00285 }
00286 
00287 void PushSpecs(struct SPECS *X)
00288 {
00289   struct SPECS *P;
00290   
00291   if (X->readonly && X->stack_next==NULL) P=X; /* optimize macro calls */
00292   else P=CloneSpecs(X);
00293   P->stack_next=S;
00294   S=P;
00295 }
00296 
00297 void PopSpecs(void)
00298 {
00299   struct SPECS *P;
00300   
00301   P=S;
00302   S=P->stack_next;
00303   if (P->readonly) P->stack_next=NULL;
00304   else {
00305     FreeComments(P);
00306     free(P);
00307   }
00308   if (S==NULL) bug("#mode restore without #mode save");
00309 }
00310 
00311 void usage(void) {
00312   fprintf(stderr,"GPP Version 2.1.2 - Generic Preprocessor - (c) Denis Auroux 1996-2002\n");
00313   fprintf(stderr,"Usage : gpp [-{o|O} outfile] [-I/include/path] [-Dname=val ...] [-z] [-x] [-m]\n");
00314   fprintf(stderr,"            [-n] [-C | -T | -H | -P | -U ... [-M ...]] [+c<n> str1 str2]\n");
00315   fprintf(stderr,"            [+s<n> str1 str2 c] [infile]\n\n");
00316   fprintf(stderr,"      default:    #define x y           macro(arg,...)\n");
00317   fprintf(stderr," -C : maximum cpp compatibility (includes -n, +c, +s, ...)\n");
00318   fprintf(stderr," -T : TeX-like    \\define{x}{y}         \\macro{arg}{...}\n");
00319   fprintf(stderr," -H : HTML-like   <#define x|y>         <#macro arg|...>\n");
00320   fprintf(stderr," -P : Prolog compatible cpp-like mode\n");
00321   fprintf(stderr," -F : Flora compatible cpp-like mode\n");
00322   fprintf(stderr," -U : user-defined syntax (specified in 9 following args; see manual)\n");
00323   fprintf(stderr," -M : user-defined syntax for meta-macros (specified in 7 following args)\n\n");
00324   fprintf(stderr," -o : output to outfile\n");
00325   fprintf(stderr," -O : output to outfile and stdout\n");
00326   fprintf(stderr," -z : line terminator is CR-LF (MS-DOS style)\n");
00327   fprintf(stderr," -x : enable #exec built-in macro\n");
00328   fprintf(stderr," -m : enable automatic mode switching upon including .h/.c files\n");
00329   fprintf(stderr," -n : send LF characters serving as macro terminators to output\n");
00330   fprintf(stderr," +c : use next 2 args as comment start and comment end sequences\n");
00331   fprintf(stderr," +s : use next 3 args as string start, end and quote character\n\n");
00332   fprintf(stderr," -nostdinc : don't search standard directories for files to include\n");
00333   fprintf(stderr," -nocurinc : don't search the current directory for files to include\n");
00334   fprintf(stderr," -curdirinclast : search the current directory last\n");
00335   fprintf(stderr," -warninglevel n : set warning level\n");
00336   fprintf(stderr," -includemarker formatstring : keep track of #include directives in output\n\n");
00337   exit(1);
00338 }
00339 
00340 int isdelim(unsigned char c)
00341 {
00342   if (c>=128) return 0;
00343   if ((c>='0')&&(c<='9')) return 0;
00344   if ((c>='A')&&(c<='Z')) return 0;
00345   if ((c>='a')&&(c<='z')) return 0;
00346   if (c=='_') return 0;
00347   return 1;
00348 }
00349 
00350 int iswhite(char c)
00351 {
00352   if (c==' ') return 1;
00353   if (c=='\t') return 1;
00354   if (c=='\n') return 1;
00355   return 0;
00356 }
00357 
00358 struct MACRO *newmacro(char *s,int len,int hasspecs,int h)
00359 {
00360   struct MACRO *m;
00361   if (h<0) h=hash_str(s,len);
00362   if (nmacros[h]==nalloced[h]) {
00363     nalloced[h]=2*nalloced[h]+1;
00364     macros[h]=(struct MACRO *)realloc((char *)macros[h],nalloced[h]*sizeof(struct MACRO));
00365     if (macros[h]==NULL)
00366       bug("Out of memory");
00367   }
00368   m=macros[h]+nmacros[h];
00369   nmacros[h]++;
00370   m->username=malloc(len+1);
00371   strncpy(m->username,s,len);
00372   m->username[len]=0;
00373   m->argnames=NULL;
00374   m->nnamedargs=0;
00375   m->defined_in_comment=0;
00376   if (hasspecs) {
00377     m->define_specs=CloneSpecs(S);
00378     m->define_specs->readonly=1;
00379   } else
00380     m->define_specs = NULL; /* otherwise might crash when PushSpecs is
00381                                called in ParsePossibleUser */
00382   return m;
00383 }
00384 
00385 void lookupArgRefs(struct MACRO *m)
00386 {
00387   int i,l;
00388   char *p;
00389   
00390   if (m->argnames!=NULL) return; /* don't mess with those */
00391   m->nnamedargs=-1;
00392   l=strlen(S->User.mArgRef);
00393   for (i=0,p=m->macrotext;i<m->macrolen;i++,p++) {
00394     if ((*p!=0)&&(*p==S->User.quotechar)) { i++; p++; }
00395     else if (!strncmp(p,S->User.mArgRef,l))
00396       if ((p[l]>='1')&&(p[l]<='9')) 
00397         { m->nnamedargs=0; return; }
00398   }
00399 }
00400 
00401 char *strnl0(char *s) /* replace "\\n" by "\n" in a cmd-line arg */
00402 {
00403   char *t,*u;
00404   t=(char *)malloc(strlen(s)+1);
00405   u=t;
00406   while (*s!=0) {
00407     if ((*s=='\\')&&(s[1]=='n')) { *u='\n'; s++; }
00408     else *u=*s;
00409     s++; u++;
00410   }
00411   *u=0;
00412   return t;
00413 }
00414 
00415 char *strnl(char *s) /* the same but with whitespace specifier handling */
00416 {
00417   char *t,*u;
00418   int neg;
00419   t=(char *)malloc(strlen(s)+1);
00420   u=t;
00421   if (!isdelim(*s)) bug("character not allowed to start a syntax specifier");
00422   while (*s!=0) {
00423     if (((*s&0x60)==0)&&(*s!='\n')&&(*s!='\t')) 
00424       bug("character not allowed in syntax specifier");
00425     if (*s=='\\') {
00426       neg=(s[1]=='!');
00427       switch(s[neg+1]) {
00428         case 'n': case 'r': *u='\n'; break;
00429         case 't': *u='\t'; break;
00430         case 'b': *u='\001'; break;  /* one or more spaces */
00431         case 'w': if (neg) bug("\\w and \\W cannot be negated");
00432                   *u='\002'; break;  /* zero or more spaces */
00433         case 'B': *u='\003'; break;  /* one or more spaces or \n */
00434         case 'W': if (neg) bug("\\w and \\W cannot be negated");
00435                   *u='\004'; break;  /* zero or more spaces or \n */
00436         case 'a': *u='\005'; break;  /* alphabetic */
00437         case 'A': *u='\006'; break;  /* alphabetic + space */
00438         case '#': *u='\007'; break;  /* numeric */
00439         case 'i': *u='\010'; break;  /* identifier */
00440         case 'o': *u='\013'; break;  /* operator */
00441         case 'O': *u='\014'; break;  /* operator/parenthese */
00442         default: *u='\\'; neg=-1;
00443       }
00444       if (neg>0) *u+=(char)128;
00445       s+=neg+1;
00446     }
00447     else if (*s==' ') *u='\001';
00448     else *u=*s;
00449     s++; u++;
00450   }
00451   *u=0;
00452   return t;
00453 }
00454 
00455 /* same as strnl() but for C strings & in-place */
00456 char *strnl2(char *s,int check_delim)
00457 {
00458   char *u;
00459   int neg;
00460   u=s;
00461   if (check_delim&&!isdelim(*s)) 
00462     bug("character not allowed to start a syntax specifier");
00463   while (*s!='"') {
00464     if (((*s&0x60)==0)&&(*s!='\n')&&(*s!='\t')) 
00465       bug("character not allowed in syntax specifier");
00466     if (*s=='\\') {
00467       neg=(s[1]=='!');
00468       switch(s[neg+1]) {
00469         case 'n': case 'r': *u='\n'; break;
00470         case 't': *u='\t'; break;
00471         case 'b': *u='\001'; break;  /* one or more spaces */
00472         case 'w': if (neg) bug("\\w and \\W cannot be negated");
00473                   *u='\002'; break;  /* zero or more spaces */
00474         case 'B': *u='\003'; break;  /* one or more spaces or \n */
00475         case 'W': if (neg) bug("\\w and \\W cannot be negated");
00476                   *u='\004'; break;  /* zero or more spaces or \n */
00477         case 'a': *u='\005'; break;  /* alphabetic */
00478         case 'A': *u='\006'; break;  /* alphabetic + space */
00479         case '#': *u='\007'; break;  /* numeric */
00480         case 'i': *u='\010'; break;  /* identifier */
00481         case 'o': *u='\013'; break;  /* operator */
00482         case 'O': *u='\014'; break;  /* operator/parenthese */
00483         case '"': case '\\': if (!neg) { *u=s[1]; break; }
00484         default: bug("unknown escape sequence in syntax specifier");
00485       }
00486       if (neg>0) *u+=(char)128;
00487       s+=neg+1;
00488     }
00489     else if (*s==' ') *u='\001';
00490     else *u=*s;
00491     if (*s==0) bug("unterminated string in #mode command");
00492     s++; u++;
00493   }
00494   *u=0;
00495   return (s+1);
00496 }
00497 
00498 int iswhitesep(char *s)
00499 {
00500   while (iswhite(*s)||(*s=='\001')||(*s=='\002')||(*s=='\003')||(*s=='\004')) 
00501     s++;
00502   return (*s==0);
00503 }
00504 
00505 int nowhite_strcmp(char *s,char *t)
00506 {
00507   char *p;
00508   
00509   while (iswhite(*s)) s++;
00510   while (iswhite(*t)) t++;
00511   if ((*s==0)||(*t==0)) return strcmp(s,t);
00512   p=s+strlen(s)-1;
00513   while (iswhite(*p)) *(p--)=0;
00514   p=t+strlen(t)-1;
00515   while (iswhite(*p)) *(p--)=0;
00516   return strcmp(s,t);
00517 }
00518 
00519 void parseCmdlineDefine(char *s)
00520 {
00521   int l, i, argc, h;
00522   struct MACRO *m;
00523   
00524   for (l=0;s[l]&&(s[l]!='=')&&(s[l]!='(');l++);
00525   h = -1;
00526   i = findIdent(s,l,&h);
00527   if (i>=0) delete_macro(h,i);
00528   m=newmacro(s,l,0,h);
00529   
00530   /* possibly allow named arguments: -Dmacro(arg1,arg2)=... (no spaces) */
00531   if (s[l]=='(') {
00532     argc = 0;
00533     do {
00534       l++; i=l;
00535       while (!isdelim(s[i])) i++;
00536       if (s[i]!=',' && s[i]!=')') bug("invalid syntax in -D declaration");
00537       if (i>l) argc++;
00538       m->argnames = (char **)realloc(m->argnames, (argc+1)*sizeof(char *));
00539       if (i>l) {
00540         m->argnames[argc-1]=malloc(i-l+1);
00541         memcpy(m->argnames[argc-1], s+l, i-l);
00542         m->argnames[argc-1][i-l]=0;
00543       }
00544       l = i;
00545     } while (s[l]!=')');
00546     l++;
00547     m->nnamedargs = argc;
00548     m->argnames[argc] = NULL;
00549   }
00550   
00551   /* the macro definition afterwards ! */
00552   if (s[l]=='=') l++;
00553   else if (s[l]!=0) bug("invalid syntax in -D declaration");
00554   m->macrolen=strlen(s+l);
00555   m->macrotext=strdup(s+l);
00556 }
00557 
00558 int readModeDescription(char **args,struct MODE *mode,int ismeta)
00559 {
00560   if (!(*(++args))) return 0;
00561   mode->mStart=strnl(*args);
00562   if (!(*(++args))) return 0;
00563   mode->mEnd=strnl(*args);
00564   if (!(*(++args))) return 0;
00565   mode->mArgS=strnl(*args); 
00566   if (!(*(++args))) return 0;
00567   mode->mArgSep=strnl(*args); 
00568   if (!(*(++args))) return 0;
00569   mode->mArgE=strnl(*args); 
00570   if (!(*(++args))) return 0;
00571   mode->stackchar=strnl(*args); 
00572   if (!(*(++args))) return 0;
00573   mode->unstackchar=strnl(*args); 
00574   if (ismeta) return 1;
00575   if (!(*(++args))) return 0;
00576   mode->mArgRef=strnl(*args); 
00577   if (!(*(++args))) return 0;
00578   mode->quotechar=**args;
00579   return 1;
00580 }
00581 
00582 int parse_comment_specif(char c)
00583 {
00584   switch (c) {
00585   case 'I': case 'i': return FLAG_IGNORE;
00586   case 'c': return FLAG_COMMENT;
00587   case 's': return FLAG_STRING;
00588   case 'q': return OUTPUT_TEXT;
00589   case 'S': return FLAG_STRING|PARSE_MACROS;
00590   case 'Q': return OUTPUT_TEXT|PARSE_MACROS;
00591   case 'C': return FLAG_COMMENT|PARSE_MACROS;
00592   default: bug("Invalid comment/string modifier"); return 0;
00593   }
00594 }
00595 
00596 void add_comment(struct SPECS *S,char *specif,char *start,char *end,char quote,char warn)
00597 {
00598   struct COMMENT *p;
00599   
00600   if (*start==0) bug("Comment/string start delimiter must be non-empty");
00601   for (p=S->comments;p!=NULL;p=p->next)
00602     if (!strcmp(p->start,start)) {
00603       if (strcmp(p->end,end)) /* already exists with a different end */
00604         bug("Conflicting comment/string delimiter specifications");
00605       free(p->start);
00606       free(p->end);
00607       break;
00608     }
00609 
00610   if (p==NULL) {
00611     p=(struct COMMENT *)malloc(sizeof(struct COMMENT));
00612     p->next=S->comments;
00613     S->comments=p;
00614   }
00615   p->start=start;
00616   p->end=end;
00617   p->quote=quote;
00618   p->warn=warn;
00619   if (strlen(specif)!=3) bug("Invalid comment/string modifier");
00620   p->flags[FLAG_META]=parse_comment_specif(specif[0]);
00621   p->flags[FLAG_USER]=parse_comment_specif(specif[1]);
00622   p->flags[FLAG_TEXT]=parse_comment_specif(specif[2]);
00623 }
00624 
00625 void delete_comment(struct SPECS *S,char *start)
00626 {
00627   struct COMMENT *p,*q;
00628   
00629   q=NULL;
00630   for (p=S->comments;p!=NULL;p=p->next) {
00631     if (!strcmp(p->start,start)) {
00632       if (q==NULL) S->comments=p->next;
00633       else q->next=p->next;
00634       free(p->start);
00635       free(p->end);
00636       free(p);
00637       free(start);
00638       return;
00639     }
00640     else q=p;
00641   }
00642   free(start);
00643 }
00644 
00645 void outchar(char c)
00646 {
00647   if (C->out->bufsize) {
00648     if (C->out->len+1==C->out->bufsize) { 
00649       C->out->bufsize=C->out->bufsize*2;
00650       C->out->buf=realloc(C->out->buf,C->out->bufsize);
00651       if (C->out->buf==NULL) bug("Out of memory");
00652     }
00653     C->out->buf[C->out->len++]=c;
00654   }
00655   else {
00656     if (dosmode&&(c==10)) {
00657       fputc(13,C->out->f);
00658       if (file_and_stdout)
00659         fputc(13,stdout);
00660     }
00661     if (c!=13) {
00662       fputc(c,C->out->f);
00663       if (file_and_stdout)
00664         fputc(c,stdout);
00665     }
00666   }
00667 }
00668 
00669 void sendout(char *s,int l,int proc) /* only process the quotechar, that's all */
00670 {
00671   int i;
00672   
00673   if (!commented[iflevel])
00674     for (i=0;i<l;i++) {
00675       if (proc&&(s[i]!=0)&&(s[i]==S->User.quotechar)) 
00676         { i++; if (i==l) return; }
00677       if (s[i]!=0) outchar(s[i]);
00678     }
00679   else
00680     replace_definition_with_blank_lines(s, s+l-1, 0);
00681 }
00682 
00683 void extendBuf(int pos)
00684 {
00685   char *p;
00686   if (C->bufsize<=pos) {
00687     C->bufsize+=pos; /* approx double */
00688     p=(char *)malloc(C->bufsize);
00689     memcpy(p,C->buf,C->len);
00690     free(C->malloced_buf);
00691     C->malloced_buf=C->buf=p;
00692     if (C->buf==NULL) bug("Out of memory");
00693   }
00694 }
00695 
00696 char getChar(int pos)
00697 {
00698   int c;
00699 
00700   if (C->in==NULL) {
00701     if (pos>=C->len) return 0;
00702     else return C->buf[pos];
00703   }
00704   extendBuf(pos);
00705   while (pos>=C->len) {
00706     do { c=fgetc(C->in); } while (c==13);
00707     if (c=='\n') C->lineno++;
00708     if (c==EOF) c=0;
00709     C->buf[C->len++]=(char)c;
00710   }
00711   return C->buf[pos];
00712 }
00713 
00714 int whiteout(int *pos1,int *pos2) /* remove whitespace on both sides */
00715 {
00716   while ((*pos1<*pos2)&&iswhite(getChar(*pos1))) (*pos1)++;
00717   while ((*pos1<*pos2)&&iswhite(getChar(*pos2-1))) (*pos2)--;
00718   return (*pos1<*pos2);
00719 }
00720 
00721 int identifierEnd(int start)
00722 {
00723   char c;
00724 
00725   c=getChar(start);
00726   if (c==0) return start;
00727   if (c==S->User.quotechar) {
00728     c=getChar(start+1);
00729     if (c==0) return (start+1);
00730     if (isdelim(c)) return (start+2);
00731     start+=2;
00732     c=getChar(start);
00733   }
00734   while (!isdelim(c)) c=getChar(++start);
00735   return start;
00736 }
00737 
00738 int iterIdentifierEnd(int start)
00739 {
00740   int x;
00741   while(1) {
00742     x=identifierEnd(start);
00743     if (x==start) return x;
00744     start=x;
00745   }
00746 }
00747 
00748 int IsInCharset(CHARSET_SUBSET x,int c)
00749 {
00750   return (x[c>>LOG_LONG_BITS] & 1L<<(c&((1<<LOG_LONG_BITS)-1)))!=0;
00751 }
00752 
00753 int matchSequence(char *s,int *pos)
00754 {
00755   int i=*pos;
00756   int match;
00757   char c;
00758 
00759   while (*s!=0) {
00760     if (!((*s)&0x60)) { /* special sequences */
00761       match=1;
00762       switch((*s)&0x1f) {
00763       case '\001':
00764         c=getChar(i++);
00765         if ((c!=' ')&&(c!='\t'))
00766           { match=0; break; }
00767       case '\002':
00768         i--;
00769         do { c=getChar(++i); } while ((c==' ')||(c=='\t'));
00770         break;
00771       case '\003':
00772         c=getChar(i++);
00773         if ((c!=' ')&&(c!='\t')&&(c!='\n'))
00774           { match=0; break; }
00775       case '\004':
00776         i--;
00777         do { c=getChar(++i); } while ((c==' ')||(c=='\t')||(c=='\n'));
00778         break;
00779       case '\006':
00780         c=getChar(i++);
00781         match = ((c>='a')&&(c<='z')) || ((c>='A')&&(c<='Z')) 
00782           ||(c==' ')||(c=='\t')||(c=='\n');
00783         break;
00784       case '\005':
00785         c=getChar(i++);
00786         match = ((c>='a')&&(c<='z')) || ((c>='A')&&(c<='Z')); break;
00787       case '\007':
00788         c=getChar(i++);
00789         match = ((c>='0')&&(c<='9')); break;
00790       case '\010':
00791         c=getChar(i++);
00792         match = IsInCharset(S->id_set,c); break;
00793       case '\011':
00794         c=getChar(i++);
00795         match = (c=='\t'); break;
00796       case '\012':
00797         c=getChar(i++);
00798         match = (c=='\n'); break;
00799       case '\013':
00800         c=getChar(i++);
00801         match = IsInCharset(S->op_set,c); break;
00802       case '\014':
00803         c=getChar(i++);
00804         match = IsInCharset(S->ext_op_set,c) || IsInCharset(S->op_set,c); 
00805         break;
00806       }
00807       if ((*s)&0x80) match=!match;
00808       if (!match) return 0;
00809     }
00810     else if (getChar(i++)!=*s) return 0;
00811     s++;
00812   }
00813   *pos=i;
00814   return 1;
00815 }
00816 
00817 int matchEndSequence(char *s,int *pos)
00818 {
00819   if (*s==0) return 1;
00820   /* if terminator is \n and we're at end of input, let it be... */
00821   if (getChar(*pos)==0 && s[0]=='\n' && s[1]==0) return 1;
00822   if (!matchSequence(s,pos)) return 0;
00823   if (S->preservelf&&iswhite(getChar(*pos-1))) (*pos)--;
00824   return 1;
00825 }
00826 
00827 int matchStartSequence(char *s,int *pos)
00828 {
00829   char c;
00830   int match;
00831 
00832   if (!((*s)&0x60)) { /* special sequences from prev. context */
00833     c=getChar(*pos-1);
00834     match=1;
00835     if (*s==0) return 1;
00836     switch((*s)&0x1f) {
00837     case '\001':
00838       if ((c!=' ')&&(c!='\t')) {
00839         match=0;
00840         break;
00841       }
00842     case '\002':
00843       break;
00844     case '\003':
00845       if ((c!=' ')&&(c!='\t')&&(c!='\n')) {
00846         match=0;
00847         break;
00848       }
00849     case '\004':
00850       break;
00851     case '\006':
00852       if ((c==' ')||(c=='\t')||(c=='\n'))
00853         break;
00854     case '\005':
00855       match = ((c>='a')&&(c<='z')) || ((c>='A')&&(c<='Z'));
00856       break;
00857     case '\007':
00858       match = ((c>='0')&&(c<='9'));
00859       break;
00860     case '\010':
00861       match = IsInCharset(S->id_set,c);
00862       break;
00863     case '\011':
00864       match = (c=='\t');
00865       break;
00866     case '\012':
00867       match = (c=='\n');
00868       break;
00869     case '\013':
00870       match = IsInCharset(S->op_set,c);
00871       break;
00872     case '\014':
00873       match = IsInCharset(S->ext_op_set,c) || IsInCharset(S->op_set,c);
00874       break;
00875     }
00876     if ((*s)&0x80) match=!match;
00877     if (!match) return 0;
00878     s++;
00879   }    
00880   return matchSequence(s,pos);
00881 }
00882 
00883 void AddToCharset(CHARSET_SUBSET x,int c)
00884 {
00885   x[c>>LOG_LONG_BITS] |= 1L<<(c&((1<<LOG_LONG_BITS)-1));
00886 }
00887 
00888 CHARSET_SUBSET MakeCharsetSubset(unsigned char *s)
00889 {
00890   CHARSET_SUBSET x;
00891   int i;
00892   unsigned char c;
00893 
00894   x=(CHARSET_SUBSET) malloc(CHARSET_SUBSET_LEN*sizeof(unsigned long));
00895   for (i=0;i<CHARSET_SUBSET_LEN;i++) x[i]=0;
00896   while (*s!=0) {
00897     if (!((*s)&0x60)) { /* special sequences */
00898       if ((*s)&0x80) bug("negated special sequences not allowed in charset specifications");
00899       switch((*s)&0x1f) {
00900       case '\002':  /* \w, \W, \i, \o, \O not allowed */
00901       case '\004':
00902       case '\010':
00903       case '\013':
00904       case '\014':
00905         bug("special sequence not allowed in charset specification");
00906       case '\003':
00907         AddToCharset(x,'\n');
00908       case '\001':
00909         AddToCharset(x,' ');
00910       case '\011':
00911         AddToCharset(x,'\t');
00912         break;
00913       case '\006':
00914         AddToCharset(x,'\n');
00915         AddToCharset(x,' ');
00916         AddToCharset(x,'\t');
00917       case '\005':
00918         for (c='A';c<='Z';c++) AddToCharset(x,c);
00919         for (c='a';c<='z';c++) AddToCharset(x,c);
00920         break;
00921       case '\007':
00922         for (c='0';c<='9';c++) AddToCharset(x,c);
00923         break;
00924       case '\012':
00925         AddToCharset(x,'\n');
00926         break;
00927       }
00928     }
00929     else if ((s[1]=='-')&&((s[2]&0x60)!=0)&&(s[2]>=*s)) {
00930       for (c=*s;c<=s[2];c++) AddToCharset(x,c);
00931       s+=2;
00932     }
00933     else AddToCharset(x,*s);
00934     s++;
00935   }
00936   return x;
00937 }
00938 
00939 
00940 int idequal(char *b,int l,char *s)
00941 {
00942   int i;
00943   
00944   for (i=0;i<l;i++) if (b[i]!=s[i]) return 0;
00945   return (s[l]==0);
00946 }
00947 
00948 int findIdent(char *b,int l,int *h)
00949 {
00950   int i;
00951   
00952   if (*h<0) *h=hash_str(b,l);
00953   for (i=0;i<nmacros[*h];i++)
00954     if (idequal(b,l,macros[*h][i].username)) return i;
00955   return -1;
00956 }
00957 
00958 int findNamedArg(char *b,int l)
00959 {
00960   char *s; 
00961   int i;
00962 
00963   for (i=0;;i++) {
00964     s=C->namedargs[i];
00965     if (s==NULL) return -1;
00966     if (idequal(b,l,s)) return i;
00967   } 
00968 }
00969 
00970 void shiftIn(int l)
00971 {
00972   int i;
00973   
00974   if (l<=1) return;
00975   l--;
00976   if (l>=C->len) C->len=0;
00977   else {
00978     if (C->len-l>100) { /* we want to shrink that buffer */
00979       C->buf+=l; C->bufsize-=l;
00980     } else
00981       for (i=l;i<C->len;i++) C->buf[i-l]=C->buf[i];
00982     C->len-=l;
00983     C->eof=(C->buf[0]==0);
00984   }
00985   if (C->len<=1) {
00986     if (C->in==NULL) C->eof=1;
00987     else C->eof=feof(C->in);
00988   }
00989 }
00990 
00991 void initthings(int argc, char **argv)
00992 {
00993   char **arg,*s;
00994   int h,i,isinput,isoutput,ishelp,ismode,hasmeta,usrmode;
00995 
00996   DefaultOp=MakeCharsetSubset(DEFAULT_OP_STRING);
00997   PrologOp=MakeCharsetSubset(PROLOG_OP_STRING);
00998   FloraOp=MakeCharsetSubset(FLORA_OP_STRING);
00999   DefaultExtOp=MakeCharsetSubset(DEFAULT_OP_PLUS);
01000   DefaultId=MakeCharsetSubset(DEFAULT_ID_STRING);
01001 
01002   for (h=0;h<HASH_SIZE;h++) {
01003     nmacros[h]=0;
01004     nalloced[h]=1;
01005     macros[h]=(struct MACRO *)malloc(nalloced[h]*sizeof(struct MACRO));
01006   }
01007   S=(struct SPECS *)malloc(sizeof(struct SPECS));
01008   S->User=CUser;
01009   S->Meta=CMeta;
01010   S->comments=NULL;
01011   S->stack_next=NULL;
01012   S->preservelf=0;
01013   S->op_set=DefaultOp;
01014   S->ext_op_set=DefaultExtOp;
01015   S->id_set=DefaultId;
01016   S->readonly=0;
01017   
01018   C=(struct INPUTCONTEXT *)malloc(sizeof(struct INPUTCONTEXT));
01019   C->in=stdin;
01020   C->argc=0;
01021   C->argv=NULL;
01022   C->filename=strdup("stdin");
01023   C->out=(struct OUTPUTCONTEXT *)malloc(sizeof(struct OUTPUTCONTEXT));
01024   C->out->f=stdout;
01025   C->out->bufsize=0;
01026   C->lineno=0;
01027   isinput=isoutput=ismode=ishelp=hasmeta=usrmode=0;
01028   nincludedirs=0;
01029   C->bufsize=80;
01030   C->len=0;
01031   C->buf=C->malloced_buf=malloc(C->bufsize);
01032   C->eof=0;
01033   C->namedargs=NULL;
01034   C->in_comment=0;
01035   C->ambience=FLAG_TEXT;
01036   C->may_have_args=0;
01037   commented[0]=0;
01038   iflevel=0;
01039   execallowed=0;
01040   autoswitch=0;
01041   dosmode=DEFAULT_CRLF;
01042   
01043   for (arg=argv+1;*arg;arg++) {
01044     if (strcmp(*arg, "-nostdinc") == 0) {
01045       NoStdInc = 1;
01046       continue;
01047     }
01048     if (strcmp(*arg, "-nocurinc") == 0) {
01049       NoCurIncFirst = 1;
01050       continue;
01051     }
01052     if (strcmp(*arg, "-curdirinclast") == 0) {
01053       CurDirIncLast = 1;
01054       NoCurIncFirst = 1;
01055       continue;
01056     }
01057     if (strcmp(*arg, "-includemarker") == 0) {
01058       if (!(*(++arg))) usage();
01059       construct_include_directive_marker(&include_directive_marker, *arg);
01060       continue;
01061     }
01062 
01063     if (strcmp(*arg, "-warninglevel") == 0) {
01064       if (!(*(++arg))) usage();
01065       WarningLevel = atoi(*arg);
01066       continue;
01067     }
01068 
01069     if (**arg=='+') {
01070       switch((*arg)[1]) {
01071       case 'c':
01072         s=(*arg)+2;
01073         if (*s==0) s="ccc";
01074         if (!(*(++arg))) usage();
01075         if (!(*(++arg))) usage();
01076         add_comment(S,s,strnl(*(arg-1)),strnl(*arg),0,0);
01077         break;
01078       case 's':
01079         s=(*arg)+2;
01080         if (*s==0) s="sss";
01081         if (!(*(++arg))) usage();
01082         if (!(*(++arg))) usage();
01083         if (!(*(++arg))) usage();
01084         add_comment(S,s,strnl(*(arg-2)),strnl(*(arg-1)),**arg,0);
01085         break;
01086       case 'z': 
01087         dosmode=0;
01088         break;
01089       case 'n':
01090         S->preservelf=0;
01091         break;
01092       default: ishelp=1;
01093       }
01094     }
01095     else if (**arg!='-') {
01096       ishelp|=isinput; isinput=1;
01097       C->in=fopen(*arg,"r");
01098       free(C->filename); C->filename=strdup(*arg);
01099       if (C->in==NULL) bug("Cannot open input file");
01100     }
01101     else switch((*arg)[1]) {
01102     case 'I':
01103       if (nincludedirs==MAXINCL) 
01104         bug("too many include directories");
01105       if ((*arg)[2]==0) {
01106         if (!(*(++arg))) usage();
01107         includedir[nincludedirs++]=strdup(*arg);
01108       }
01109       else includedir[nincludedirs++]=strdup((*arg)+2);
01110       break;
01111     case 'C':
01112       ishelp|=ismode|hasmeta|usrmode; ismode=1;
01113       S->User=KUser; S->Meta=KMeta;
01114       S->preservelf=1;
01115       add_comment(S,"ccc",strdup("/*"),strdup("*/"),0,0);
01116       add_comment(S,"ccc",strdup("//"),strdup("\n"),0,0);
01117       add_comment(S,"ccc",strdup("\\\n"),strdup(""),0,0);
01118       add_comment(S,"sss",strdup("\""),strdup("\""),'\\','\n');
01119       add_comment(S,"sss",strdup("'"),strdup("'"),'\\','\n');
01120       break;
01121     case 'P':
01122       ishelp|=ismode|hasmeta|usrmode; ismode=1;
01123       S->User=KUser; S->Meta=KMeta;
01124       S->preservelf=1;
01125       S->op_set=PrologOp;
01126       add_comment(S,"css",strdup("\213/*"),strdup("*/"),0,0); /* \!o */
01127       add_comment(S,"cii",strdup("\\\n"),strdup(""),0,0);
01128       add_comment(S,"css",strdup("%"),strdup("\n"),0,0);
01129       add_comment(S,"sss",strdup("\""),strdup("\""),0,'\n');
01130       add_comment(S,"sss",strdup("\207'"),strdup("'"),0,'\n'); /* \!# */
01131       break;
01132     case 'F':
01133       ishelp|=ismode|hasmeta|usrmode; ismode=1;
01134       S->User=KUser; S->Meta=KMeta;
01135       S->preservelf=1;
01136       S->op_set=FloraOp;
01137       add_comment(S,"css",strdup("\213/*"),strdup("*/"),0,0); /* \!o */
01138       add_comment(S,"cii",strdup("\\\n"),strdup(""),0,0);
01139       add_comment(S,"css",strdup("//"),strdup("\n"),0,0);
01140       add_comment(S,"sss",strdup("\""),strdup("\""),'\\','\n');
01141       add_comment(S,"sss",strdup("\207'"),strdup("'"),'\\','\n'); /* \!# */
01142       break;
01143     case 'T':
01144       ishelp|=ismode|hasmeta|usrmode; ismode=1;
01145       S->User=S->Meta=Tex;
01146       break;
01147     case 'H':
01148       ishelp|=ismode|hasmeta|usrmode; ismode=1;
01149       S->User=S->Meta=Html;
01150       break;
01151     case 'U':
01152       ishelp|=ismode|usrmode; usrmode=1;
01153       if (!readModeDescription(arg,&(S->User),0))
01154           usage();
01155       arg+=9;
01156       if (!hasmeta) S->Meta=S->User;
01157       break;
01158     case 'M':
01159       ishelp|=ismode|hasmeta; hasmeta=1;
01160       if (!readModeDescription(arg,&(S->Meta),1))
01161           usage();
01162       arg+=7;
01163       break;
01164     case 'O':
01165       file_and_stdout = 1;
01166     case 'o':
01167       if (!(*(++arg)))
01168           usage();
01169       ishelp|=isoutput; isoutput=1;
01170       C->out->f=fopen(*arg,"w");
01171       if (C->out->f==NULL) bug("Cannot create output file");
01172       break;
01173     case 'D':
01174       if ((*arg)[2]==0) {
01175         if (!(*(++arg)))
01176           usage();
01177         s=strnl0(*arg);
01178       }
01179       else s=strnl0((*arg)+2);
01180       parseCmdlineDefine(s); free(s); break;
01181     case 'x':
01182       execallowed=1;
01183       break;
01184     case 'n':
01185       S->preservelf=1;
01186       break;
01187     case 'z':
01188       dosmode=1;
01189       break;
01190     case 'c':
01191     case 's':
01192       if (!(*(++arg)))
01193           usage();
01194       delete_comment(S,strnl(*arg));
01195       break;
01196     case 'm':
01197       autoswitch=1; break;
01198     default:
01199       ishelp=1;
01200     }
01201     if (hasmeta&&!usrmode) usage();
01202     if (ishelp) usage();
01203   }
01204 
01205 #ifndef WIN_NT
01206   if ((nincludedirs==0) && !NoStdInc) {
01207     includedir[0]=strdup("/usr/include");
01208     nincludedirs=1;
01209   }
01210 #endif
01211 
01212   for (h=0;h<HASH_SIZE;h++)
01213     for (i=0;i<nmacros[h];i++) {
01214       if (macros[h][i].define_specs == NULL) {
01215         macros[h][i].define_specs=CloneSpecs(S);
01216         macros[h][i].define_specs->readonly=1;
01217       }
01218       lookupArgRefs(&(macros[h][i])); /* for macro aliasing */
01219     }
01220 }
01221 
01222 int findCommentEnd(char *endseq,char quote,char warn,int pos,int flags)
01223 {
01224   int i;
01225   char c;
01226 
01227   while (1) {
01228     c=getChar(pos);
01229     i=pos;
01230     if (matchEndSequence(endseq,&i)) return pos;
01231     if (c==0) bug("Input ended while scanning a comment/string");
01232     if (c==warn) {
01233       warn=0;
01234       if (WarningLevel > 2)
01235         warning("possible comment/string termination problem");
01236     }
01237     if (c==quote) pos+=2;
01238     else if ((flags&PARSE_MACROS)&&(c==S->User.quotechar)) pos+=2;
01239     else pos++;
01240   }
01241 }
01242 
01243 void SkipPossibleComments(int *pos,int cmtmode,int silentonly)
01244 {
01245   int found;
01246   struct COMMENT *c;
01247   
01248   if (C->in_comment) return;
01249   do {
01250     found=0;
01251     if (getChar(*pos)==0) return; /* EOF */
01252     for (c=S->comments;c!=NULL;c=c->next)
01253       if (!(c->flags[cmtmode]&FLAG_IGNORE))
01254         if (!silentonly||(c->flags[cmtmode]==FLAG_COMMENT))
01255           if (matchStartSequence(c->start,pos)) {
01256             *pos=findCommentEnd(c->end,c->quote,c->warn,*pos,c->flags[cmtmode]);
01257             matchEndSequence(c->end,pos);
01258             found=1;
01259             break;
01260           }
01261   } while (found);
01262 }
01263 
01264 /* look for a possible user macro.
01265    Input :  idstart = scan start
01266             idcheck = check id for long macro forms before splicing args ?
01267             cmtmode = comment mode (FLAG_META or FLAG_USER)
01268    Output : idstart/idend = macro name location
01269             sh_end/lg_end = macro form end (-1 if no match)
01270             argb/arge     = argument locations for long form
01271             argc          = argument count for long form
01272             id            = macro id, if idcheck was set at input 
01273             hash          = macro name hash, if idcheck was set at input
01274 */
01275 
01276 int SplicePossibleUser(int *idstart,int *idend,int *sh_end,int *lg_end,
01277                        int *argb,int *arge,int *argc,int idcheck,
01278                        int *id,int cmtmode,int *hash)
01279 {
01280   int match,k,pos;
01281 
01282   if (!matchStartSequence(S->User.mStart,idstart)) return 0;
01283   *idend=identifierEnd(*idstart);
01284   if ((*idend)&&!getChar(*idend-1)) return 0;
01285   
01286   /* look for args or no args */
01287   *sh_end=*idend;
01288   if (!matchEndSequence(S->User.mEnd,sh_end)) *sh_end=-1;
01289   pos=*idend;
01290   match=matchSequence(S->User.mArgS,&pos);
01291     
01292   if (idcheck) {
01293     *hash=-1;
01294     *id=findIdent(C->buf+*idstart,*idend-*idstart,hash);
01295     if (*id<0) match=0;
01296   }
01297   *lg_end=-1;
01298   
01299   if (match) {
01300     *argc=0;
01301     while (1) {
01302       if (*argc>=MAXARGS) bug("too many macro parameters");
01303       argb[*argc]=pos;
01304       k=0;
01305       while(1) { /* look for mArgE, mArgSep, or comment-start */
01306         pos=iterIdentifierEnd(pos);
01307         SkipPossibleComments(&pos,cmtmode,0);
01308         if (getChar(pos)==0) return (*sh_end>=0); /* EOF */
01309         if (strchr(S->User.stackchar,getChar(pos))) k++;
01310         if (k) { if (strchr(S->User.unstackchar,getChar(pos))) k--; }
01311         else {
01312           arge[*argc]=pos;
01313           if (matchSequence(S->User.mArgSep,&pos)) { match=0; break; }
01314           if (matchEndSequence(S->User.mArgE,&pos)) 
01315             { match=1; break; }
01316         }
01317         pos++; /* nothing matched, go forward */
01318       }
01319       (*argc)++;
01320       if (match) { /* no more args */
01321         *lg_end=pos;
01322         break;
01323       }
01324     }
01325   }
01326   return ((*lg_end>=0)||(*sh_end>=0));
01327 }
01328 
01329 int findMetaArgs(int start,int *p1b,int *p1e,int *p2b,int *p2e,int *endm,int *argc,int *argb,int *arge)
01330 {
01331   int pos,k;
01332   int hyp_end1,hyp_end2;
01333   
01334   /* look for mEnd or mArgS */
01335   pos=start;
01336   if (!matchSequence(S->Meta.mArgS,&pos)) {
01337     if (!matchEndSequence(S->Meta.mEnd,&pos)) return -1;
01338     *endm=pos;
01339     return 0;
01340   }
01341   *p1b=pos;
01342 
01343   /* special syntax for #define : 1st arg is a macro call */
01344   if ((*argc)&&SplicePossibleUser(&pos,p1e,&hyp_end1,&hyp_end2,
01345                                   argb,arge,argc,0,NULL,FLAG_META,NULL)) {
01346     *p1b=pos;
01347     if (hyp_end2>=0) pos=hyp_end2; else { pos=hyp_end1; *argc=0; }
01348     if (!matchSequence(S->Meta.mArgSep,&pos)) {
01349       if (!matchEndSequence(S->Meta.mArgE,&pos))
01350         bug("#define/#defeval requires an identifier or a single macro call");
01351       *endm=pos;
01352       return 1;
01353     }
01354   } else {
01355     *argc=0;
01356     k=0;
01357     while(1) { /* look for mArgE, mArgSep, or comment-start */
01358       pos=iterIdentifierEnd(pos);
01359       SkipPossibleComments(&pos,FLAG_META,0);
01360       if (getChar(pos)!=0 && strchr(S->Meta.stackchar,getChar(pos))) k++;
01361       if (k) { 
01362         if (getChar(pos)!=0 && strchr(S->Meta.unstackchar,getChar(pos))) 
01363           k--;
01364       } else {
01365         *p1e=pos;
01366         if (matchSequence(S->Meta.mArgSep,&pos)) break;
01367         if (matchEndSequence(S->Meta.mArgE,&pos)) {
01368           *endm=pos;
01369           return 1;
01370         }
01371       }
01372       if (getChar(pos)==0) bug("unfinished macro argument");
01373       pos++; /* nothing matched, go forward */
01374     }
01375   }
01376   
01377   *p2b=pos;
01378   k=0;
01379   while(1) { /* look for mArgE or comment-start */
01380     pos=iterIdentifierEnd(pos);
01381     SkipPossibleComments(&pos,FLAG_META,0);
01382     if (getChar(pos)!=0 && strchr(S->Meta.stackchar,getChar(pos))) k++;
01383     if (k) { 
01384       if (getChar(pos)!=0 && strchr(S->Meta.unstackchar,getChar(pos))) 
01385         k--;
01386     } else {
01387       *p2e=pos;
01388       if (matchEndSequence(S->Meta.mArgE,&pos)) break;
01389     }
01390     if (getChar(pos)==0) bug("unfinished macro");
01391     pos++; /* nothing matched, go forward */
01392   }
01393   *endm=pos;
01394   return 2;
01395 }
01396 
01397 char *ProcessText(char *buf,int l,int ambience)
01398 {
01399   char *s;
01400   struct INPUTCONTEXT *T;
01401   
01402   if (l==0) { s=malloc(1); s[0]=0; return s;  }
01403   s=malloc(l+2);
01404   s[0]='\n';
01405   memcpy(s+1,buf,l);
01406   s[l+1]=0;
01407   T=C;
01408   C=(struct INPUTCONTEXT *)malloc(sizeof(struct INPUTCONTEXT));
01409   C->out=(struct OUTPUTCONTEXT *)malloc(sizeof(struct OUTPUTCONTEXT));
01410   C->in=NULL;
01411   C->argc=T->argc;
01412   C->argv=T->argv;
01413   C->filename=T->filename;
01414   C->out->buf=malloc(80);
01415   C->out->len=0;
01416   C->out->bufsize=80;
01417   C->out->f=NULL;
01418   C->lineno=T->lineno;
01419   C->bufsize=l+2;
01420   C->len=l+1;
01421   C->buf=C->malloced_buf=s;
01422   C->eof=0;
01423   C->namedargs=T->namedargs;
01424   C->in_comment=T->in_comment;
01425   C->ambience=ambience;
01426   C->may_have_args=T->may_have_args;
01427   
01428   ProcessContext();
01429   outchar(0); /* note that outchar works with the half-destroyed context ! */
01430   s=C->out->buf;
01431   free(C->out);
01432   free(C);
01433   C=T;
01434   return s;
01435 }
01436 
01437 char *ProcessFastDefinition(char *buf,int l,char **argnames)
01438 {
01439   char *s;
01440   char *argval[9],argval2[16];
01441   int i;
01442   struct INPUTCONTEXT *T;
01443   
01444   if (l==0) { s=malloc(1); s[0]=0; return s;  }
01445   s=malloc(l+2);
01446   s[0]='\n';
01447   memcpy(s+1,buf,l);
01448   s[l+1]=0;
01449   for (i=0;i<8;i++) 
01450     { argval[i]=argval2+2*i; argval2[2*i]=(char)(i+1); argval2[2*i+1]=0; }
01451   argval[8]=NULL;
01452   T=C;
01453   C=(struct INPUTCONTEXT *)malloc(sizeof(struct INPUTCONTEXT));
01454   C->out=(struct OUTPUTCONTEXT *)malloc(sizeof(struct OUTPUTCONTEXT));
01455   C->in=NULL;
01456   C->argc=8;
01457   C->argv=argval;
01458   C->filename=T->filename;
01459   C->out->buf=malloc(80);
01460   C->out->len=0;
01461   C->out->bufsize=80;
01462   C->out->f=NULL;
01463   C->lineno=T->lineno;
01464   C->bufsize=l+2;
01465   C->len=l+1;
01466   C->buf=C->malloced_buf=s;
01467   C->eof=0;
01468   C->namedargs=argnames;
01469   C->in_comment=T->in_comment;
01470   C->ambience=FLAG_META;
01471   C->may_have_args=1;
01472   
01473   ProcessContext();
01474   outchar(0); /* note that outchar works with the half-destroyed context ! */
01475   s=C->out->buf;
01476   free(C->out);
01477   free(C);
01478   C=T;
01479   return s;
01480 }
01481 
01482 int SpliceInfix(char *buf,int pos1,int pos2,char *sep,int *spl1,int *spl2)
01483 {
01484   int pos,numpar,l;
01485   char *p;
01486   
01487   numpar=0; l=strlen(sep);
01488   for (pos=pos2-1,p=buf+pos;pos>=pos1;pos--,p--) {
01489     if (*p==')') numpar++;
01490     if (*p=='(') numpar--;
01491     if (numpar<0) return 0;
01492     if ((numpar==0)&&(pos2-pos>=l)&&!strncmp(p,sep,l))
01493       { *spl1=pos; *spl2=pos+l; return 1; }
01494   }
01495   return 0;
01496 }
01497 
01498 int DoArithmEval(char *buf,int pos1,int pos2,int *result)
01499 {
01500   int spl1,spl2,result1,result2,l;
01501   char c,*p;
01502   
01503   while ((pos1<pos2)&&iswhite(buf[pos1])) pos1++;
01504   while ((pos1<pos2)&&iswhite(buf[pos2-1])) pos2--;
01505   if (pos1==pos2) return 0;
01506   
01507   /* look for C operators starting with lowest precedence */
01508   
01509   if (SpliceInfix(buf,pos1,pos2,"||",&spl1,&spl2)) {
01510     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01511         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01512     *result=result1||result2;
01513     return 1;
01514   }
01515 
01516   if (SpliceInfix(buf,pos1,pos2,"&&",&spl1,&spl2)) {
01517     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01518         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01519     *result=result1&&result2;
01520     return 1;
01521   }
01522 
01523   if (SpliceInfix(buf,pos1,pos2,"|",&spl1,&spl2)) {
01524     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01525         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01526     *result=result1|result2;
01527     return 1;
01528   }
01529 
01530   if (SpliceInfix(buf,pos1,pos2,"^",&spl1,&spl2)) {
01531     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01532         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01533     *result=result1^result2;
01534     return 1;
01535   }
01536 
01537   if (SpliceInfix(buf,pos1,pos2,"&",&spl1,&spl2)) {
01538     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01539         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01540     *result=result1&result2;
01541     return 1;
01542   }
01543 
01544   if (SpliceInfix(buf,pos1,pos2,"!=",&spl1,&spl2)) {
01545     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01546         !DoArithmEval(buf,spl2,pos2,&result2)) {
01547       /* revert to string comparison */
01548       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
01549       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
01550       if (spl1-pos1!=pos2-spl2) *result=1;
01551       else *result=(strncmp(buf+pos1,buf+spl2,spl1-pos1)!=0);
01552     }  
01553     else *result=(result1!=result2);
01554     return 1;
01555   }
01556   
01557   if (SpliceInfix(buf,pos1,pos2,"==",&spl1,&spl2)) {
01558     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01559         !DoArithmEval(buf,spl2,pos2,&result2)) {
01560       /* revert to string comparison */
01561       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
01562       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
01563       if (spl1-pos1!=pos2-spl2) *result=0;
01564       else *result=(strncmp(buf+pos1,buf+spl2,spl1-pos1)==0);
01565     }  
01566     else *result=(result1==result2);
01567     return 1;
01568   }
01569 
01570   if (SpliceInfix(buf,pos1,pos2,">=",&spl1,&spl2)) {
01571     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01572         !DoArithmEval(buf,spl2,pos2,&result2)) {
01573       /* revert to string comparison */
01574       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
01575       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
01576       l=spl1-pos1; if (l>pos2-spl2) l=pos2-spl2;
01577       result1=strncmp(buf+pos1,buf+spl2,l);
01578       *result=(result1>0) || ((result1==0) && (spl1-pos1>=pos2-spl2));
01579     }  
01580     else *result=(result1>=result2);
01581     return 1;
01582   }
01583 
01584   if (SpliceInfix(buf,pos1,pos2,">",&spl1,&spl2)) {
01585     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01586         !DoArithmEval(buf,spl2,pos2,&result2)) {
01587       /* revert to string comparison */
01588       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
01589       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
01590       l=spl1-pos1; if (l>pos2-spl2) l=pos2-spl2;
01591       result1=strncmp(buf+pos1,buf+spl2,l);
01592       *result=(result1>0) || ((result1==0) && (spl1-pos1>pos2-spl2));
01593     }  
01594     else *result=(result1>result2);
01595     return 1;
01596   }
01597   
01598   if (SpliceInfix(buf,pos1,pos2,"<=",&spl1,&spl2)) {
01599     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01600         !DoArithmEval(buf,spl2,pos2,&result2)) {
01601       /* revert to string comparison */
01602       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
01603       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
01604       l=spl1-pos1; if (l>pos2-spl2) l=pos2-spl2;
01605       result1=strncmp(buf+pos1,buf+spl2,l);
01606       *result=(result1<0) || ((result1==0) && (spl1-pos1<=pos2-spl2));
01607     }  
01608     else *result=(result1<=result2);
01609     return 1;
01610   }
01611 
01612   if (SpliceInfix(buf,pos1,pos2,"<",&spl1,&spl2)) {
01613     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01614         !DoArithmEval(buf,spl2,pos2,&result2)) {
01615       /* revert to string comparison */
01616       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
01617       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
01618       l=spl1-pos1; if (l>pos2-spl2) l=pos2-spl2;
01619       result1=strncmp(buf+pos1,buf+spl2,l);
01620       *result=(result1<0) || ((result1==0) && (spl1-pos1<pos2-spl2));
01621     }  
01622     else *result=(result1<result2);
01623     return 1;
01624   }
01625 
01626   if (SpliceInfix(buf,pos1,pos2,"+",&spl1,&spl2)) {
01627     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01628         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01629     *result=result1+result2;
01630     return 1;
01631   }
01632 
01633   if (SpliceInfix(buf,pos1,pos2,"-",&spl1,&spl2))
01634     if (spl1!=pos1) {
01635       if (!DoArithmEval(buf,pos1,spl1,&result1)||
01636           !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01637       *result=result1-result2;
01638       return 1;
01639     }
01640 
01641   if (SpliceInfix(buf,pos1,pos2,"*",&spl1,&spl2)) {
01642     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01643         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01644     *result=result1*result2;
01645     return 1;
01646   }
01647 
01648   if (SpliceInfix(buf,pos1,pos2,"/",&spl1,&spl2)) {
01649     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01650         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01651     if (result2==0) bug("Division by zero in expression");
01652     *result=result1/result2;
01653     return 1;
01654   }
01655 
01656   if (SpliceInfix(buf,pos1,pos2,"%",&spl1,&spl2)) {
01657     if (!DoArithmEval(buf,pos1,spl1,&result1)||
01658         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
01659     if (result2==0) bug("Division by zero in expression");
01660     *result=result1%result2;
01661     return 1;
01662   }
01663 
01664   if (buf[pos1]=='~') {
01665     if (!DoArithmEval(buf,pos1+1,pos2,&result1)) return 0;
01666     *result=~result1;
01667     return 1;
01668   }
01669 
01670   if (buf[pos1]=='!') {
01671     if (!DoArithmEval(buf,pos1+1,pos2,&result1)) return 0;
01672     *result=!result1;
01673     return 1;
01674   }
01675 
01676   if (buf[pos1]=='-') {
01677     if (!DoArithmEval(buf,pos1+1,pos2,&result1)) return 0;
01678     *result=-result1;
01679     return 1;
01680   }
01681 
01682   /* add the length() builtin to measure the length of the macro expansion */
01683   if (strncmp(buf+pos1,"length(",strlen("length("))==0) {
01684     if (buf[pos2-1]!=')') return 0;
01685     *result=pos2-pos1-strlen("length()");
01686     return 1;
01687   }
01688   
01689   if (buf[pos1]=='(') {
01690     if (buf[pos2-1]!=')') return 0;
01691     return DoArithmEval(buf,pos1+1,pos2-1,result);
01692   }
01693   
01694   c=buf[pos2]; buf[pos2]=0;
01695   *result=(int)strtol(buf+pos1,&p,0);
01696   buf[pos2]=c;
01697   return (p==buf+pos2);
01698 }
01699 
01700 void delete_macro(int h,int i)
01701 {
01702   int j;
01703   nmacros[h]--;
01704   free(macros[h][i].username);
01705   free(macros[h][i].macrotext); 
01706   if (macros[h][i].argnames!=NULL) {
01707     for (j=0;j<macros[h][i].nnamedargs;j++) free(macros[h][i].argnames[j]);
01708     free(macros[h][i].argnames);
01709     macros[h][i].argnames=NULL;
01710   }
01711   if (macros[h][i].define_specs->stack_next!=NULL) /* in use ! */
01712     macros[h][i].define_specs->readonly=0;
01713   else {
01714     FreeComments(macros[h][i].define_specs);
01715     free(macros[h][i].define_specs);
01716   }
01717   memcpy((char *)(macros[h]+i),(char *)(macros[h]+nmacros[h]),sizeof(struct MACRO));
01718 }
01719 
01720 char *ArithmEval(int pos1,int pos2)
01721 {
01722   char *s,*t;
01723   int i,h;
01724   struct MACRO *m;
01725   
01726   /* first define the defined(...) operator */
01727   h=-1;
01728   i=findIdent("defined",strlen("defined"),&h);
01729   if (i>=0) warning("the defined(...) macro is already defined");
01730   else {
01731     m=newmacro("defined",strlen("defined"),1,h);
01732     m->macrolen=0;
01733     m->macrotext=malloc(1);
01734     m->macrotext[0]=0;
01735     m->nnamedargs=-2; /* trademark of the defined(...) macro */
01736   }
01737   /* process the text in a usual way */
01738   s=ProcessText(C->buf+pos1,pos2-pos1,FLAG_META);
01739   /* undefine the defined(...) operator */
01740   if (i<0) {
01741     i=findIdent("defined",strlen("defined"),&h);
01742     if ((i<0)||(macros[h][i].nnamedargs!=-2))
01743       warning("the defined(...) macro was redefined in expression");
01744     else delete_macro(h,i);
01745   }
01746 
01747   if (!DoArithmEval(s,0,strlen(s),&i)) return s; /* couldn't compute */
01748   t=malloc(MAX_GPP_NUM_SIZE);
01749   sprintf(t,"%d",i);
01750   free(s);
01751   return t;
01752 }
01753 
01754 int comment_or_white(int start,int end,int cmtmode)
01755 {
01756   char c;
01757   
01758   while (start<end) {
01759     SkipPossibleComments(&start,cmtmode,1);
01760     if (start<end) {
01761       c=getChar(start++);
01762       if ((c!=' ')&&(c!='\n')&&(c!='\t')) return 0;
01763     }
01764   }
01765   return 1;
01766 }
01767 
01768 char *remove_comments(int start,int end,int cmtmode)
01769 {
01770   char *s,*t;
01771   
01772   t=s=malloc(end-start+1);
01773   while (start<end) {
01774     SkipPossibleComments(&start,cmtmode,1);
01775     if (start<end) {
01776       *t=getChar(start++);
01777       if ((*t==S->User.quotechar)&&(start<end)) { *(++t)=getChar(start++); }
01778       t++;
01779     }
01780   }
01781   *t=0;
01782   return s;
01783 }
01784 
01785 void SetStandardMode(struct SPECS *P,char *opt) 
01786 {
01787   P->op_set=DefaultOp;
01788   P->ext_op_set=DefaultExtOp;
01789   P->id_set=DefaultId;
01790   FreeComments(P);
01791   if (!strcmp(opt,"C")||!strcmp(opt,"cpp")) {
01792     P->User=KUser; P->Meta=KMeta;
01793     P->preservelf=1;
01794     add_comment(P,"ccc",strdup("/*"),strdup("*/"),0,0);
01795     add_comment(P,"ccc",strdup("//"),strdup("\n"),0,0);
01796     add_comment(P,"ccc",strdup("\\\n"),strdup(""),0,0);
01797     add_comment(P,"sss",strdup("\""),strdup("\""),'\\','\n');
01798     add_comment(P,"sss",strdup("'"),strdup("'"),'\\','\n');
01799   }
01800   else if (!strcmp(opt,"TeX")||!strcmp(opt,"tex")) {
01801     P->User=Tex; P->Meta=Tex;
01802     P->preservelf=0;
01803   }
01804   else if (!strcmp(opt,"HTML")||!strcmp(opt,"html")) {
01805     P->User=Html; P->Meta=Html;
01806     P->preservelf=0;
01807   }
01808   else if (!strcmp(opt,"default")) {
01809     P->User=CUser; P->Meta=CMeta;
01810     P->preservelf=0;
01811   }
01812   else if (!strcmp(opt,"Prolog")||!strcmp(opt,"prolog")) {
01813     P->User=KUser; P->Meta=KMeta;
01814     P->preservelf=1;
01815     P->op_set=PrologOp;
01816     add_comment(P,"css",strdup("\213/*"),strdup("*/"),0,0); /* \!o */ 
01817     add_comment(P,"cii",strdup("\\\n"),strdup(""),0,0);
01818     add_comment(P,"css",strdup("%"),strdup("\n"),0,0);
01819     add_comment(P,"sss",strdup("\""),strdup("\""),0,'\n');
01820     add_comment(P,"sss",strdup("\207'"),strdup("'"),0,'\n');   /* \!# */
01821   }
01822   else if (!strcmp(opt,"Flora")||!strcmp(opt,"flora")) {
01823     P->User=KUser; P->Meta=KMeta;
01824     P->preservelf=1;
01825     P->op_set=FloraOp;
01826     add_comment(P,"css",strdup("\213/*"),strdup("*/"),0,0); /* \!o */ 
01827     add_comment(P,"cii",strdup("\\\n"),strdup(""),0,0);
01828     add_comment(P,"css",strdup("//"),strdup("\n"),0,0);
01829     add_comment(P,"sss",strdup("\""),strdup("\""),0,'\n');
01830     add_comment(P,"sss",strdup("\207'"),strdup("'"),0,'\n');   /* \!# */
01831   }
01832   else bug("unknown standard mode");
01833 }
01834 
01835 void ProcessModeCommand(int p1start,int p1end,int p2start,int p2end)
01836 {
01837   struct SPECS *P;
01838   char *s,*p,*opt;
01839   int nargs,check_isdelim;
01840   char *args[10]; /* can't have more than 10 arguments */
01841   
01842   whiteout(&p1start,&p1end);
01843   if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
01844     bug("invalid #mode syntax");
01845   if (p2start<0) s=strdup("");
01846   else s=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
01847 
01848   /* argument parsing */
01849   p=s; opt=NULL;
01850   while (iswhite(*p)) p++;
01851   if ((*p!='"')&&(*p!=0)) {
01852     opt=p;
01853     while ((*p!=0)&&!iswhite(*p)) p++;
01854     if (*p!=0) {
01855       *(p++)=0;
01856       while (iswhite(*p)) p++;
01857     }
01858   }
01859   nargs=0;
01860   check_isdelim=!idequal(C->buf+p1start,p1end-p1start,"charset");
01861   while (*p!=0) {
01862     if (nargs==10) bug("too many arguments in #mode command");
01863     if (*(p++)!='"') bug("syntax error in #mode command (missing \" or trailing data)");
01864     args[nargs++]=p;
01865     p=strnl2(p,check_isdelim);
01866     while (iswhite(*p)) p++;
01867   }
01868 
01869   if (idequal(C->buf+p1start,p1end-p1start,"save")
01870            ||idequal(C->buf+p1start,p1end-p1start,"push")) {
01871     if ((opt!=NULL)||nargs) bug("too many arguments to #mode save");
01872     P=CloneSpecs(S->stack_next);
01873     P->stack_next=S->stack_next;
01874     S->stack_next=P;
01875     free(s);
01876     return;
01877   }
01878   if (idequal(C->buf+p1start,p1end-p1start,"restore")
01879            ||idequal(C->buf+p1start,p1end-p1start,"pop")) {
01880     if ((opt!=NULL)||nargs) bug("too many arguments to #mode restore");
01881     P=S->stack_next->stack_next;
01882     if (P==NULL) bug("#mode restore without #mode save");
01883     if (S->stack_next->readonly) S->stack_next->stack_next=NULL;
01884     else {
01885       FreeComments(S->stack_next);
01886       free(S->stack_next);
01887     }
01888     S->stack_next=P;
01889     free(s);
01890     return;
01891   }
01892 
01893   if (S->stack_next->readonly) { /* we must duplicate it first */
01894     P=CloneSpecs(S->stack_next);
01895     P->stack_next=S->stack_next->stack_next;
01896     S->stack_next->stack_next=NULL;
01897     S->stack_next=P;
01898   }
01899 
01900   if (idequal(C->buf+p1start,p1end-p1start,"quote")) {
01901     if (opt||(nargs>1)) bug("syntax error in #mode quote command");
01902     if (nargs==0) args[0]="";
01903     S->stack_next->User.quotechar=args[0][0];
01904   }
01905   else if (idequal(C->buf+p1start,p1end-p1start,"comment")) {
01906     if ((nargs<2)||(nargs>4)) bug("syntax error in #mode comment command");
01907     if (!opt) opt="ccc";
01908     if (nargs<3) args[2]="";
01909     if (nargs<4) args[3]="";
01910     add_comment(S->stack_next,opt,strdup(args[0]),strdup(args[1]),args[2][0],args[3][0]);
01911   }
01912   else if (idequal(C->buf+p1start,p1end-p1start,"string")) {
01913     if ((nargs<2)||(nargs>4)) bug("syntax error in #mode string command");
01914     if (!opt) opt="sss";
01915     if (nargs<3) args[2]="";
01916     if (nargs<4) args[3]="";
01917     add_comment(S->stack_next,opt,strdup(args[0]),strdup(args[1]),args[2][0],args[3][0]);
01918   }
01919   else if (idequal(C->buf+p1start,p1end-p1start,"standard")) {
01920     if ((opt==NULL)||nargs) bug("syntax error in #mode standard");
01921     SetStandardMode(S->stack_next,opt);
01922   }
01923   else if (idequal(C->buf+p1start,p1end-p1start,"user")) {
01924     if ((opt!=NULL)||(nargs!=9)) bug("#mode user requires 9 arguments");
01925     S->stack_next->User.mStart=strdup(args[0]);
01926     S->stack_next->User.mEnd=strdup(args[1]);
01927     S->stack_next->User.mArgS=strdup(args[2]);
01928     S->stack_next->User.mArgSep=strdup(args[3]);
01929     S->stack_next->User.mArgE=strdup(args[4]);
01930     S->stack_next->User.stackchar=strdup(args[5]);
01931     S->stack_next->User.unstackchar=strdup(args[6]);
01932     S->stack_next->User.mArgRef=strdup(args[7]);
01933     S->stack_next->User.quotechar=args[8][0];
01934   }
01935   else if (idequal(C->buf+p1start,p1end-p1start,"meta")) {
01936     if ((opt!=NULL)&&!nargs&&!strcmp(opt,"user")) 
01937       S->stack_next->Meta=S->stack_next->User;
01938     else {
01939       if ((opt!=NULL)||(nargs!=7)) bug("#mode meta requires 7 arguments");
01940       S->stack_next->Meta.mStart=strdup(args[0]);
01941       S->stack_next->Meta.mEnd=strdup(args[1]);
01942       S->stack_next->Meta.mArgS=strdup(args[2]);
01943       S->stack_next->Meta.mArgSep=strdup(args[3]);
01944       S->stack_next->Meta.mArgE=strdup(args[4]);
01945       S->stack_next->Meta.stackchar=strdup(args[5]);
01946       S->stack_next->Meta.unstackchar=strdup(args[6]);
01947     }
01948   }
01949   else if (idequal(C->buf+p1start,p1end-p1start,"preservelf")) {
01950     if ((opt==NULL)||nargs) bug("syntax error in #mode preservelf");
01951     if (!strcmp(opt,"1")||!strcasecmp(opt,"on")) S->stack_next->preservelf=1;
01952     else if (!strcmp(opt,"0")||!strcasecmp(opt,"off")) S->stack_next->preservelf=0;
01953     else bug("#mode preservelf requires on/off argument");
01954   }
01955   else if (idequal(C->buf+p1start,p1end-p1start,"nocomment")
01956            ||idequal(C->buf+p1start,p1end-p1start,"nostring")) {
01957     if ((opt!=NULL)||(nargs>1))
01958       bug("syntax error in #mode nocomment/nostring");
01959     if (nargs==0) FreeComments(S->stack_next);
01960     else delete_comment(S->stack_next,strdup(args[0]));
01961   }
01962   else if (idequal(C->buf+p1start,p1end-p1start,"charset")) {
01963     if ((opt==NULL)||(nargs!=1)) bug("syntax error in #mode charset");
01964     if (!strcasecmp(opt,"op"))
01965       S->stack_next->op_set=MakeCharsetSubset((unsigned char *)args[0]);
01966     else if (!strcasecmp(opt,"par"))
01967       S->stack_next->ext_op_set=MakeCharsetSubset((unsigned char *)args[0]);
01968     else if (!strcasecmp(opt,"id"))
01969       S->stack_next->id_set=MakeCharsetSubset((unsigned char *)args[0]);
01970     else bug("unknown charset subset name in #mode charset");
01971   }
01972   else bug("unrecognized #mode command");
01973        
01974   free(s);
01975 }
01976 
01977 int ParsePossibleMeta()
01978 {
01979   int cklen,nameend;
01980   int id,expparams,nparam,i,j,h;
01981   int p1start,p1end,p2start,p2end,macend;
01982   int argc,argb[MAXARGS],arge[MAXARGS];
01983   char *tmpbuf,**tmpargs;
01984   struct MACRO *m;
01985 
01986   cklen=1;
01987   if (!matchStartSequence(S->Meta.mStart,&cklen)) return -1;  
01988   nameend=identifierEnd(cklen);
01989   if (nameend&&!getChar(nameend-1)) return -1;
01990   id=0;
01991   argc=0; /* for #define with named args */
01992   if (idequal(C->buf+cklen,nameend-cklen,"define"))      /* check identifier */
01993     { id=1; expparams=2; argc=1; }
01994   else if (idequal(C->buf+cklen,nameend-cklen,"undef"))
01995     { id=2; expparams=1; }
01996   else if (idequal(C->buf+cklen,nameend-cklen,"ifdef"))
01997     { id=3; expparams=1; }
01998   else if (idequal(C->buf+cklen,nameend-cklen,"ifndef"))
01999     { id=4; expparams=1; }
02000   else if (idequal(C->buf+cklen,nameend-cklen,"else"))
02001     { id=5; expparams=0; }
02002   else if (idequal(C->buf+cklen,nameend-cklen,"endif"))
02003     { id=6; expparams=0; }
02004   else if (idequal(C->buf+cklen,nameend-cklen,"include"))
02005     { id=7; expparams=1; }
02006   else if (idequal(C->buf+cklen,nameend-cklen,"exec"))
02007     { id=8; expparams=1; }
02008   else if (idequal(C->buf+cklen,nameend-cklen,"defeval"))
02009     { id=9; expparams=2; argc=1; }
02010   else if (idequal(C->buf+cklen,nameend-cklen,"ifeq"))
02011     { id=10; expparams=2; }
02012   else if (idequal(C->buf+cklen,nameend-cklen,"ifneq"))
02013     { id=11; expparams=2; }
02014   else if (idequal(C->buf+cklen,nameend-cklen,"eval"))
02015     { id=12; expparams=1; }
02016   else if (idequal(C->buf+cklen,nameend-cklen,"if"))
02017     { id=13; expparams=1; }
02018   else if (idequal(C->buf+cklen,nameend-cklen,"mode"))
02019     { id=14; expparams=2; }
02020   else if (idequal(C->buf+cklen,nameend-cklen,"elif"))
02021     { id=15; expparams=1; }
02022   else if (idequal(C->buf+cklen,nameend-cklen,"deffast"))
02023     { id=16; expparams=2; argc=1; }
02024   else return -1;
02025 
02026   /* #MODE magic : define "..." to be C-style strings */
02027   if (id==14) {
02028     PushSpecs(S);
02029     S->preservelf=1;
02030     delete_comment(S,strdup("\""));
02031     add_comment(S,"sss",strdup("\""),strdup("\""),'\\','\n');
02032   }
02033 
02034   nparam=findMetaArgs(nameend,&p1start,&p1end,&p2start,&p2end,&macend,&argc,argb,arge);
02035   if (nparam==-1) return -1; 
02036 
02037   if ((nparam==2)&&iswhitesep(S->Meta.mArgSep))
02038     if (comment_or_white(p2start,p2end,FLAG_META)) nparam=1;
02039   if ((nparam==1)&&iswhitesep(S->Meta.mArgS))
02040     if (comment_or_white(p1start,p1end,FLAG_META)) nparam=0;
02041   if (expparams&&!nparam) bug("Missing argument in meta-macro");
02042   
02043   h=-1;
02044 
02045   switch(id) {
02046   case 1: /* DEFINE */
02047     if (!commented[iflevel]) {
02048       whiteout(&p1start,&p1end); /* recall comments are not allowed here */
02049       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end)) 
02050         bug("#define requires an identifier (A-Z,a-z,0-9,_ only)");
02051       /* buf starts 1 char before the macro */
02052       i=findIdent(C->buf+p1start,p1end-p1start,&h);
02053       if (i>=0) delete_macro(h,i);
02054       m=newmacro(C->buf+p1start,p1end-p1start,1,h);
02055       if (nparam==1) { p2end=p2start=p1end; }
02056       replace_definition_with_blank_lines(C->buf+1,C->buf+p2end,S->preservelf);
02057       m->macrotext=remove_comments(p2start,p2end,FLAG_META);
02058       m->macrolen=strlen(m->macrotext);
02059       m->defined_in_comment=C->in_comment;
02060 
02061       if (argc) {
02062         for (j=0;j<argc;j++) whiteout(argb+j,arge+j);
02063         /* define with one empty argument */
02064         if ((argc==1)&&(arge[0]==argb[0])) argc=0;
02065         m->argnames=(char **)malloc((argc+1)*sizeof(char *));
02066         m->argnames[argc]=NULL;
02067       }
02068       m->nnamedargs=argc;
02069       for (j=0;j<argc;j++) {
02070         if ((argb[j]==arge[j])||(identifierEnd(argb[j])!=arge[j]))
02071           bug("#define with named args needs identifiers as arg names");
02072         m->argnames[j]=malloc(arge[j]-argb[j]+1);
02073         memcpy(m->argnames[j],C->buf+argb[j],arge[j]-argb[j]);
02074         m->argnames[j][arge[j]-argb[j]]=0;
02075       }
02076       lookupArgRefs(m);
02077     } else
02078       replace_directive_with_blank_line(C->out->f);
02079     break;
02080      
02081   case 2: /* UNDEF */
02082     replace_directive_with_blank_line(C->out->f);
02083     if (!commented[iflevel]) {
02084       if (nparam==2 && WarningLevel > 0)
02085         warning("Extra argument to #undef ignored");
02086       whiteout(&p1start,&p1end);
02087       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
02088         bug("#undef requires an identifier (A-Z,a-z,0-9,_ only)");
02089       i=findIdent(C->buf+p1start,p1end-p1start,&h);
02090       if (i>=0) delete_macro(h,i);
02091     }
02092     break;
02093 
02094   case 3: /* IFDEF */
02095     replace_directive_with_blank_line(C->out->f);
02096     iflevel++;
02097     if (iflevel==STACKDEPTH) bug("Too many nested #ifdefs");
02098     commented[iflevel]=commented[iflevel-1];
02099 
02100     if (!commented[iflevel]) {
02101       if (nparam==2 && WarningLevel > 0)
02102         warning("Extra argument to #ifdef ignored");
02103       whiteout(&p1start,&p1end);
02104       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
02105         bug("#ifdef requires an identifier (A-Z,a-z,0-9,_ only)");
02106       i=findIdent(C->buf+p1start,p1end-p1start,&h);
02107       commented[iflevel]=(i==-1);
02108     }
02109     break;
02110 
02111   case 4: /* IFNDEF */
02112     replace_directive_with_blank_line(C->out->f);
02113     iflevel++;
02114     if (iflevel==STACKDEPTH) bug("Too many nested #ifdefs");
02115     commented[iflevel]=commented[iflevel-1];
02116     if (!commented[iflevel]) {
02117       if (nparam==2 && WarningLevel > 0)
02118         warning("Extra argument to #ifndef ignored");
02119       whiteout(&p1start,&p1end);
02120       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
02121         bug("#ifndef requires an identifier (A-Z,a-z,0-9,_ only)");
02122       i=findIdent(C->buf+p1start,p1end-p1start,&h);
02123       commented[iflevel]=(i!=-1);
02124     }
02125     break;
02126     
02127   case 5: /* ELSE */
02128     replace_directive_with_blank_line(C->out->f);
02129     if (!commented[iflevel] && (nparam>0) && WarningLevel > 0)
02130       warning("Extra argument to #else ignored");
02131     if (iflevel==0) bug("#else without #if");
02132     if (!commented[iflevel-1] && commented[iflevel]!=2) 
02133       commented[iflevel]=!commented[iflevel];
02134     break;
02135 
02136   case 6: /* ENDIF */
02137     replace_directive_with_blank_line(C->out->f);
02138     if (!commented[iflevel] && (nparam>0) && WarningLevel > 0)
02139       warning("Extra argument to #endif ignored");
02140     if (iflevel==0) bug("#endif without #if");
02141     iflevel--;
02142     break;
02143 
02144   case 7: /* INCLUDE */
02145     if (!commented[iflevel]) {
02146       struct INPUTCONTEXT *N;
02147       FILE *f = NULL;
02148       char *incfile_name;
02149       char *temp;
02150       int pos1,pos2;
02151 
02152       if (nparam==2 && WarningLevel > 0)
02153         warning("Extra argument to #include ignored");
02154       temp = ProcessText(C->buf+p1start, p1end-p1start, FLAG_META);
02155       pos1 = 0; pos2 = strlen(temp)-1;
02156       while ((pos1<=pos2)&&iswhite(temp[pos1])) pos1++;
02157       while ((pos1<=pos2)&&iswhite(temp[pos2])) pos2--;
02158       if (pos1>pos2) bug("Missing file name in #include");
02159       if ((temp[pos1]=='\"' && temp[pos2]=='\"') ||
02160           (temp[pos1]=='<' && temp[pos2]=='>')) {
02161         pos1++;
02162         pos2--;
02163       }
02164       if (pos1>pos2) bug("Missing file name in #include");
02165       incfile_name=malloc(pos2-pos1+2);
02166       memcpy(incfile_name, temp+pos1, pos2-pos1+1);
02167       incfile_name[pos2-pos1+1] = 0;
02168 
02169       /* if absolute path name is specified */
02170       if (incfile_name[0]==SLASH
02171 #ifdef WIN_NT
02172           || (isalpha(incfile_name[0]) && incfile_name[1]==':')
02173 #endif
02174           )
02175         f=fopen(incfile_name,"r");
02176       else /* search current dir, if this search isn't turned off */
02177         if (!NoCurIncFirst) {
02178           f = openInCurrentDir(incfile_name);
02179         }
02180 
02181       for (j=0;(f==NULL)&&(j<nincludedirs);j++) {
02182         incfile_name =
02183           realloc(incfile_name,pos2-pos1+strlen(includedir[j])+3);
02184         strcpy(incfile_name,includedir[j]);
02185         incfile_name[strlen(includedir[j])]=SLASH;
02186         /* extract the orig include filename */
02187         memcpy(incfile_name+strlen(includedir[j])+1, temp+pos1, pos2-pos1+1);
02188         incfile_name[pos2-pos1+strlen(includedir[j])+2] = '\0';
02189         f=fopen(incfile_name,"r");
02190       }
02191 
02192       /* If didn't find the file and "." is said to be searched last */
02193       if (f==NULL && CurDirIncLast) {
02194         incfile_name=realloc(incfile_name,pos2-pos1+2);
02195         /* extract the orig include filename */
02196         memcpy(incfile_name, temp+pos1, pos2-pos1+1);
02197         incfile_name[pos2-pos1+1] = '\0';
02198         f = openInCurrentDir(incfile_name);
02199       }
02200 
02201       free(temp);
02202 
02203       if (f==NULL) {
02204         char *msg = "Requested include file not found, ";
02205         char *error = (char*)calloc(1,strlen(msg)+strlen(incfile_name)+2);
02206         strcat(error, msg);
02207         strcat(error, incfile_name);
02208         /* no need to dealloc *error because bug(...) kills the process */
02209         bug(error);
02210       }
02211 
02212       N=C;
02213       C=(struct INPUTCONTEXT *)malloc(sizeof(struct INPUTCONTEXT));
02214       C->in=f;
02215       C->argc=0;
02216       C->argv=NULL;
02217       C->filename=incfile_name;
02218       C->out=N->out;
02219       C->lineno=0;
02220       C->bufsize=80;
02221       C->len=0;
02222       C->buf=C->malloced_buf=malloc(C->bufsize);
02223       C->eof=0;
02224       C->namedargs=NULL;
02225       C->in_comment=0;
02226       C->ambience=FLAG_TEXT;
02227       C->may_have_args=0;
02228       PushSpecs(S);
02229       if (autoswitch) {
02230         if (!strcmp(incfile_name+strlen(incfile_name)-2,".h")
02231             || !strcmp(incfile_name+strlen(incfile_name)-2,".c"))
02232           SetStandardMode(S,"C");
02233       }
02234 
02235       /* Include marker before the included contents */
02236       write_include_marker(N->out->f, 1, C->filename, "1");
02237       ProcessContext();
02238       /* Include marker after the included contents */
02239       write_include_marker(N->out->f, N->lineno, N->filename, "2");
02240       /* Need to leave the blank line in lieu of #include, like cpp does */
02241       replace_directive_with_blank_line(N->out->f);
02242       free(C);
02243       PopSpecs();
02244       C=N;
02245     } else
02246       replace_directive_with_blank_line(C->out->f);
02247     break;
02248 
02249   case 8: /* EXEC */
02250     if (!commented[iflevel]) {
02251       if (!execallowed)
02252         warning("Not allowed to #exec. Command output will be left blank");
02253       else {
02254         char *s,*t;
02255         int c;
02256         FILE *f;
02257         s=ProcessText(C->buf+p1start,p1end-p1start,FLAG_META);
02258         if (nparam==2) {
02259           t=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
02260           i=strlen(s);
02261           s=realloc(s,i+strlen(t)+2);
02262           s[i]=' ';
02263           strcpy(s+i+1,t);
02264           free(t);
02265         }
02266         f=popen(s,"r");
02267         free(s);
02268         if (f==NULL) warning("Cannot #exec. Command not found ?");
02269         else {
02270           while ((c=fgetc(f)) != EOF) outchar((char)c);
02271           pclose(f);
02272         }
02273       }
02274     }
02275     break;
02276 
02277   case 9: /* DEFEVAL */
02278     if (!commented[iflevel]) {
02279       whiteout(&p1start,&p1end);
02280       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end)) 
02281         bug("#defeval requires an identifier (A-Z,a-z,0-9,_ only)");
02282       tmpbuf=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
02283       i=findIdent(C->buf+p1start,p1end-p1start,&h);
02284       if (i>=0) delete_macro(h,i);
02285       m=newmacro(C->buf+p1start,p1end-p1start,1,h);
02286       if (nparam==1) { p2end=p2start=p1end; }
02287       replace_definition_with_blank_lines(C->buf+1,C->buf+p2end,S->preservelf);
02288       m->macrotext=tmpbuf;
02289       m->macrolen=strlen(m->macrotext);
02290       m->defined_in_comment=C->in_comment;
02291 
02292       if (argc) {
02293         for (j=0;j<argc;j++) whiteout(argb+j,arge+j);
02294         /* define with one empty argument */
02295         if ((argc==1)&&(arge[0]==argb[0])) argc=0;
02296         m->argnames=(char **)malloc((argc+1)*sizeof(char *));
02297         m->argnames[argc]=NULL;
02298       }
02299       m->nnamedargs=argc;
02300       for (j=0;j<argc;j++) {
02301         if ((argb[j]==arge[j])||(identifierEnd(argb[j])!=arge[j]))
02302           bug("#defeval with named args needs identifiers as arg names");
02303         m->argnames[j]=malloc(arge[j]-argb[j]+1);
02304         memcpy(m->argnames[j],C->buf+argb[j],arge[j]-argb[j]);
02305         m->argnames[j][arge[j]-argb[j]]=0;
02306       }
02307       lookupArgRefs(m);
02308     } else
02309       replace_directive_with_blank_line(C->out->f);
02310     break;
02311      
02312   case 10: /* IFEQ */
02313     replace_directive_with_blank_line(C->out->f);
02314     iflevel++;
02315     if (iflevel==STACKDEPTH) bug("Too many nested #ifeqs");
02316     commented[iflevel]=commented[iflevel-1];
02317     if (!commented[iflevel]) {
02318       char *s,*t;
02319       if (nparam!=2) bug("#ifeq requires two arguments");
02320       s=ProcessText(C->buf+p1start,p1end-p1start,FLAG_META);
02321       t=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
02322       commented[iflevel]=(nowhite_strcmp(s,t)!=0);
02323       free(s); free(t);
02324     }
02325     break;
02326 
02327   case 11: /* IFNEQ */
02328     replace_directive_with_blank_line(C->out->f);
02329     iflevel++;
02330     if (iflevel==STACKDEPTH) bug("Too many nested #ifeqs");
02331     commented[iflevel]=commented[iflevel-1];
02332     if (!commented[iflevel]) {
02333       char *s,*t;
02334       if (nparam!=2) bug("#ifneq requires two arguments");
02335       s=ProcessText(C->buf+p1start,p1end-p1start,FLAG_META);
02336       t=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
02337       commented[iflevel]=(nowhite_strcmp(s,t)==0);
02338       free(s); free(t);
02339     }
02340     break;
02341 
02342   case 12: /* EVAL */
02343     if (!commented[iflevel]) {
02344       char *s,*t;
02345       if (nparam==2) p1end=p2end; /* we really want it all ! */
02346       s=ArithmEval(p1start,p1end);
02347       for (t=s;*t;t++) outchar(*t);
02348       free(s);
02349     }
02350     break;
02351 
02352   case 13: /* IF */
02353     replace_directive_with_blank_line(C->out->f);
02354     iflevel++;
02355     if (iflevel==STACKDEPTH) bug("Too many nested #ifs");
02356     commented[iflevel]=commented[iflevel-1];
02357     if (!commented[iflevel]) {
02358       char *s;
02359       if (nparam==2) p1end=p2end; /* we really want it all ! */
02360       s=ArithmEval(p1start,p1end);
02361       commented[iflevel]=((s[0]=='0')&&(s[1]==0));
02362       free(s);
02363     }
02364     break;
02365 
02366   case 14: /* MODE */
02367     replace_directive_with_blank_line(C->out->f);
02368     if (nparam==1) p2start=-1;
02369     if (!commented[iflevel])
02370       ProcessModeCommand(p1start,p1end,p2start,p2end);
02371     PopSpecs();
02372     break;
02373 
02374   case 15: /* ELIF */
02375     replace_directive_with_blank_line(C->out->f);
02376     if (iflevel==0) bug("#elif without #if");
02377     if (!commented[iflevel-1]) {
02378       if (commented[iflevel]!=1) commented[iflevel]=2;
02379       else {
02380         char *s;
02381         commented[iflevel]=0;
02382         if (nparam==2) p1end=p2end; /* we really want it all ! */
02383         s=ArithmEval(p1start,p1end);
02384         commented[iflevel]=((s[0]=='0')&&(s[1]==0));
02385         free(s);
02386       }
02387     }
02388     break;
02389 
02390   case 16: /* DEFFAST */
02391     if (!commented[iflevel]) {
02392       whiteout(&p1start,&p1end);
02393       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end)) 
02394         bug("#deffast requires an identifier (A-Z,a-z,0-9,_ only)");
02395       tmpargs=NULL;
02396       if (argc) {
02397         for (j=0;j<argc;j++) whiteout(argb+j,arge+j);
02398         /* define with one empty argument */
02399         if ((argc==1)&&(arge[0]==argb[0])) argc=0;
02400         tmpargs=(char **)malloc((argc+1)*sizeof(char *));
02401         tmpargs[argc]=NULL;
02402       }
02403       for (j=0;j<argc;j++) {
02404         if ((argb[j]==arge[j])||(identifierEnd(argb[j])!=arge[j]))
02405           bug("#defeval with named args needs identifiers as arg names");
02406         tmpargs[j]=malloc(arge[j]-argb[j]+1);
02407         memcpy(tmpargs[j],C->buf+argb[j],arge[j]-argb[j]);
02408         tmpargs[j][arge[j]-argb[j]]=0;
02409       }
02410       tmpbuf=ProcessFastDefinition(C->buf+p2start,p2end-p2start,tmpargs);
02411       for (j=0;j<argc;j++) free(tmpargs[j]);
02412       if (tmpargs!=NULL) free(tmpargs);
02413       i=findIdent(C->buf+p1start,p1end-p1start,&h);
02414       if (i>=0) delete_macro(h,i);
02415       m=newmacro(C->buf+p1start,p1end-p1start,1,h);
02416       if (nparam==1) { p2end=p2start=p1end; }
02417       replace_definition_with_blank_lines(C->buf+1,C->buf+p2end,S->preservelf);
02418       m->macrotext=tmpbuf;
02419       m->macrolen=strlen(m->macrotext);
02420       m->defined_in_comment=C->in_comment;
02421       m->argnames=NULL;
02422       m->nnamedargs=-3; /* special value */
02423     } else
02424       replace_directive_with_blank_line(C->out->f);
02425     break;
02426      
02427   default: bug("Internal meta-macro identification error");
02428   }
02429   shiftIn(macend);
02430   return 0;
02431 }
02432 
02433 int ParsePossibleUser(void)
02434 {
02435   int idstart,idend,sh_end,lg_end,macend;
02436   int argc,id,i,l,h;
02437   char *argv[MAXARGS];
02438   int argb[MAXARGS],arge[MAXARGS];
02439   struct INPUTCONTEXT *T;
02440   struct MACRO *m;
02441 
02442   idstart=1;
02443   id=0;
02444   if (!SplicePossibleUser(&idstart,&idend,&sh_end,&lg_end,
02445                           argb,arge,&argc,1,&id,FLAG_USER,&h))
02446     return -1;
02447   if ((sh_end>=0)&&(C->namedargs!=NULL)) {
02448     i=findNamedArg(C->buf+idstart,idend-idstart);
02449     if (i>=0) {
02450       if (i<C->argc) sendout(C->argv[i],strlen(C->argv[i]),0);
02451       shiftIn(sh_end);
02452       return 0;
02453     }
02454   }
02455 
02456   if (id<0) return -1;
02457   if (lg_end>=0) macend=lg_end; else { macend=sh_end; argc=0; }
02458   m=&(macros[h][id]);
02459 
02460   if (m->nnamedargs==-2) { /* defined(...) macro for arithmetic */
02461     char *s,*t;
02462     if (argc!=1) return -1;
02463     s=remove_comments(argb[0],arge[0],FLAG_USER);
02464     t=s+strlen(s)-1;
02465     if (*s!=0) while ((t!=s)&&iswhite(*t)) *(t--)=0;
02466     t=s; while (iswhite(*t)) t++;
02467     h=-1;
02468     if (findIdent(t,strlen(t),&h)>=0) outchar('1');
02469     else outchar('0');
02470     free(s);
02471     shiftIn(macend);
02472     return 0;
02473   }
02474   if (!m->macrotext[0]) { /* the empty macro */
02475     shiftIn(macend);
02476     return 0;
02477   }
02478   
02479   for (i=0;i<argc;i++)
02480     argv[i]=ProcessText(C->buf+argb[i],arge[i]-argb[i],FLAG_USER);
02481     
02482   /* fast macros get a short execution */
02483   if (m->nnamedargs==-3) {
02484     char *s;
02485     for (s=m->macrotext;*s!=0;s++) {
02486       if (*s>=1 && *s<=8)
02487         { if (*s-1<argc) sendout(argv[*s-1],strlen(argv[*s-1]),0); }
02488       else { if (!commented[iflevel]) outchar(*s); }
02489     }
02490     for (i=0;i<argc;i++) free(argv[i]);
02491     shiftIn(macend);
02492     return 0;
02493   }
02494   
02495   /* process macro text */
02496   T=C;
02497   C=(struct INPUTCONTEXT *)malloc(sizeof(struct INPUTCONTEXT));
02498   C->out=T->out;
02499   C->in=NULL;
02500   C->argc=argc;
02501   C->argv=argv;
02502   C->filename=T->filename;
02503   C->lineno=T->lineno;
02504   C->may_have_args=1;
02505   if ((m->nnamedargs==-1)&&(lg_end>=0)&&
02506       (m->define_specs->User.mEnd[0]==0)) {
02507     /* build an aliased macro call */
02508     l=strlen(m->macrotext)+2
02509       +strlen(m->define_specs->User.mArgS)
02510       +strlen(m->define_specs->User.mArgE)
02511       +(argc-1)*strlen(m->define_specs->User.mArgSep);
02512     for (i=0;i<argc;i++) l+=strlen(argv[i]);
02513     C->buf=C->malloced_buf=malloc(l);
02514     l=strlen(m->macrotext)+1;
02515     C->buf[0]='\n';
02516     strcpy(C->buf+1,m->macrotext);
02517     while ((l>1)&&iswhite(C->buf[l-1])) l--;
02518     strcpy(C->buf+l,m->define_specs->User.mArgS);
02519     for (i=0;i<argc;i++) {
02520       if (i>0) strcat(C->buf,m->define_specs->User.mArgSep);
02521       strcat(C->buf,argv[i]);
02522     }
02523     strcat(C->buf,m->define_specs->User.mArgE);
02524     C->may_have_args=0;
02525   } 
02526   else {
02527     C->buf=C->malloced_buf=malloc(strlen(m->macrotext)+2);
02528     C->buf[0]='\n';
02529     strcpy(C->buf+1,m->macrotext);
02530   }
02531   C->len=strlen(C->buf);
02532   C->bufsize=C->len+1;
02533   C->eof=0;
02534   C->namedargs=m->argnames;
02535   C->in_comment=m->defined_in_comment;
02536   C->ambience=FLAG_META; 
02537   if (m != NULL) 
02538     PushSpecs(m->define_specs);
02539   ProcessContext();
02540   PopSpecs();
02541   free(C);
02542   C=T;
02543   
02544   for (i=0;i<argc;i++) free(argv[i]);
02545   shiftIn(macend);
02546   return 0;
02547 }
02548 
02549 void ParseText(void)
02550 {
02551   int l,cs,ce;
02552   char c,*s;
02553   struct COMMENT *p;
02554 
02555   /* look for comments first */
02556   if (!C->in_comment) {
02557     cs=1;
02558     for (p=S->comments;p!=NULL;p=p->next)
02559       if (!(p->flags[C->ambience]&FLAG_IGNORE))
02560         if (matchStartSequence(p->start,&cs)) {
02561           l=ce=findCommentEnd(p->end,p->quote,p->warn,cs,p->flags[C->ambience]);
02562           matchEndSequence(p->end,&l);
02563           if (p->flags[C->ambience]&OUTPUT_DELIM)
02564             sendout(C->buf+1,cs-1,0);
02565           if (!(p->flags[C->ambience]&OUTPUT_TEXT)) 
02566             replace_definition_with_blank_lines(C->buf+1, C->buf+ce-1, 0);
02567           if (p->flags[C->ambience]&PARSE_MACROS) {
02568             C->in_comment=1;
02569             s=ProcessText(C->buf+cs,ce-cs,C->ambience);
02570             if (p->flags[C->ambience]&OUTPUT_TEXT) sendout(s,strlen(s),0);
02571             C->in_comment=0;
02572             free(s);
02573           } 
02574           else if (p->flags[C->ambience]&OUTPUT_TEXT)
02575             sendout(C->buf+cs,ce-cs,0);
02576           if (p->flags[C->ambience]&OUTPUT_DELIM)
02577             sendout(C->buf+ce,l-ce,0);
02578           shiftIn(l);
02579           return;
02580         }
02581   }
02582 
02583   if (ParsePossibleMeta()>=0) return;
02584   if (ParsePossibleUser()>=0) return;
02585   
02586   l=1;
02587   /* If matching numbered macro argument and inside a macro */
02588   if (matchSequence(S->User.mArgRef,&l) && C->may_have_args) {
02589     /* Process macro arguments referenced as #1,#2,... */
02590     c=getChar(l);
02591     if ((c>='1')&&(c<='9')) {
02592       c=c-'1';
02593       if (c<C->argc)
02594         sendout(C->argv[(int)c],strlen(C->argv[(int)c]),0);
02595       shiftIn(l+1);
02596       return;
02597     }
02598   }
02599   
02600   l=identifierEnd(1);
02601   if (l==1) l=2;
02602   sendout(C->buf+1,l-1,1);
02603   shiftIn(l);
02604 }
02605 
02606 void ProcessContext(void)
02607 {
02608   if (C->len==0) { C->buf[0]='\n'; C->len++; }
02609   while (!C->eof) ParseText();
02610   if (C->in!=NULL) fclose(C->in);
02611   free(C->malloced_buf);
02612 }
02613 
02614 /* additions by M. Kifer - revised D.A. 12/16/01 */
02615 
02616 /* copy SLASH-terminated name of the directory of fname */
02617 static void getDirname(char *fname, char *dirname)
02618 {
02619   int i;
02620 
02621   for (i = strlen(fname)-1; i>=0; i--) {
02622     if (fname[i] == SLASH)
02623       break;
02624   }
02625   if (i >= 0) {
02626     strncpy(dirname,fname,i);
02627     dirname[i] = SLASH;
02628   } else
02629     /* just a precaution: i must be -1 in this case anyway */
02630     i = -1;
02631 
02632   dirname[i+1] = '\0';
02633 }
02634 
02635 static FILE *openInCurrentDir(char *incfile)
02636 {
02637   char *absfile =
02638     (char *)calloc(strlen(C->filename)+strlen(incfile)+1, sizeof(char));
02639   FILE *f;
02640   getDirname(C->filename,absfile);
02641   strcat(absfile,incfile);
02642   f=fopen(absfile,"r");
02643   free(absfile);
02644   return f;
02645 }
02646 
02647 /* skip = # of \n's already output by other mechanisms, to be skipped */
02648 void replace_definition_with_blank_lines(char *start, char *end, int skip)
02649 {
02650   if ((include_directive_marker != NULL) && (C->out->f != NULL)) {
02651     while (start <= end) {
02652       if (*start == '\n') {
02653         if (skip) skip--; else fprintf(C->out->f,"\n");
02654       }
02655       start++;
02656     }
02657   }
02658 }
02659 
02660 /* insert blank line where the metas IFDEF,ELSE,INCLUDE, etc., stood in the
02661    input text
02662 */
02663 void replace_directive_with_blank_line(FILE *f)
02664 {
02665   if ((include_directive_marker != NULL) && (f != NULL)
02666       && (!S->preservelf) && (S->Meta.mArgE[0]=='\n')) {
02667     fprintf(f,"\n");
02668   }
02669 }
02670 
02671 
02672 /* If lineno is > 15 digits - the number won't be printed correctly */
02673 void write_include_marker(FILE *f, int lineno, char *filename, char *marker)
02674 {
02675   static char lineno_buf[MAX_GPP_NUM_SIZE];
02676   static char *escapedfilename = NULL;
02677 
02678   if ((include_directive_marker != NULL) && (f != NULL)) {
02679 #ifdef WIN_NT
02680     escape_backslashes(filename,&escapedfilename);
02681 #else
02682     escapedfilename = filename;
02683 #endif
02684     sprintf(lineno_buf,"%d", lineno);
02685     fprintf(f, include_directive_marker, lineno_buf, escapedfilename, marker);
02686   }
02687 }
02688 
02689 
02690 /* Under windows, files can have backslashes in them. 
02691    These should be escaped.
02692 */
02693 void escape_backslashes(char *instr, char **outstr)
02694 {
02695   int out_idx=0;
02696 
02697   if (*outstr != NULL) free(*outstr);
02698   *outstr = malloc(2*strlen(instr));
02699 
02700   while (*instr != '\0') {
02701     if (*instr=='\\') {
02702       *(*outstr+out_idx) = '\\';
02703       out_idx++;
02704     }
02705     *(*outstr+out_idx) = *instr;
02706     out_idx++;
02707     instr++;
02708   }
02709   *(*outstr+out_idx) = '\0';
02710 }
02711 
02712 
02713 /* includemarker_input should have 3 ?-marks, which are replaced with %s.
02714    Also, @ is replaced with a space. These symbols can be escaped with a
02715    backslash.
02716 */
02717 void construct_include_directive_marker(char **include_directive_marker,
02718                                         char *includemarker_input)
02719 {
02720   int len = strlen(includemarker_input);
02721   char ch;
02722   int in_idx=0, out_idx=0;
02723   int quoted = 0, num_repl = 0;
02724 
02725   /* only 6 extra chars are needed: 3 for the three %'s, 2 for \n, 1 for \0 */
02726   *include_directive_marker = malloc(len+18);
02727 
02728   ch = *includemarker_input;
02729   while (ch != '\0' && in_idx < len) {
02730     if (quoted) {
02731       *(*include_directive_marker+out_idx) = ch;
02732       out_idx++;
02733       quoted = 0;
02734     } else {
02735       switch (ch) {
02736       case '\\':
02737         quoted = 1;
02738         break;
02739       case '@':
02740         *(*include_directive_marker+out_idx) = ' ';
02741         out_idx++;
02742         break;
02743       case '%':
02744       case '?':
02745         *(*include_directive_marker+out_idx) = '%';
02746         out_idx++;
02747         *(*include_directive_marker+out_idx) = 's';
02748         out_idx++;
02749         if (++num_repl > 3) bug("only 3 substitutions allowed in -includemarker");
02750         break;
02751       default:
02752         *(*include_directive_marker+out_idx) = ch;
02753         out_idx++;
02754       }
02755     }
02756 
02757     in_idx++;
02758     ch = *(includemarker_input+in_idx);
02759   }
02760 
02761   *(*include_directive_marker+out_idx) = '\n';
02762   out_idx++;
02763   *(*include_directive_marker+out_idx) = '\0';
02764 }
02765 
02766 
02767 int main(int argc,char **argv)
02768 {
02769   initthings(argc,argv); 
02770   /* The include marker at the top of the file */
02771   write_include_marker(C->out->f, 1, C->filename, "");
02772   ProcessContext();
02773   fclose(C->out->f);
02774   return 0;
02775 }
02776 

Generated on Wed Jul 26 13:30:44 2006 for XSB by  doxygen 1.4.5