1 /* File:      gpp.c  -- generic preprocessor
2 ** Author:    Denis Auroux, Tristan Miller
3 ** Contact:   psychonaut@nothingisreal.com
4 **
5 ** Copyright (C) 1996, 1999, 2001 Denis Auroux
6 ** Copyright (C) 2003, 2004 Tristan Miller
7 **
8 ** GPP is free software; you can redistribute it and/or modify it under the
9 ** terms of the GNU Lesser General Public License as published by the Free
10 ** Software Foundation; either version 2.1 of the License, or (at your option)
11 ** any later version.
12 **
13 ** GPP is distributed in the hope that it will be useful, but WITHOUT ANY
14 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 ** FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
16 ** more details.
17 **
18 ** You should have received a copy of the GNU Lesser General Public License
19 ** along with this software; if not, write to the Free Software Foundation,
20 ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 **
22 ** $Id: gpp.c,v 1.10 2004/09/19 20:19:16 psy Exp $
23 **
24 */
25 
26 /* To compile under MS VC++, one must define WIN_NT */
27 
28 #if HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 #ifdef WIN_NT              /* WIN NT settings */
33 #define popen   _popen
34 #define pclose  _pclose
35 #define my_strdup  _strdup
36 #define my_strcasecmp _stricmp
37 #define SLASH '\\'
38 #define DEFAULT_CRLF 1
39 #else                      /* UNIX settings */
40 #define SLASH '/'
41 #define DEFAULT_CRLF 0
42 #endif
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48 #if HAVE_FNMATCH_H
49 #  include <fnmatch.h>
50 #endif
51 #include <time.h>
52 
53 #define STACKDEPTH 50
54 #define MAXARGS 100
55 #define MAXINCL 10   /* max # of include dirs */
56 
57 #define MAX_GPP_NUM_SIZE 15
58 #define MAX_GPP_DATE_SIZE 1024
59 
60 typedef struct MODE {
61   char *mStart;		/* before macro name */
62   char *mEnd;		/* end macro without arg */
63   char *mArgS;		/* start 1st argument */
64   char *mArgSep;	/* separate arguments */
65   char *mArgE;		/* end last argument */
66   char *mArgRef;	/* how to refer to arguments in a def */
67   char quotechar;	/* quote next char */
68   char *stackchar;      /* characters to stack */
69   char *unstackchar ;   /* characters to unstack */
70 } MODE;
71 
72 /* translation for delimiters :
73    \001 = \b = ' ' = one or more spaces    \201 = \!b = non-space
74    \002 = \w = zero or more spaces
75    \003 = \B = one or more spaces or \n    \203 = \!B = non-space nor \n
76    \004 = \W = zero or more spaces or \n
77    \005 = \a = alphabetic (a-z, A-Z)       \205 = \!a = non-alphabetic
78    \006 = \A = alphabetic or space/\n      \206 = \!A
79    \007 = \# = numeric (0-9)               \207 = \!#
80    \010 = \i = identifier (a-zA-Z0-9_)     \210 = \!i
81    \011 = \t, \012 = \n                    \211 = \!t, \212 = \!n
82    \013 = \o = operator (+-*\/^<>=`~:.?@#&!%|) \213 = \!o
83    \014 = \O = operator or ()[]{}              \214 = \!O
84 */
85 /*                   st        end   args   sep    arge ref  quot  stk  unstk*/
86 struct MODE CUser = {"",       "",   "(",   ",",   ")", "#", '\\', "(", ")" };
87 struct MODE CMeta = {"#",      "\n", "\001","\001","\n","#", '\\', "(", ")" };
88 struct MODE KUser = {"",       "",   "(",   ",",   ")", "#",  0,   "(", ")" };
89 struct MODE KMeta = {"\n#\002","\n", "\001","\001","\n","#",  0,   "",  ""  };
90 struct MODE Tex   = {"\\",     "",   "{",   "}{",  "}", "#", '@',  "{", "}" };
91 struct MODE Html  = {"<#",     ">",  "\003","|",   ">", "#", '\\', "<", ">" };
92 struct MODE XHtml = {"<#",     "/>", "\003","|",   "/>","#", '\\', "<", ">" };
93 
94 #define DEFAULT_OP_STRING (unsigned char *)"+-*/\\^<>=`~:.?@#&!%|"
95 #define PROLOG_OP_STRING  (unsigned char *)"+-*/\\^<>=`~:.?@#&"
96 #define DEFAULT_OP_PLUS   (unsigned char *)"()[]{}"
97 #define DEFAULT_ID_STRING (unsigned char *)"\005\007_" /* or equiv. "A-Za-z0-9_" */
98 
99 /* here we assume that longs are at least 32 bit... if not, change this ! */
100 #define LOG_LONG_BITS 5
101 #define CHARSET_SUBSET_LEN (256>>LOG_LONG_BITS)
102 typedef unsigned long *CHARSET_SUBSET;
103 
104 CHARSET_SUBSET DefaultOp,DefaultExtOp,PrologOp,DefaultId;
105 
106 typedef struct COMMENT {
107   char *start;          /* how the comment/string starts */
108   char *end;            /* how it ends */
109   char quote;           /* how to prevent it from ending */
110   char warn;            /* a character that shouldn't be in there */
111   int flags[3];         /* meta, user, text */
112   struct COMMENT *next;
113 } COMMENT;
114 
115 #define OUTPUT_TEXT     0x1   /* what's inside will be output */
116 #define OUTPUT_DELIM    0x2   /* the delimiters will be output */
117 #define PARSE_MACROS    0x4   /* macros inside will be parsed */
118 #define FLAG_IGNORE     0x40
119 
120 #define FLAG_STRING    (OUTPUT_TEXT|OUTPUT_DELIM)
121 #define FLAG_COMMENT   0
122 
123 #define FLAG_META 0
124 #define FLAG_USER 1
125 #define FLAG_TEXT 2
126 
127 /* Some stuff I removed because it made for some impossible situations :
128 
129  #define PARSE_COMMENTS  0x8
130    comments inside comments will not be parsed because nesting comments is
131    too complicated (syntax conflicts, esp. to find a comment's end)
132    -- of course, unless the comment is ignored.
133 
134  #define MACRO_FRIENDLY  0x20
135    a comment-end is to be processed even if an unfinished macro call has
136    started inside the comment, otherwise it's too hard do decide in advance
137    where a comment ends. In particular foo('bar((((') is valid.
138 
139  #define PREVENT_DELIM   0x10
140    all comments will prevent macro delimitation, i.e. foo('bar) is invalid.
141    -- of course, unless the comment is ignored.
142    Too bad, #define foo '...    terminates only at following "'".
143    Unless one adds quotechars like in #define foo \' ...
144 
145  ALSO NOTE : comments are not allowed before the end of the first argument
146  to a meta-macro. E.g. this is legal :   #define foo <* blah *> 3
147  This is not legal :                     #define <* blah *> foo 3
148  If a comment occurs here, the behavior depends on the actual meta-macro :
149  most will yield an error and stop gpp (#define, #undef, #ifdef/ifndef,
150  #defeval, #include, #mode) ; #exec, #if and #eval should be ok ;
151  #ifeq will always fail while #ifneq will always succeed ;
152 */
153 
154 typedef struct SPECS {
155   struct MODE User,Meta;
156   struct COMMENT *comments;
157   struct SPECS *stack_next;
158   int preservelf;
159   CHARSET_SUBSET op_set,ext_op_set,id_set;
160 } SPECS;
161 
162 struct SPECS *S;
163 
164 typedef struct MACRO {
165   char *username,*macrotext,**argnames;
166   int macrolen,nnamedargs;
167   struct SPECS *define_specs;
168   int defined_in_comment;
169 } MACRO;
170 
171 struct MACRO *macros;
172 int nmacros,nalloced;
173 char *includedir[MAXINCL];
174 int nincludedirs;
175 int execallowed;
176 int dosmode;
177 int autoswitch;
178 /* must be a format-like string that has % % % in it.
179    The first % is replaced with line number, the second with "filename", and
180    the third with 1, 2 or blank
181    Can also use ? instead of %.
182 */
183 char *include_directive_marker = NULL;
184 short WarningLevel = 2;
185 
186 /* controls if standard dirs, like /usr/include, are to be searched for
187    #include and whether the current dir is to be searched first or last. */
188 int NoStdInc        = 0;
189 int NoCurIncFirst   = 0;
190 int CurDirIncLast   = 0;
191 int file_and_stdout = 0;
192 char *IncludeFile   = NULL;
193 
194 typedef struct OUTPUTCONTEXT {
195   char *buf;
196   int len,bufsize;
197   FILE *f;
198 } OUTPUTCONTEXT;
199 
200 typedef struct INPUTCONTEXT {
201   char *buf;
202   char *malloced_buf; /* what was actually malloc-ed (buf may have shifted) */
203   int len,bufsize;
204   int lineno;
205   char *filename;
206   FILE *in;
207   int argc;
208   char **argv;
209   char **namedargs;
210   struct OUTPUTCONTEXT *out;
211   int eof;
212   int in_comment;
213   int ambience; /* FLAG_TEXT, FLAG_USER or FLAG_META */
214   int may_have_args;
215 } INPUTCONTEXT;
216 
217 struct INPUTCONTEXT *C;
218 
219 int commented[STACKDEPTH],iflevel;
220 /* commented = 0: output, 1: not output,
221    2: not output because we're in a #elif and we've already gone through
222       the right case (so #else/#elif can't toggle back to output) */
223 
224 void ProcessContext(void); /* the main loop */
225 
226 int findIdent(const char *b,int l);
227 void delete_macro(int i);
228 
229 /* various recent additions */
230 void usage(void);
231 void display_version(void);
232 void bug(const char *s);
233 void warning(const char *s);
234 static void getDirname(const char *fname, char *dirname);
235 static FILE *openInCurrentDir(const char *incfile);
236 char *ArithmEval(int pos1,int pos2);
237 void replace_definition_with_blank_lines(const char *start, const char *end, int skip);
238 void replace_directive_with_blank_line(FILE *file);
239 void write_include_marker(FILE *f, int lineno, char *filename, const char *marker);
240 void construct_include_directive_marker(char **include_directive_marker,
241 					const char *includemarker_input);
242 void escape_backslashes(const char *instr, char **outstr);
243 static void DoInclude(char *file_name);
244 
245 /*
246 ** strdup() and my_strcasecmp() are not ANSI C, so here we define our own
247 ** versions in case the compiler does not support them
248 */
249 #if ! HAVE_STRDUP
250 inline char *my_strdup(const char *s);
my_strdup(const char * s)251 inline char *my_strdup(const char *s) {
252   size_t len=strlen(s) + 1;
253   char *newstr=malloc(len);
254   return newstr ? (char *)memcpy(newstr, s, len) : NULL;
255 }
256 #else
257 #  undef my_strdup
258 #  define my_strdup strdup
259 #endif
260 #if ! HAVE_STRCASECMP
my_strcasecmp(const char * s,const char * s2)261 int my_strcasecmp(const char *s, const char *s2) {
262   do {
263     char c1=tolower(*s);
264     char c2=tolower(*s2);
265     if (c1>c2)
266       return 1;
267     if (c1<c2)
268       return -1;
269   } while (*s++ && *s2++);
270   return 0;
271 }
272 #else
273 #  undef my_strcasecmp
274 #  define my_strcasecmp strcasecmp
275 #endif
276 
bug(const char * s)277 void bug(const char *s)
278 {
279   fprintf(stderr,"%s:%d: error: %s\n",C->filename,C->lineno,s);
280   exit(EXIT_FAILURE);
281 }
282 
warning(const char * s)283 void warning(const char *s)
284 {
285   fprintf(stderr,"%s:%d: warning: %s\n",C->filename,C->lineno,s);
286 }
287 
CloneSpecs(const struct SPECS * Q)288 struct SPECS *CloneSpecs(const struct SPECS *Q)
289 {
290   struct SPECS *P;
291   struct COMMENT *x,*y;
292 
293   P=malloc(sizeof *P);
294   if (P==NULL) bug("Out of memory.");
295   memcpy(P,Q,sizeof(struct SPECS));
296   P->stack_next=NULL;
297   if (Q->comments!=NULL)
298     P->comments=malloc(sizeof *(P->comments));
299   for (x=Q->comments,y=P->comments;x!=NULL;x=x->next,y=y->next) {
300     memcpy(y,x,sizeof(struct COMMENT));
301     y->start=my_strdup(x->start);
302     y->end=my_strdup(x->end);
303     if (x->next!=NULL)
304       y->next=malloc(sizeof *(y->next));
305   }
306   return P;
307 }
308 
FreeComments(struct SPECS * Q)309 void FreeComments(struct SPECS *Q)
310 {
311   struct COMMENT *p;
312 
313   while (Q && Q->comments!=NULL) {
314     p=Q->comments;
315     Q->comments=p->next;
316     free(p->start);
317     free(p->end);
318     free(p);
319   }
320 }
321 
PushSpecs(const struct SPECS * X)322 void PushSpecs(const struct SPECS *X)
323 {
324   struct SPECS *P;
325 
326   P=CloneSpecs(X);
327   P->stack_next=S;
328   S=P;
329 }
330 
PopSpecs(void)331 void PopSpecs(void)
332 {
333   struct SPECS *P;
334 
335   P=S;
336   S=P->stack_next;
337   FreeComments(P);
338   free(P);
339   if (S==NULL) bug("#mode restore without #mode save");
340 }
341 
display_version(void)342 void display_version(void) {
343   fprintf(stderr, PACKAGE_STRING "\n");
344   fprintf(stderr,"Copyright (C) 1996-2001 Denis Auroux\n");
345   fprintf(stderr,"Copyright (C) 2003, 2004 Tristan Miller\n");
346   fprintf(stderr,
347 	  "This is free software; see the source for copying conditions.  There is NO\n"
348 	  "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
349 	  );
350 }
351 
usage(void)352 void usage(void) {
353   fprintf(stderr,"Usage : gpp [-{o|O} outfile] [-I/include/path] [-Dname=val ...] [-z] [-x] [-m]\n");
354   fprintf(stderr,"            [-n] [-C | -T | -H | -X | -P | -U ... [-M ...]] [+c<n> str1 str2]\n");
355   fprintf(stderr,"            [+s<n> str1 str2 c] [long options] [infile]\n\n");
356   fprintf(stderr,"      default:    #define x y           macro(arg,...)\n");
357   fprintf(stderr," -C : maximum cpp compatibility (includes -n, +c, +s, ...)\n");
358   fprintf(stderr," -T : TeX-like    \\define{x}{y}         \\macro{arg}{...}\n");
359   fprintf(stderr," -H : HTML-like   <#define x|y>         <#macro arg|...>\n");
360   fprintf(stderr," -X : XHTML-like  <#define x|y/>        <#macro arg|.../>\n");
361   fprintf(stderr," -P : prolog compatible cpp-like mode\n");
362   fprintf(stderr," -U : user-defined syntax (specified in 9 following args; see manual)\n");
363   fprintf(stderr," -M : user-defined syntax for meta-macros (specified in 7 following args)\n\n");
364   fprintf(stderr," -o : output to outfile\n");
365   fprintf(stderr," -O : output to outfile and stdout\n");
366   fprintf(stderr," -z : line terminator is CR-LF (MS-DOS style)\n");
367   fprintf(stderr," -x : enable #exec built-in macro\n");
368   fprintf(stderr," -m : enable automatic mode switching upon including .h/.c files\n");
369   fprintf(stderr," -n : send LF characters serving as macro terminators to output\n");
370   fprintf(stderr," +c : use next 2 args as comment start and comment end sequences\n");
371   fprintf(stderr," +s : use next 3 args as string start, end and quote character\n\n");
372   fprintf(stderr," Long options:\n");
373   fprintf(stderr," --include file : process file before infile\n");
374   fprintf(stderr," --nostdinc : don't search standard directories for files to include\n");
375   fprintf(stderr," --nocurinc : don't search the current directory for files to include\n");
376   fprintf(stderr," --curdirinclast : search the current directory last\n");
377   fprintf(stderr," --warninglevel n : set warning level\n");
378   fprintf(stderr," --includemarker formatstring : keep track of #include directives in output\n\n");
379   fprintf(stderr," --version : display version information and exit\n");
380   fprintf(stderr," -h, --help : display this message and exit\n\n");
381 }
382 
isdelim(unsigned char c)383 int isdelim(unsigned char c)
384 {
385   if (c>=128) return 0;
386   if ((c>='0')&&(c<='9')) return 0;
387   if ((c>='A')&&(c<='Z')) return 0;
388   if ((c>='a')&&(c<='z')) return 0;
389   if (c=='_') return 0;
390   return 1;
391 }
392 
iswhite(char c)393 int iswhite(char c)
394 {
395   if (c==' ') return 1;
396   if (c=='\t') return 1;
397   if (c=='\n') return 1;
398   return 0;
399 }
400 
newmacro(const char * s,int len,int hasspecs)401 void newmacro(const char *s,int len,int hasspecs)
402 {
403   if (nmacros==nalloced) {
404     nalloced=2*nalloced+1;
405     macros=realloc(macros,nalloced*sizeof *macros);
406     if (macros==NULL)
407       bug("Out of memory");
408   }
409   macros[nmacros].username=malloc(len+1);
410   strncpy(macros[nmacros].username,s,len);
411   macros[nmacros].username[len]=0;
412   macros[nmacros].argnames=NULL;
413   macros[nmacros].nnamedargs=0;
414   macros[nmacros].defined_in_comment=0;
415   if (hasspecs)
416     macros[nmacros].define_specs=CloneSpecs(S);
417   else
418     macros[nmacros].define_specs=NULL;
419 }
420 
lookupArgRefs(int n)421 void lookupArgRefs(int n)
422 {
423   int i,l;
424   char *p;
425 
426   if (macros[n].argnames!=NULL) return; /* don't mess with those */
427   macros[n].nnamedargs=-1;
428   l=strlen(S->User.mArgRef);
429   for (i=0,p=macros[n].macrotext;i<macros[n].macrolen;i++,p++) {
430     if ((*p!=0)&&(*p==S->User.quotechar)) { i++; p++; }
431     else if (!strncmp(p,S->User.mArgRef,l))
432       if ((p[l]>='1')&&(p[l]<='9'))
433         { macros[n].nnamedargs=0; return; }
434   }
435 }
436 
strnl0(const char * s)437 char *strnl0(const char *s) /* replace "\\n" by "\n" in a cmd-line arg */
438 {
439   char *t,*u;
440   t=malloc(strlen(s)+1);
441   u=t;
442   while (*s!=0) {
443     if ((*s=='\\')&&(s[1]=='n')) { *u='\n'; s++; }
444     else *u=*s;
445     s++; u++;
446   }
447   *u=0;
448   return t;
449 }
450 
strnl(const char * s)451 char *strnl(const char *s) /* the same but with whitespace specifier handling */
452 {
453   char *t,*u;
454   int neg;
455   t=malloc(strlen(s)+1);
456   u=t;
457   if (!isdelim(*s)) bug("character not allowed to start a syntax specifier");
458   while (*s!=0) {
459     if (((*s&0x60)==0)&&(*s!='\n')&&(*s!='\t'))
460       bug("character not allowed in syntax specifier");
461     if (*s=='\\') {
462       neg=(s[1]=='!');
463       switch(s[neg+1]) {
464         case 'n': case 'r': *u='\n'; break;
465         case 't': *u='\t'; break;
466         case 'b': *u='\001'; break;  /* one or more spaces */
467         case 'w': if (neg) bug("\\w and \\W cannot be negated");
468                   *u='\002'; break;  /* zero or more spaces */
469         case 'B': *u='\003'; break;  /* one or more spaces or \n */
470         case 'W': if (neg) bug("\\w and \\W cannot be negated");
471                   *u='\004'; break;  /* zero or more spaces or \n */
472         case 'a': *u='\005'; break;  /* alphabetic */
473         case 'A': *u='\006'; break;  /* alphabetic + space */
474         case '#': *u='\007'; break;  /* numeric */
475         case 'i': *u='\010'; break;  /* identifier */
476         case 'o': *u='\013'; break;  /* operator */
477         case 'O': *u='\014'; break;  /* operator/parenthese */
478         default: *u='\\'; neg=-1;
479       }
480       if (neg>0) *u+=(char)128;
481       s+=neg+1;
482     }
483     else if (*s==' ') *u='\001';
484     else *u=*s;
485     s++; u++;
486   }
487   *u=0;
488   return t;
489 }
490 
491 /* same as strnl() but for C strings & in-place */
strnl2(char * s,int check_delim)492 char *strnl2(char *s,int check_delim)
493 {
494   char *u;
495   int neg;
496   u=s;
497   if (check_delim&&!isdelim(*s))
498     bug("character not allowed to start a syntax specifier");
499   while (*s!='"') {
500     if (((*s&0x60)==0)&&(*s!='\n')&&(*s!='\t'))
501       bug("character not allowed in syntax specifier");
502     if (*s=='\\') {
503       neg=(s[1]=='!');
504       switch(s[neg+1]) {
505         case 'n': case 'r': *u='\n'; break;
506         case 't': *u='\t'; break;
507         case 'b': *u='\001'; break;  /* one or more spaces */
508         case 'w': if (neg) bug("\\w and \\W cannot be negated");
509                   *u='\002'; break;  /* zero or more spaces */
510         case 'B': *u='\003'; break;  /* one or more spaces or \n */
511         case 'W': if (neg) bug("\\w and \\W cannot be negated");
512                   *u='\004'; break;  /* zero or more spaces or \n */
513         case 'a': *u='\005'; break;  /* alphabetic */
514         case 'A': *u='\006'; break;  /* alphabetic + space */
515         case '#': *u='\007'; break;  /* numeric */
516         case 'i': *u='\010'; break;  /* identifier */
517         case 'o': *u='\013'; break;  /* operator */
518         case 'O': *u='\014'; break;  /* operator/parenthese */
519         case '"': case '\\': if (!neg) { *u=s[1]; break; }
520         default: bug("unknown escape sequence in syntax specifier");
521       }
522       if (neg>0) *u+=(char)128;
523       s+=neg+1;
524     }
525     else if (*s==' ') *u='\001';
526     else *u=*s;
527     if (*s==0) bug("unterminated string in #mode command");
528     s++; u++;
529   }
530   *u=0;
531   return (s+1);
532 }
533 
iswhitesep(const char * s)534 int iswhitesep(const char *s)
535 {
536   while (iswhite(*s)||(*s=='\001')||(*s=='\002')||(*s=='\003')||(*s=='\004'))
537     s++;
538   return (*s==0);
539 }
540 
nowhite_strcmp(char * s,char * t)541 int nowhite_strcmp(char *s,char *t)
542 {
543   char *p;
544 
545   while (iswhite(*s)) s++;
546   while (iswhite(*t)) t++;
547   if ((*s==0)||(*t==0)) return strcmp(s,t);
548   p=s+strlen(s)-1;
549   while (iswhite(*p)) *(p--)=0;
550   p=t+strlen(t)-1;
551   while (iswhite(*p)) *(p--)=0;
552   return strcmp(s,t);
553 }
554 
parseCmdlineDefine(const char * s)555 void parseCmdlineDefine(const char *s)
556 {
557   int l, i, argc;
558 
559   for (l=0;s[l]&&(s[l]!='=')&&(s[l]!='(');l++);
560   i = findIdent(s,l);
561   if (i>=0) delete_macro(i);
562   newmacro(s,l,0);
563 
564   /* possibly allow named arguments: -Dmacro(arg1,arg2)=... (no spaces) */
565   if (s[l]=='(') {
566     argc = 0;
567     do {
568       l++; i=l;
569       while (!isdelim(s[i])) i++;
570       if (s[i]!=',' && s[i]!=')') bug("invalid syntax in -D declaration");
571       if (i>l) argc++;
572       macros[nmacros].argnames =
573         realloc(macros[nmacros].argnames, (argc+1)*sizeof(char *));
574       if (i>l) {
575         macros[nmacros].argnames[argc-1]=malloc(i-l+1);
576         memcpy(macros[nmacros].argnames[argc-1], s+l, i-l);
577         macros[nmacros].argnames[argc-1][i-l]=0;
578       }
579       l = i;
580     } while (s[l]!=')');
581     l++;
582     macros[nmacros].nnamedargs = argc;
583     macros[nmacros].argnames[argc] = NULL;
584   }
585 
586   /* the macro definition afterwards ! */
587   if (s[l]=='=') l++;
588   else if (s[l]!=0) bug("invalid syntax in -D declaration");
589   macros[nmacros].macrolen=strlen(s+l);
590   macros[nmacros++].macrotext=my_strdup(s+l);
591 }
592 
readModeDescription(char ** args,struct MODE * mode,int ismeta)593 int readModeDescription(char **args,struct MODE *mode,int ismeta)
594 {
595   if (!(*(++args))) return 0;
596   mode->mStart=strnl(*args);
597   if (!(*(++args))) return 0;
598   mode->mEnd=strnl(*args);
599   if (!(*(++args))) return 0;
600   mode->mArgS=strnl(*args);
601   if (!(*(++args))) return 0;
602   mode->mArgSep=strnl(*args);
603   if (!(*(++args))) return 0;
604   mode->mArgE=strnl(*args);
605   if (!(*(++args))) return 0;
606   mode->stackchar=strnl(*args);
607   if (!(*(++args))) return 0;
608   mode->unstackchar=strnl(*args);
609   if (ismeta) return 1;
610   if (!(*(++args))) return 0;
611   mode->mArgRef=strnl(*args);
612   if (!(*(++args))) return 0;
613   mode->quotechar=**args;
614   return 1;
615 }
616 
parse_comment_specif(char c)617 int parse_comment_specif(char c)
618 {
619   switch (c) {
620   case 'I': case 'i': return FLAG_IGNORE;
621   case 'c': return FLAG_COMMENT;
622   case 's': return FLAG_STRING;
623   case 'q': return OUTPUT_TEXT;
624   case 'S': return FLAG_STRING|PARSE_MACROS;
625   case 'Q': return OUTPUT_TEXT|PARSE_MACROS;
626   case 'C': return FLAG_COMMENT|PARSE_MACROS;
627   default: bug("Invalid comment/string modifier"); return 0;
628   }
629 }
630 
add_comment(struct SPECS * S,const char * specif,char * start,char * end,char quote,char warn)631 void add_comment(struct SPECS *S,const char *specif,char *start,char *end,char quote,char warn)
632 {
633   struct COMMENT *p;
634 
635   if (*start==0) bug("Comment/string start delimiter must be non-empty");
636   for (p=S->comments;p!=NULL;p=p->next)
637     if (!strcmp(p->start,start)) {
638       if (strcmp(p->end,end)) /* already exists with a different end */
639         bug("Conflicting comment/string delimiter specifications");
640       free(p->start);
641       free(p->end);
642       break;
643     }
644 
645   if (p==NULL) {
646     p=malloc(sizeof *p);
647     p->next=S->comments;
648     S->comments=p;
649   }
650   p->start=start;
651   p->end=end;
652   p->quote=quote;
653   p->warn=warn;
654   if (strlen(specif)!=3) bug("Invalid comment/string modifier");
655   p->flags[FLAG_META]=parse_comment_specif(specif[0]);
656   p->flags[FLAG_USER]=parse_comment_specif(specif[1]);
657   p->flags[FLAG_TEXT]=parse_comment_specif(specif[2]);
658 }
659 
delete_comment(struct SPECS * S,char * start)660 void delete_comment(struct SPECS *S,char *start)
661 {
662   struct COMMENT *p,*q;
663 
664   q=NULL;
665   for (p=S->comments;p!=NULL;p=p->next) {
666     if (!strcmp(p->start,start)) {
667       if (q==NULL) S->comments=p->next;
668       else q->next=p->next;
669       free(p->start);
670       free(p->end);
671       free(p);
672       free(start);
673       return;
674     }
675     else q=p;
676   }
677   free(start);
678 }
679 
outchar(char c)680 void outchar(char c)
681 {
682   if (C->out->bufsize) {
683     if (C->out->len+1==C->out->bufsize) {
684       C->out->bufsize=C->out->bufsize*2;
685       C->out->buf=realloc(C->out->buf,C->out->bufsize);
686       if (C->out->buf==NULL) bug("Out of memory");
687     }
688     C->out->buf[C->out->len++]=c;
689   }
690   else {
691     if (dosmode&&(c==10)) {
692       fputc(13,C->out->f);
693       if (file_and_stdout)
694         fputc(13,stdout);
695     }
696     if (c!=13) {
697       fputc(c,C->out->f);
698       if (file_and_stdout)
699         fputc(c,stdout);
700     }
701   }
702 }
703 
sendout(const char * s,int l,int proc)704 void sendout(const char *s,int l,int proc) /* only process the quotechar, that's all */
705 {
706   int i;
707 
708   if (!commented[iflevel])
709     for (i=0;i<l;i++) {
710       if (proc&&(s[i]!=0)&&(s[i]==S->User.quotechar))
711         { i++; if (i==l) return; }
712       if (s[i]!=0) outchar(s[i]);
713     }
714   else
715     replace_definition_with_blank_lines(s, s+l-1, 0);
716 }
717 
extendBuf(int pos)718 void extendBuf(int pos)
719 {
720   char *p;
721   if (C->bufsize<=pos) {
722     C->bufsize+=pos; /* approx double */
723     p=malloc(C->bufsize);
724     memcpy(p,C->buf,C->len);
725     free(C->malloced_buf);
726     C->malloced_buf=C->buf=p;
727     if (C->buf==NULL) bug("Out of memory");
728   }
729 }
730 
getChar(int pos)731 char getChar(int pos)
732 {
733   static int lastchar = -666;
734   int c;
735 
736   if (lastchar == -666 && !strcmp(S->Meta.mEnd, "\n")) lastchar='\n';
737 
738   if (C->in==NULL) {
739     if (pos>=C->len) return 0;
740     else return C->buf[pos];
741   }
742   extendBuf(pos);
743   while (pos>=C->len) {
744     do { c=fgetc(C->in); } while (c==13);
745     if (lastchar=='\n') C->lineno++;
746     lastchar=c;
747     if (c==EOF) c=0;
748     C->buf[C->len++]=(char)c;
749   }
750   return C->buf[pos];
751 }
752 
whiteout(int * pos1,int * pos2)753 int whiteout(int *pos1,int *pos2) /* remove whitespace on both sides */
754 {
755   while ((*pos1<*pos2)&&iswhite(getChar(*pos1))) (*pos1)++;
756   while ((*pos1<*pos2)&&iswhite(getChar(*pos2-1))) (*pos2)--;
757   return (*pos1<*pos2);
758 }
759 
identifierEnd(int start)760 int identifierEnd(int start)
761 {
762   char c;
763 
764   c=getChar(start);
765   if (c==0) return start;
766   if (c==S->User.quotechar) {
767     c=getChar(start+1);
768     if (c==0) return (start+1);
769     if (isdelim(c)) return (start+2);
770     start+=2;
771     c=getChar(start);
772   }
773   while (!isdelim(c)) c=getChar(++start);
774   return start;
775 }
776 
iterIdentifierEnd(int start)777 int iterIdentifierEnd(int start)
778 {
779   int x;
780   while(1) {
781     x=identifierEnd(start);
782     if (x==start) return x;
783     start=x;
784   }
785 }
786 
IsInCharset(CHARSET_SUBSET x,int c)787 int IsInCharset(CHARSET_SUBSET x,int c)
788 {
789   return (x[c>>LOG_LONG_BITS] & 1L<<(c&((1<<LOG_LONG_BITS)-1)))!=0;
790 }
791 
matchSequence(const char * s,int * pos)792 int matchSequence(const char *s,int *pos)
793 {
794   int i=*pos;
795   int match;
796   char c;
797 
798   while (*s!=0) {
799     if (!((*s)&0x60)) { /* special sequences */
800       match=1;
801       switch((*s)&0x1f) {
802       case '\001':
803 	c=getChar(i++);
804 	if ((c!=' ')&&(c!='\t'))
805 	  { match=0; break; }
806       case '\002':
807 	i--;
808 	do { c=getChar(++i); } while ((c==' ')||(c=='\t'));
809 	break;
810       case '\003':
811 	c=getChar(i++);
812 	if ((c!=' ')&&(c!='\t')&&(c!='\n'))
813 	  { match=0; break; }
814       case '\004':
815 	i--;
816 	do { c=getChar(++i); } while ((c==' ')||(c=='\t')||(c=='\n'));
817 	break;
818       case '\006':
819 	c=getChar(i++);
820 	match = ((c>='a')&&(c<='z')) || ((c>='A')&&(c<='Z'))
821 	  ||(c==' ')||(c=='\t')||(c=='\n');
822 	break;
823       case '\005':
824 	c=getChar(i++);
825 	match = ((c>='a')&&(c<='z')) || ((c>='A')&&(c<='Z')); break;
826       case '\007':
827 	c=getChar(i++);
828 	match = ((c>='0')&&(c<='9')); break;
829       case '\010':
830 	c=getChar(i++);
831 	match = IsInCharset(S->id_set,c); break;
832       case '\011':
833 	c=getChar(i++);
834 	match = (c=='\t'); break;
835       case '\012':
836 	c=getChar(i++);
837 	match = (c=='\n'); break;
838       case '\013':
839 	c=getChar(i++);
840 	match = IsInCharset(S->op_set,c); break;
841       case '\014':
842 	c=getChar(i++);
843 	match = IsInCharset(S->ext_op_set,c) || IsInCharset(S->op_set,c);
844 	break;
845       }
846       if ((*s)&0x80) match=!match;
847       if (!match) return 0;
848     }
849     else if (getChar(i++)!=*s) return 0;
850     s++;
851   }
852   *pos=i;
853   return 1;
854 }
855 
matchEndSequence(const char * s,int * pos)856 int matchEndSequence(const char *s,int *pos)
857 {
858   if (*s==0) return 1;
859   /* if terminator is \n and we're at end of input, let it be... */
860   if (getChar(*pos)==0 && s[0]=='\n' && s[1]==0) return 1;
861   if (!matchSequence(s,pos)) return 0;
862   if (S->preservelf&&iswhite(getChar(*pos-1))) (*pos)--;
863   return 1;
864 }
865 
matchStartSequence(const char * s,int * pos)866 int matchStartSequence(const char *s,int *pos)
867 {
868   char c;
869   int match;
870 
871   if (!((*s)&0x60)) { /* special sequences from prev. context */
872     c=getChar(*pos-1);
873     match=1;
874     if (*s==0) return 1;
875     switch((*s)&0x1f) {
876     case '\001':
877       if ((c!=' ')&&(c!='\t')) {
878 	match=0;
879 	break;
880       }
881     case '\002':
882       break;
883     case '\003':
884       if ((c!=' ')&&(c!='\t')&&(c!='\n')) {
885 	match=0;
886 	break;
887       }
888     case '\004':
889       break;
890     case '\006':
891       if ((c==' ')||(c=='\t')||(c=='\n'))
892 	break;
893     case '\005':
894       match = ((c>='a')&&(c<='z')) || ((c>='A')&&(c<='Z'));
895       break;
896     case '\007':
897       match = ((c>='0')&&(c<='9'));
898       break;
899     case '\010':
900       match = IsInCharset(S->id_set,c);
901       break;
902     case '\011':
903       match = (c=='\t');
904       break;
905     case '\012':
906       match = (c=='\n');
907       break;
908     case '\013':
909       match = IsInCharset(S->op_set,c);
910       break;
911     case '\014':
912       match = IsInCharset(S->ext_op_set,c) || IsInCharset(S->op_set,c);
913       break;
914     }
915     if ((*s)&0x80) match=!match;
916     if (!match) return 0;
917     s++;
918   }
919   return matchSequence(s,pos);
920 }
921 
AddToCharset(CHARSET_SUBSET x,int c)922 void AddToCharset(CHARSET_SUBSET x,int c)
923 {
924   x[c>>LOG_LONG_BITS] |= 1L<<(c&((1<<LOG_LONG_BITS)-1));
925 }
926 
MakeCharsetSubset(unsigned char * s)927 CHARSET_SUBSET MakeCharsetSubset(unsigned char *s)
928 {
929   CHARSET_SUBSET x;
930   int i;
931   unsigned char c;
932 
933   x=malloc(CHARSET_SUBSET_LEN*sizeof(unsigned long));
934   for (i=0;i<CHARSET_SUBSET_LEN;i++) x[i]=0;
935   while (*s!=0) {
936     if (!((*s)&0x60)) { /* special sequences */
937       if ((*s)&0x80) bug("negated special sequences not allowed in charset specifications");
938       switch((*s)&0x1f) {
939       case '\002':  /* \w, \W, \i, \o, \O not allowed */
940       case '\004':
941       case '\010':
942       case '\013':
943       case '\014':
944 	bug("special sequence not allowed in charset specification");
945       case '\003':
946 	AddToCharset(x,'\n');
947       case '\001':
948 	AddToCharset(x,' ');
949       case '\011':
950 	AddToCharset(x,'\t');
951 	break;
952       case '\006':
953 	AddToCharset(x,'\n');
954 	AddToCharset(x,' ');
955 	AddToCharset(x,'\t');
956       case '\005':
957 	for (c='A';c<='Z';c++) AddToCharset(x,c);
958 	for (c='a';c<='z';c++) AddToCharset(x,c);
959 	break;
960       case '\007':
961 	for (c='0';c<='9';c++) AddToCharset(x,c);
962 	break;
963       case '\012':
964 	AddToCharset(x,'\n');
965 	break;
966       }
967     }
968     else if ((s[1]=='-')&&((s[2]&0x60)!=0)&&(s[2]>=*s)) {
969       for (c=*s;c<=s[2];c++) AddToCharset(x,c);
970       s+=2;
971     }
972     else AddToCharset(x,*s);
973     s++;
974   }
975   return x;
976 }
977 
978 
idequal(const char * b,int l,const char * s)979 int idequal(const char *b,int l,const char *s)
980 {
981   int i;
982 
983   if ((int)strlen(s)!=l) return 0;
984   for (i=0;i<l;i++) if (b[i]!=s[i]) return 0;
985   return 1;
986 }
987 
findIdent(const char * b,int l)988 int findIdent(const char *b,int l)
989 {
990   int i;
991 
992   for (i=0;i<nmacros;i++)
993     if (idequal(b,l,macros[i].username)) return i;
994   return -1;
995 }
996 
findNamedArg(const char * b,int l)997 int findNamedArg(const char *b,int l)
998 {
999   char *s;
1000   int i;
1001 
1002   for (i=0;;i++) {
1003     s=C->namedargs[i];
1004     if (s==NULL) return -1;
1005     if (idequal(b,l,s)) return i;
1006   }
1007 }
1008 
shiftIn(int l)1009 void shiftIn(int l)
1010 {
1011   int i;
1012 
1013   if (l<=1) return;
1014   l--;
1015   if (l>=C->len) C->len=0;
1016   else {
1017     if (C->len-l>100) { /* we want to shrink that buffer */
1018       C->buf+=l; C->bufsize-=l;
1019     } else
1020       for (i=l;i<C->len;i++) C->buf[i-l]=C->buf[i];
1021     C->len-=l;
1022     C->eof=(C->buf[0]==0);
1023   }
1024   if (C->len<=1) {
1025     if (C->in==NULL) C->eof=1;
1026     else C->eof=feof(C->in);
1027   }
1028 }
1029 
initthings(int argc,char ** argv)1030 void initthings(int argc, char **argv)
1031 {
1032   char **arg, *s;
1033   int i,isinput,isoutput,ishelp,ismode,hasmeta,usrmode;
1034 
1035   DefaultOp=MakeCharsetSubset(DEFAULT_OP_STRING);
1036   PrologOp=MakeCharsetSubset(PROLOG_OP_STRING);
1037   DefaultExtOp=MakeCharsetSubset(DEFAULT_OP_PLUS);
1038   DefaultId=MakeCharsetSubset(DEFAULT_ID_STRING);
1039 
1040   nmacros=0;
1041   nalloced=31;
1042   macros=malloc(nalloced*sizeof *macros);
1043 
1044   S=malloc(sizeof *S);
1045   S->User=CUser;
1046   S->Meta=CMeta;
1047   S->comments=NULL;
1048   S->stack_next=NULL;
1049   S->preservelf=0;
1050   S->op_set=DefaultOp;
1051   S->ext_op_set=DefaultExtOp;
1052   S->id_set=DefaultId;
1053 
1054   C=malloc(sizeof *C);
1055   C->in=stdin;
1056   C->argc=0;
1057   C->argv=NULL;
1058   C->filename=my_strdup("stdin");
1059   C->out=malloc(sizeof *(C->out));
1060   C->out->f=stdout;
1061   C->out->bufsize=0;
1062   C->lineno=1;
1063   isinput=isoutput=ismode=ishelp=hasmeta=usrmode=0;
1064   nincludedirs=0;
1065   C->bufsize=80;
1066   C->len=0;
1067   C->buf=C->malloced_buf=malloc(C->bufsize);
1068   C->eof=0;
1069   C->namedargs=NULL;
1070   C->in_comment=0;
1071   C->ambience=FLAG_TEXT;
1072   C->may_have_args=0;
1073   commented[0]=0;
1074   iflevel=0;
1075   execallowed=0;
1076   autoswitch=0;
1077   dosmode=DEFAULT_CRLF;
1078 
1079   for (arg=argv+1;*arg;arg++) {
1080     if (strcmp(*arg, "--help") == 0 || strcmp(*arg, "-h") == 0) {
1081       usage();
1082       exit(EXIT_SUCCESS);
1083     }
1084     if (strcmp(*arg, "--version") == 0) {
1085       display_version();
1086       exit(EXIT_SUCCESS);
1087     }
1088 #define DEPRECATED_WARNING fprintf(stderr, "gpp: warning: deprecated option `%s'; use `-%s' instead\n", *arg, *arg)
1089     if (strcmp(*arg, "-nostdinc") == 0) {
1090       DEPRECATED_WARNING;
1091       NoStdInc = 1;
1092       continue;
1093     }
1094     if (strcmp(*arg, "-nocurinc") == 0) {
1095       DEPRECATED_WARNING;
1096       NoCurIncFirst = 1;
1097       continue;
1098     }
1099     if (strcmp(*arg, "-curdirinclast") == 0) {
1100       DEPRECATED_WARNING;
1101       CurDirIncLast = 1;
1102       NoCurIncFirst = 1;
1103       continue;
1104     }
1105     if (strcmp(*arg, "-includemarker") == 0) {
1106       DEPRECATED_WARNING;
1107       if (!(*(++arg))) {
1108 	usage();
1109 	exit(EXIT_FAILURE);
1110       }
1111       construct_include_directive_marker(&include_directive_marker, *arg);
1112       continue;
1113     }
1114     if (strcmp(*arg, "--include") == 0) {
1115       if (!(*(++arg))) {
1116         usage();
1117         exit(EXIT_FAILURE);
1118       }
1119       IncludeFile = *arg;
1120       continue;
1121     }
1122     if (strcmp(*arg, "-warninglevel") == 0) {
1123       DEPRECATED_WARNING;
1124       if (!(*(++arg))) {usage(); exit(EXIT_FAILURE);}
1125       WarningLevel = atoi(*arg);
1126       continue;
1127     }
1128     if (strcmp(*arg, "--nostdinc") == 0) {
1129       NoStdInc = 1;
1130       continue;
1131     }
1132     if (strcmp(*arg, "--nocurinc") == 0) {
1133       NoCurIncFirst = 1;
1134       continue;
1135     }
1136     if (strcmp(*arg, "--curdirinclast") == 0) {
1137       CurDirIncLast = 1;
1138       NoCurIncFirst = 1;
1139       continue;
1140     }
1141     if (strcmp(*arg, "--includemarker") == 0) {
1142       if (!(*(++arg))) {
1143 	usage();
1144 	exit(EXIT_FAILURE);
1145       }
1146       construct_include_directive_marker(&include_directive_marker, *arg);
1147       continue;
1148     }
1149     if (strcmp(*arg, "--warninglevel") == 0) {
1150       if (!(*(++arg))) {usage(); exit(EXIT_FAILURE);}
1151       WarningLevel = atoi(*arg);
1152       continue;
1153     }
1154 
1155     if (**arg=='+') {
1156       switch((*arg)[1]) {
1157       case 'c':
1158 	s=(*arg)+2;
1159 	if (*s==0) s="ccc";
1160 	if (!(*(++arg))) {usage(); exit(EXIT_FAILURE);}
1161 	if (!(*(++arg))) {usage(); exit(EXIT_FAILURE);}
1162 	add_comment(S,s,strnl(*(arg-1)),strnl(*arg),0,0);
1163 	break;
1164       case 's':
1165 	s=(*arg)+2;
1166 	if (*s==0) s="sss";
1167 	if (!(*(++arg))) {usage(); exit(EXIT_FAILURE);}
1168 	if (!(*(++arg))) {usage(); exit(EXIT_FAILURE);}
1169 	if (!(*(++arg))) {usage(); exit(EXIT_FAILURE);}
1170 	add_comment(S,s,strnl(*(arg-2)),strnl(*(arg-1)),**arg,0);
1171 	break;
1172       case 'z':
1173 	dosmode=0;
1174 	break;
1175       case 'n':
1176         S->preservelf=0;
1177         break;
1178       default: ishelp=1;
1179       }
1180     }
1181     else if (**arg!='-') {
1182       ishelp|=isinput; isinput=1;
1183       C->in=fopen(*arg,"r");
1184       free(C->filename); C->filename=my_strdup(*arg);
1185       if (C->in==NULL) bug("Cannot open input file");
1186     }
1187     else switch((*arg)[1]) {
1188     case 'I':
1189       if (nincludedirs==MAXINCL)
1190 	bug("too many include directories");
1191       if ((*arg)[2]==0) {
1192 	if (!(*(++arg))) {usage(); exit(EXIT_FAILURE);}
1193 	includedir[nincludedirs++]=my_strdup(*arg);
1194       }
1195       else includedir[nincludedirs++]=my_strdup((*arg)+2);
1196       break;
1197     case 'C':
1198       ishelp|=ismode|hasmeta|usrmode; ismode=1;
1199       S->User=KUser; S->Meta=KMeta;
1200       S->preservelf=1;
1201       add_comment(S,"ccc",my_strdup("/*"),my_strdup("*/"),0,0);
1202       add_comment(S,"ccc",my_strdup("//"),my_strdup("\n"),0,0);
1203       add_comment(S,"ccc",my_strdup("\\\n"),my_strdup(""),0,0);
1204       add_comment(S,"sss",my_strdup("\""),my_strdup("\""),'\\','\n');
1205       add_comment(S,"sss",my_strdup("'"),my_strdup("'"),'\\','\n');
1206       break;
1207     case 'P':
1208       ishelp|=ismode|hasmeta|usrmode; ismode=1;
1209       S->User=KUser; S->Meta=KMeta;
1210       S->preservelf=1;
1211       S->op_set=PrologOp;
1212       add_comment(S,"css",my_strdup("\213/*"),my_strdup("*/"),0,0); /* \!o */
1213       add_comment(S,"cii",my_strdup("\\\n"),my_strdup(""),0,0);
1214       add_comment(S,"css",my_strdup("%"),my_strdup("\n"),0,0);
1215       add_comment(S,"sss",my_strdup("\""),my_strdup("\""),0,'\n');
1216       add_comment(S,"sss",my_strdup("\207'"),my_strdup("'"),0,'\n'); /* \!# */
1217       break;
1218     case 'T':
1219       ishelp|=ismode|hasmeta|usrmode; ismode=1;
1220       S->User=S->Meta=Tex;
1221       break;
1222     case 'H':
1223       ishelp|=ismode|hasmeta|usrmode; ismode=1;
1224       S->User=S->Meta=Html;
1225       break;
1226     case 'X':
1227       ishelp|=ismode|hasmeta|usrmode; ismode=1;
1228       S->User=S->Meta=XHtml;
1229       break;
1230     case 'U':
1231       ishelp|=ismode|usrmode; usrmode=1;
1232       if (!readModeDescription(arg,&(S->User),0))
1233 	  {usage(); exit(EXIT_FAILURE);}
1234       arg+=9;
1235       if (!hasmeta) S->Meta=S->User;
1236       break;
1237     case 'M':
1238       ishelp|=ismode|hasmeta; hasmeta=1;
1239       if (!readModeDescription(arg,&(S->Meta),1))
1240 	  {usage(); exit(EXIT_FAILURE);}
1241       arg+=7;
1242       break;
1243     case 'O':
1244       file_and_stdout = 1;
1245     case 'o':
1246       if (!(*(++arg)))
1247 	  {usage(); exit(EXIT_FAILURE);}
1248       ishelp|=isoutput; isoutput=1;
1249       C->out->f=fopen(*arg,"w");
1250       if (C->out->f==NULL) bug("Cannot create output file");
1251       break;
1252     case 'D':
1253       if ((*arg)[2]==0) {
1254   	if (!(*(++arg)))
1255 	  {usage(); exit(EXIT_FAILURE);}
1256 	s=strnl0(*arg);
1257       }
1258       else s=strnl0((*arg)+2);
1259       parseCmdlineDefine(s); free(s); break;
1260     case 'x':
1261       execallowed=1;
1262       break;
1263     case 'n':
1264       S->preservelf=1;
1265       break;
1266     case 'z':
1267       dosmode=1;
1268       break;
1269     case 'c':
1270     case 's':
1271       if (!(*(++arg)))
1272 	  {usage(); exit(EXIT_FAILURE);}
1273       delete_comment(S,strnl(*arg));
1274       break;
1275     case 'm':
1276       autoswitch=1; break;
1277     default:
1278       ishelp=1;
1279     }
1280     if (hasmeta&&!usrmode) {usage(); exit(EXIT_FAILURE);}
1281     if (ishelp) {usage(); exit(EXIT_FAILURE);}
1282   }
1283 
1284 #ifndef WIN_NT
1285   if ((nincludedirs==0) && !NoStdInc) {
1286     includedir[0]=my_strdup("/usr/include");
1287     nincludedirs=1;
1288   }
1289 #endif
1290 
1291   for (i=0;i<nmacros;i++) {
1292     if (macros[i].define_specs == NULL)
1293       macros[i].define_specs=CloneSpecs(S);
1294     lookupArgRefs(i); /* for macro aliasing */
1295   }
1296 }
1297 
findCommentEnd(const char * endseq,char quote,char warn,int pos,int flags)1298 int findCommentEnd(const char *endseq,char quote,char warn,int pos,int flags)
1299 {
1300   int i;
1301   char c;
1302 
1303   while (1) {
1304     c=getChar(pos);
1305     i=pos;
1306     if (matchEndSequence(endseq,&i)) return pos;
1307     if (c==0) bug("Input ended while scanning a comment/string");
1308     if (c==warn) {
1309       warn=0;
1310       if (WarningLevel > 1)
1311 	warning("possible comment/string termination problem");
1312     }
1313     if (c==quote) pos+=2;
1314     else if ((flags&PARSE_MACROS)&&(c==S->User.quotechar)) pos+=2;
1315     else pos++;
1316   }
1317 }
1318 
SkipPossibleComments(int * pos,int cmtmode,int silentonly)1319 void SkipPossibleComments(int *pos,int cmtmode,int silentonly)
1320 {
1321   int found;
1322   struct COMMENT *c;
1323 
1324   if (C->in_comment) return;
1325   do {
1326     found=0;
1327     if (getChar(*pos)==0) return; /* EOF */
1328     for (c=S->comments;c!=NULL;c=c->next)
1329       if (!(c->flags[cmtmode]&FLAG_IGNORE))
1330         if (!silentonly||(c->flags[cmtmode]==FLAG_COMMENT))
1331           if (matchStartSequence(c->start,pos)) {
1332             *pos=findCommentEnd(c->end,c->quote,c->warn,*pos,c->flags[cmtmode]);
1333             matchEndSequence(c->end,pos);
1334             found=1;
1335             break;
1336           }
1337   } while (found);
1338 }
1339 
1340 /* look for a possible user macro.
1341    Input :  idstart = scan start
1342             idcheck = check id for long macro forms before splicing args ?
1343             cmtmode = comment mode (FLAG_META or FLAG_USER)
1344    Output : idstart/idend = macro name location
1345             sh_end/lg_end = macro form end (-1 if no match)
1346             argb/arge     = argument locations for long form
1347             argc          = argument count for long form
1348             id            = macro id, if idcheck was set at input
1349 */
1350 
SplicePossibleUser(int * idstart,int * idend,int * sh_end,int * lg_end,int * argb,int * arge,int * argc,int idcheck,int * id,int cmtmode)1351 int SplicePossibleUser(int *idstart,int *idend,int *sh_end,int *lg_end,
1352 		       int *argb,int *arge,int *argc,int idcheck,
1353 		       int *id,int cmtmode)
1354 {
1355   int match,k,pos;
1356 
1357   if (!matchStartSequence(S->User.mStart,idstart)) return 0;
1358   *idend=identifierEnd(*idstart);
1359   if ((*idend)&&!getChar(*idend-1)) return 0;
1360 
1361   /* look for args or no args */
1362   *sh_end=*idend;
1363   if (!matchEndSequence(S->User.mEnd,sh_end)) *sh_end=-1;
1364   pos=*idend;
1365   match=matchSequence(S->User.mArgS,&pos);
1366 
1367   if (idcheck) {
1368     *id=findIdent(C->buf+*idstart,*idend-*idstart);
1369     if (*id<0) match=0;
1370   }
1371   *lg_end=-1;
1372 
1373   if (match) {
1374     *argc=0;
1375     while (1) {
1376       if (*argc>=MAXARGS) bug("too many macro parameters");
1377       argb[*argc]=pos;
1378       k=0;
1379       while(1) { /* look for mArgE, mArgSep, or comment-start */
1380         pos=iterIdentifierEnd(pos);
1381         SkipPossibleComments(&pos,cmtmode,0);
1382         if (getChar(pos)==0) return (*sh_end>=0); /* EOF */
1383         if (strchr(S->User.stackchar,getChar(pos))) k++;
1384         if (k) { if (strchr(S->User.unstackchar,getChar(pos))) k--; }
1385         else {
1386           arge[*argc]=pos;
1387           if (matchSequence(S->User.mArgSep,&pos)) { match=0; break; }
1388           if (matchEndSequence(S->User.mArgE,&pos))
1389             { match=1; break; }
1390         }
1391         pos++; /* nothing matched, go forward */
1392       }
1393       (*argc)++;
1394       if (match) { /* no more args */
1395         *lg_end=pos;
1396         break;
1397       }
1398     }
1399   }
1400   return ((*lg_end>=0)||(*sh_end>=0));
1401 }
1402 
findMetaArgs(int start,int * p1b,int * p1e,int * p2b,int * p2e,int * endm,int * argc,int * argb,int * arge)1403 int findMetaArgs(int start,int *p1b,int *p1e,int *p2b,int *p2e,int *endm,int *argc,int *argb,int *arge)
1404 {
1405   int pos,k;
1406   int hyp_end1,hyp_end2;
1407 
1408   /* look for mEnd or mArgS */
1409   pos=start;
1410   if (!matchSequence(S->Meta.mArgS,&pos)) {
1411     if (!matchEndSequence(S->Meta.mEnd,&pos)) return -1;
1412     *endm=pos;
1413     return 0;
1414   }
1415   *p1b=pos;
1416 
1417   /* special syntax for #define : 1st arg is a macro call */
1418   if ((*argc)&&SplicePossibleUser(&pos,p1e,&hyp_end1,&hyp_end2,
1419                                   argb,arge,argc,0,NULL,FLAG_META)) {
1420     *p1b=pos;
1421     if (hyp_end2>=0) pos=hyp_end2; else { pos=hyp_end1; *argc=0; }
1422     if (!matchSequence(S->Meta.mArgSep,&pos)) {
1423       if (!matchEndSequence(S->Meta.mArgE,&pos))
1424 	bug("#define/#defeval requires an identifier or a single macro call");
1425       *endm=pos;
1426       return 1;
1427     }
1428   } else {
1429     *argc=0;
1430     k=0;
1431     while(1) { /* look for mArgE, mArgSep, or comment-start */
1432       pos=iterIdentifierEnd(pos);
1433       SkipPossibleComments(&pos,FLAG_META,0);
1434       if (getChar(pos)!=0 && strchr(S->Meta.stackchar,getChar(pos))) k++;
1435       if (k) {
1436         if (getChar(pos)!=0 && strchr(S->Meta.unstackchar,getChar(pos)))
1437           k--;
1438       } else {
1439         *p1e=pos;
1440         if (matchSequence(S->Meta.mArgSep,&pos)) break;
1441         if (matchEndSequence(S->Meta.mArgE,&pos)) {
1442           *endm=pos;
1443           return 1;
1444         }
1445       }
1446       if (getChar(pos)==0) bug("unfinished macro argument");
1447       pos++; /* nothing matched, go forward */
1448     }
1449   }
1450 
1451   *p2b=pos;
1452   k=0;
1453   while(1) { /* look for mArgE or comment-start */
1454     pos=iterIdentifierEnd(pos);
1455     SkipPossibleComments(&pos,FLAG_META,0);
1456     if (getChar(pos)!=0 && strchr(S->Meta.stackchar,getChar(pos))) k++;
1457     if (k) {
1458       if (getChar(pos)!=0 && strchr(S->Meta.unstackchar,getChar(pos)))
1459         k--;
1460     } else {
1461       *p2e=pos;
1462       if (matchEndSequence(S->Meta.mArgE,&pos)) break;
1463     }
1464     if (getChar(pos)==0) bug("unfinished macro");
1465     pos++; /* nothing matched, go forward */
1466   }
1467   *endm=pos;
1468   return 2;
1469 }
1470 
ProcessText(const char * buf,int l,int ambience)1471 char *ProcessText(const char *buf,int l,int ambience)
1472 {
1473   char *s;
1474   struct INPUTCONTEXT *T;
1475 
1476   if (l==0) { s=malloc(1); s[0]=0; return s;  }
1477   s=malloc(l+2);
1478   s[0]='\n';
1479   memcpy(s+1,buf,l);
1480   s[l+1]=0;
1481   T=C;
1482   C=malloc(sizeof *C);
1483   C->out=malloc(sizeof *(C->out));
1484   C->in=NULL;
1485   C->argc=T->argc;
1486   C->argv=T->argv;
1487   C->filename=T->filename;
1488   C->out->buf=malloc(80);
1489   C->out->len=0;
1490   C->out->bufsize=80;
1491   C->out->f=NULL;
1492   C->lineno=T->lineno;
1493   C->bufsize=l+2;
1494   C->len=l+1;
1495   C->buf=C->malloced_buf=s;
1496   C->eof=0;
1497   C->namedargs=T->namedargs;
1498   C->in_comment=T->in_comment;
1499   C->ambience=ambience;
1500   C->may_have_args=T->may_have_args;
1501 
1502   ProcessContext();
1503   outchar(0); /* note that outchar works with the half-destroyed context ! */
1504   s=C->out->buf;
1505   free(C->out);
1506   free(C);
1507   C=T;
1508   return s;
1509 }
1510 
SpliceInfix(const char * buf,int pos1,int pos2,char * sep,int * spl1,int * spl2)1511 int SpliceInfix(const char *buf,int pos1,int pos2,char *sep,int *spl1,int *spl2)
1512 {
1513   int pos,numpar,l;
1514   const char *p;
1515 
1516   numpar=0; l=strlen(sep);
1517   for (pos=pos2-1,p=buf+pos;pos>=pos1;pos--,p--) {
1518     if (*p==')') numpar++;
1519     if (*p=='(') numpar--;
1520     if (numpar<0) return 0;
1521     if ((numpar==0)&&(pos2-pos>=l)&&!strncmp(p,sep,l))
1522       { *spl1=pos; *spl2=pos+l; return 1; }
1523   }
1524   return 0;
1525 }
1526 
DoArithmEval(char * buf,int pos1,int pos2,int * result)1527 int DoArithmEval(char *buf,int pos1,int pos2,int *result)
1528 {
1529   int spl1,spl2,result1,result2,l;
1530   char c,*p;
1531 
1532   while ((pos1<pos2)&&iswhite(buf[pos1])) pos1++;
1533   while ((pos1<pos2)&&iswhite(buf[pos2-1])) pos2--;
1534   if (pos1==pos2) return 0;
1535 
1536   /* look for C operators starting with lowest precedence */
1537 
1538   if (SpliceInfix(buf,pos1,pos2,"||",&spl1,&spl2)) {
1539     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1540         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1541     *result=result1||result2;
1542     return 1;
1543   }
1544 
1545   if (SpliceInfix(buf,pos1,pos2,"&&",&spl1,&spl2)) {
1546     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1547         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1548     *result=result1&&result2;
1549     return 1;
1550   }
1551 
1552   if (SpliceInfix(buf,pos1,pos2,"|",&spl1,&spl2)) {
1553     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1554         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1555     *result=result1|result2;
1556     return 1;
1557   }
1558 
1559   if (SpliceInfix(buf,pos1,pos2,"^",&spl1,&spl2)) {
1560     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1561         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1562     *result=result1^result2;
1563     return 1;
1564   }
1565 
1566   if (SpliceInfix(buf,pos1,pos2,"&",&spl1,&spl2)) {
1567     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1568         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1569     *result=result1&result2;
1570     return 1;
1571   }
1572 
1573   if (SpliceInfix(buf,pos1,pos2,"!=",&spl1,&spl2)) {
1574     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1575         !DoArithmEval(buf,spl2,pos2,&result2)) {
1576       /* revert to string comparison */
1577       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
1578       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
1579       if (spl1-pos1!=pos2-spl2) *result=1;
1580       else *result=(strncmp(buf+pos1,buf+spl2,spl1-pos1)!=0);
1581     }
1582     else *result=(result1!=result2);
1583     return 1;
1584   }
1585 
1586   if (SpliceInfix(buf,pos1,pos2,"==",&spl1,&spl2)) {
1587     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1588         !DoArithmEval(buf,spl2,pos2,&result2)) {
1589       /* revert to string comparison */
1590       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
1591       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
1592       if (spl1-pos1!=pos2-spl2) *result=0;
1593       else *result=(strncmp(buf+pos1,buf+spl2,spl1-pos1)==0);
1594     }
1595     else *result=(result1==result2);
1596     return 1;
1597   }
1598 
1599   if (SpliceInfix(buf,pos1,pos2,"=~",&spl1,&spl2)) {
1600 #if ! HAVE_FNMATCH_H
1601     bug("globbing support has not been compiled in");
1602 #endif
1603     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1604         !DoArithmEval(buf,spl2,pos2,&result2)) {
1605       char *str1,*str2;
1606 
1607       /* revert to string comparison */
1608       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
1609       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
1610       str1=strdup(buf+pos1);
1611       str1[spl1-pos1]='\0';
1612       str2=strdup(buf+spl2);
1613       str2[pos2-spl2]='\0';
1614       *result=(fnmatch(str2,str1,0)==0);
1615       free(str1);
1616       free(str2);
1617     }
1618     else *result=(result1==result2);
1619     return 1;
1620   }
1621 
1622   if (SpliceInfix(buf,pos1,pos2,">=",&spl1,&spl2)) {
1623     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1624         !DoArithmEval(buf,spl2,pos2,&result2)) {
1625       /* revert to string comparison */
1626       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
1627       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
1628       l=spl1-pos1; if (l>pos2-spl2) l=pos2-spl2;
1629       result1=strncmp(buf+pos1,buf+spl2,l);
1630       *result=(result1>0) || ((result1==0) && (spl1-pos1>=pos2-spl2));
1631     }
1632     else *result=(result1>=result2);
1633     return 1;
1634   }
1635 
1636   if (SpliceInfix(buf,pos1,pos2,">",&spl1,&spl2)) {
1637     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1638         !DoArithmEval(buf,spl2,pos2,&result2)) {
1639       /* revert to string comparison */
1640       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
1641       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
1642       l=spl1-pos1; if (l>pos2-spl2) l=pos2-spl2;
1643       result1=strncmp(buf+pos1,buf+spl2,l);
1644       *result=(result1>0) || ((result1==0) && (spl1-pos1>pos2-spl2));
1645     }
1646     else *result=(result1>result2);
1647     return 1;
1648   }
1649 
1650   if (SpliceInfix(buf,pos1,pos2,"<=",&spl1,&spl2)) {
1651     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1652         !DoArithmEval(buf,spl2,pos2,&result2)) {
1653       /* revert to string comparison */
1654       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
1655       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
1656       l=spl1-pos1; if (l>pos2-spl2) l=pos2-spl2;
1657       result1=strncmp(buf+pos1,buf+spl2,l);
1658       *result=(result1<0) || ((result1==0) && (spl1-pos1<=pos2-spl2));
1659     }
1660     else *result=(result1<=result2);
1661     return 1;
1662   }
1663 
1664   if (SpliceInfix(buf,pos1,pos2,"<",&spl1,&spl2)) {
1665     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1666         !DoArithmEval(buf,spl2,pos2,&result2)) {
1667       /* revert to string comparison */
1668       while ((pos1<spl1)&&iswhite(buf[spl1-1])) spl1--;
1669       while ((pos2>spl2)&&iswhite(buf[spl2])) spl2++;
1670       l=spl1-pos1; if (l>pos2-spl2) l=pos2-spl2;
1671       result1=strncmp(buf+pos1,buf+spl2,l);
1672       *result=(result1<0) || ((result1==0) && (spl1-pos1<pos2-spl2));
1673     }
1674     else *result=(result1<result2);
1675     return 1;
1676   }
1677 
1678   if (SpliceInfix(buf,pos1,pos2,"+",&spl1,&spl2)) {
1679     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1680         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1681     *result=result1+result2;
1682     return 1;
1683   }
1684 
1685   if (SpliceInfix(buf,pos1,pos2,"-",&spl1,&spl2))
1686     if (spl1!=pos1) {
1687       if (!DoArithmEval(buf,pos1,spl1,&result1)||
1688           !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1689       *result=result1-result2;
1690       return 1;
1691     }
1692 
1693   if (SpliceInfix(buf,pos1,pos2,"*",&spl1,&spl2)) {
1694     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1695         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1696     *result=result1*result2;
1697     return 1;
1698   }
1699 
1700   if (SpliceInfix(buf,pos1,pos2,"/",&spl1,&spl2)) {
1701     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1702         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1703     if (result2==0) bug("Division by zero in expression");
1704     *result=result1/result2;
1705     return 1;
1706   }
1707 
1708   if (SpliceInfix(buf,pos1,pos2,"%",&spl1,&spl2)) {
1709     if (!DoArithmEval(buf,pos1,spl1,&result1)||
1710         !DoArithmEval(buf,spl2,pos2,&result2)) return 0;
1711     if (result2==0) bug("Division by zero in expression");
1712     *result=result1%result2;
1713     return 1;
1714   }
1715 
1716   if (buf[pos1]=='~') {
1717     if (!DoArithmEval(buf,pos1+1,pos2,&result1)) return 0;
1718     *result=~result1;
1719     return 1;
1720   }
1721 
1722   if (buf[pos1]=='!') {
1723     if (!DoArithmEval(buf,pos1+1,pos2,&result1)) return 0;
1724     *result=!result1;
1725     return 1;
1726   }
1727 
1728   if (buf[pos1]=='-') {
1729     if (!DoArithmEval(buf,pos1+1,pos2,&result1)) return 0;
1730     *result=-result1;
1731     return 1;
1732   }
1733 
1734   /* add the length() builtin to measure the length of the macro expansion */
1735   if (strncmp(buf+pos1,"length(",strlen("length("))==0) {
1736     if (buf[pos2-1]!=')') return 0;
1737     *result=pos2-pos1-strlen("length()");
1738     return 1;
1739   }
1740 
1741   if (buf[pos1]=='(') {
1742     if (buf[pos2-1]!=')') return 0;
1743     return DoArithmEval(buf,pos1+1,pos2-1,result);
1744   }
1745 
1746   c=buf[pos2]; buf[pos2]=0;
1747   *result=(int)strtol(buf+pos1,&p,0);
1748   buf[pos2]=c;
1749   return (p==buf+pos2);
1750 }
1751 
delete_macro(int i)1752 void delete_macro(int i)
1753 {
1754   int j;
1755   nmacros--;
1756   free(macros[i].username);
1757   free(macros[i].macrotext);
1758   if (macros[i].argnames!=NULL) {
1759     for (j=0;j<macros[i].nnamedargs;j++) free(macros[i].argnames[j]);
1760     free(macros[i].argnames);
1761     macros[i].argnames=NULL;
1762   }
1763   FreeComments(macros[i].define_specs);
1764   free(macros[i].define_specs);
1765   memcpy(macros+i,macros+nmacros,sizeof(struct MACRO));
1766 }
1767 
ArithmEval(int pos1,int pos2)1768 char *ArithmEval(int pos1,int pos2)
1769 {
1770   char *s,*t;
1771   int i;
1772 
1773   /* first define the defined(...) operator */
1774   i=findIdent("defined",strlen("defined"));
1775   if (i>=0) warning("the defined(...) macro is already defined");
1776   else {
1777     newmacro("defined",strlen("defined"),1);
1778     macros[nmacros].macrolen=0;
1779     macros[nmacros].macrotext=malloc(1);
1780     macros[nmacros].macrotext[0]=0;
1781     macros[nmacros].nnamedargs=-2; /* trademark of the defined(...) macro */
1782     nmacros++;
1783   }
1784   /* process the text in a usual way */
1785   s=ProcessText(C->buf+pos1,pos2-pos1,FLAG_META);
1786   /* undefine the defined(...) operator */
1787   if (i<0) {
1788     i=findIdent("defined",strlen("defined"));
1789     if ((i<0)||(macros[i].nnamedargs!=-2))
1790       warning("the defined(...) macro was redefined in expression");
1791     else delete_macro(i);
1792   }
1793 
1794   if (!DoArithmEval(s,0,strlen(s),&i)) return s; /* couldn't compute */
1795   t=malloc(MAX_GPP_NUM_SIZE);
1796   sprintf(t,"%d",i);
1797   free(s);
1798   return t;
1799 }
1800 
comment_or_white(int start,int end,int cmtmode)1801 int comment_or_white(int start,int end,int cmtmode)
1802 {
1803   char c;
1804 
1805   while (start<end) {
1806     SkipPossibleComments(&start,cmtmode,1);
1807     if (start<end) {
1808       c=getChar(start++);
1809       if ((c!=' ')&&(c!='\n')&&(c!='\t')) return 0;
1810     }
1811   }
1812   return 1;
1813 }
1814 
remove_comments(int start,int end,int cmtmode)1815 char *remove_comments(int start,int end,int cmtmode)
1816 {
1817   char *s,*t;
1818 
1819   t=s=malloc(end-start+1);
1820   while (start<end) {
1821     SkipPossibleComments(&start,cmtmode,1);
1822     if (start<end) {
1823       *t=getChar(start++);
1824       if ((*t==S->User.quotechar)&&(start<end)) { *(++t)=getChar(start++); }
1825       t++;
1826     }
1827   }
1828   *t=0;
1829   return s;
1830 }
1831 
SetStandardMode(struct SPECS * P,const char * opt)1832 void SetStandardMode(struct SPECS *P,const char *opt)
1833 {
1834   P->op_set=DefaultOp;
1835   P->ext_op_set=DefaultExtOp;
1836   P->id_set=DefaultId;
1837   FreeComments(P);
1838   if (!strcmp(opt,"C")||!strcmp(opt,"cpp")) {
1839     P->User=KUser; P->Meta=KMeta;
1840     P->preservelf=1;
1841     add_comment(P,"ccc",my_strdup("/*"),my_strdup("*/"),0,0);
1842     add_comment(P,"ccc",my_strdup("//"),my_strdup("\n"),0,0);
1843     add_comment(P,"ccc",my_strdup("\\\n"),my_strdup(""),0,0);
1844     add_comment(P,"sss",my_strdup("\""),my_strdup("\""),'\\','\n');
1845     add_comment(P,"sss",my_strdup("'"),my_strdup("'"),'\\','\n');
1846   }
1847   else if (!strcmp(opt,"TeX")||!strcmp(opt,"tex")) {
1848     P->User=Tex; P->Meta=Tex;
1849     P->preservelf=0;
1850   }
1851   else if (!strcmp(opt,"HTML")||!strcmp(opt,"html")) {
1852     P->User=Html; P->Meta=Html;
1853     P->preservelf=0;
1854   }
1855   else if (!strcmp(opt,"XHTML")||!strcmp(opt,"xhtml")) {
1856     P->User=XHtml; P->Meta=XHtml;
1857     P->preservelf=0;
1858   }
1859   else if (!strcmp(opt,"default")) {
1860     P->User=CUser; P->Meta=CMeta;
1861     P->preservelf=0;
1862   }
1863   else if (!strcmp(opt,"Prolog")||!strcmp(opt,"prolog")) {
1864     P->User=KUser; P->Meta=KMeta;
1865     P->preservelf=1;
1866     P->op_set=PrologOp;
1867     add_comment(P,"css",my_strdup("\213/*"),my_strdup("*/"),0,0); /* \!o */
1868     add_comment(P,"cii",my_strdup("\\\n"),my_strdup(""),0,0);
1869     add_comment(P,"css",my_strdup("%"),my_strdup("\n"),0,0);
1870     add_comment(P,"sss",my_strdup("\""),my_strdup("\""),0,'\n');
1871     add_comment(P,"sss",my_strdup("\207'"),my_strdup("'"),0,'\n');   /* \!# */
1872   }
1873   else bug("unknown standard mode");
1874 }
1875 
ProcessModeCommand(int p1start,int p1end,int p2start,int p2end)1876 void ProcessModeCommand(int p1start,int p1end,int p2start,int p2end)
1877 {
1878   struct SPECS *P;
1879   char *s,*p,*opt;
1880   int nargs,check_isdelim;
1881   char *args[10]; /* can't have more than 10 arguments */
1882 
1883   whiteout(&p1start,&p1end);
1884   if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
1885     bug("invalid #mode syntax");
1886   if (p2start<0) s=my_strdup("");
1887   else s=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
1888 
1889   /* argument parsing */
1890   p=s; opt=NULL;
1891   while (iswhite(*p)) p++;
1892   if ((*p!='"')&&(*p!=0)) {
1893     opt=p;
1894     while ((*p!=0)&&!iswhite(*p)) p++;
1895     if (*p!=0) {
1896       *(p++)=0;
1897       while (iswhite(*p)) p++;
1898     }
1899   }
1900   nargs=0;
1901   check_isdelim=!idequal(C->buf+p1start,p1end-p1start,"charset");
1902   while (*p!=0) {
1903     if (nargs==10) bug("too many arguments in #mode command");
1904     if (*(p++)!='"') bug("syntax error in #mode command (missing \" or trailing data)");
1905     args[nargs++]=p;
1906     p=strnl2(p,check_isdelim);
1907     while (iswhite(*p)) p++;
1908   }
1909 
1910   if (idequal(C->buf+p1start,p1end-p1start,"quote")) {
1911     if (opt||(nargs>1)) bug("syntax error in #mode quote command");
1912     if (nargs==0) args[0]="";
1913     S->stack_next->User.quotechar=args[0][0];
1914   }
1915   else if (idequal(C->buf+p1start,p1end-p1start,"comment")) {
1916     if ((nargs<2)||(nargs>4)) bug("syntax error in #mode comment command");
1917     if (!opt) opt="ccc";
1918     if (nargs<3) args[2]="";
1919     if (nargs<4) args[3]="";
1920     add_comment(S->stack_next,opt,my_strdup(args[0]),my_strdup(args[1]),args[2][0],args[3][0]);
1921   }
1922   else if (idequal(C->buf+p1start,p1end-p1start,"string")) {
1923     if ((nargs<2)||(nargs>4)) bug("syntax error in #mode string command");
1924     if (!opt) opt="sss";
1925     if (nargs<3) args[2]="";
1926     if (nargs<4) args[3]="";
1927     add_comment(S->stack_next,opt,my_strdup(args[0]),my_strdup(args[1]),args[2][0],args[3][0]);
1928   }
1929   else if (idequal(C->buf+p1start,p1end-p1start,"save")
1930 	   ||idequal(C->buf+p1start,p1end-p1start,"push")) {
1931     if ((opt!=NULL)||nargs) bug("too many arguments to #mode save");
1932     P=CloneSpecs(S->stack_next);
1933     P->stack_next=S->stack_next;
1934     S->stack_next=P;
1935   }
1936   else if (idequal(C->buf+p1start,p1end-p1start,"restore")
1937 	   ||idequal(C->buf+p1start,p1end-p1start,"pop")) {
1938     if ((opt!=NULL)||nargs) bug("too many arguments to #mode restore");
1939     P=S->stack_next->stack_next;
1940     if (P==NULL) bug("#mode restore without #mode save");
1941     FreeComments(S->stack_next);
1942     free(S->stack_next);
1943     S->stack_next=P;
1944   }
1945   else if (idequal(C->buf+p1start,p1end-p1start,"standard")) {
1946     if ((opt==NULL)||nargs) bug("syntax error in #mode standard");
1947     SetStandardMode(S->stack_next,opt);
1948   }
1949   else if (idequal(C->buf+p1start,p1end-p1start,"user")) {
1950     if ((opt!=NULL)||(nargs!=9)) bug("#mode user requires 9 arguments");
1951     S->stack_next->User.mStart=my_strdup(args[0]);
1952     S->stack_next->User.mEnd=my_strdup(args[1]);
1953     S->stack_next->User.mArgS=my_strdup(args[2]);
1954     S->stack_next->User.mArgSep=my_strdup(args[3]);
1955     S->stack_next->User.mArgE=my_strdup(args[4]);
1956     S->stack_next->User.stackchar=my_strdup(args[5]);
1957     S->stack_next->User.unstackchar=my_strdup(args[6]);
1958     S->stack_next->User.mArgRef=my_strdup(args[7]);
1959     S->stack_next->User.quotechar=args[8][0];
1960   }
1961   else if (idequal(C->buf+p1start,p1end-p1start,"meta")) {
1962     if ((opt!=NULL)&&!nargs&&!strcmp(opt,"user"))
1963       S->stack_next->Meta=S->stack_next->User;
1964     else {
1965       if ((opt!=NULL)||(nargs!=7)) bug("#mode meta requires 7 arguments");
1966       S->stack_next->Meta.mStart=my_strdup(args[0]);
1967       S->stack_next->Meta.mEnd=my_strdup(args[1]);
1968       S->stack_next->Meta.mArgS=my_strdup(args[2]);
1969       S->stack_next->Meta.mArgSep=my_strdup(args[3]);
1970       S->stack_next->Meta.mArgE=my_strdup(args[4]);
1971       S->stack_next->Meta.stackchar=my_strdup(args[5]);
1972       S->stack_next->Meta.unstackchar=my_strdup(args[6]);
1973     }
1974   }
1975   else if (idequal(C->buf+p1start,p1end-p1start,"preservelf")) {
1976     if ((opt==NULL)||nargs) bug("syntax error in #mode preservelf");
1977     if (!strcmp(opt,"1")||!my_strcasecmp(opt,"on")) S->stack_next->preservelf=1;
1978     else if (!strcmp(opt,"0")||!my_strcasecmp(opt,"off")) S->stack_next->preservelf=0;
1979     else bug("#mode preservelf requires on/off argument");
1980   }
1981   else if (idequal(C->buf+p1start,p1end-p1start,"nocomment")
1982 	   ||idequal(C->buf+p1start,p1end-p1start,"nostring")) {
1983     if ((opt!=NULL)||(nargs>1))
1984       bug("syntax error in #mode nocomment/nostring");
1985     if (nargs==0) FreeComments(S->stack_next);
1986     else delete_comment(S->stack_next,my_strdup(args[0]));
1987   }
1988   else if (idequal(C->buf+p1start,p1end-p1start,"charset")) {
1989     if ((opt==NULL)||(nargs!=1)) bug("syntax error in #mode charset");
1990     if (!my_strcasecmp(opt,"op"))
1991       S->stack_next->op_set=MakeCharsetSubset((unsigned char *)args[0]);
1992     else if (!my_strcasecmp(opt,"par"))
1993       S->stack_next->ext_op_set=MakeCharsetSubset((unsigned char *)args[0]);
1994     else if (!my_strcasecmp(opt,"id"))
1995       S->stack_next->id_set=MakeCharsetSubset((unsigned char *)args[0]);
1996     else bug("unknown charset subset name in #mode charset");
1997   }
1998   else bug("unrecognized #mode command");
1999 
2000   free(s);
2001 }
2002 
DoInclude(char * file_name)2003 static void DoInclude(char *file_name)
2004 {
2005   struct INPUTCONTEXT *N;
2006   char *incfile_name = NULL;
2007   FILE *f = NULL;
2008   int i, j;
2009   int len = strlen(file_name);
2010 
2011   /* if absolute path name is specified */
2012   if (file_name[0]==SLASH
2013 #ifdef WIN_NT
2014       || (isalpha(file_name[0]) && file_name[1]==':')
2015 #endif
2016       )
2017     f=fopen(file_name,"r");
2018   else /* search current dir, if this search isn't turned off */
2019     if (!NoCurIncFirst) {
2020       f = openInCurrentDir(file_name);
2021     }
2022 
2023   for (j=0;(f==NULL)&&(j<nincludedirs);j++) {
2024     incfile_name =
2025       realloc(incfile_name,len+strlen(includedir[j])+2);
2026     strcpy(incfile_name,includedir[j]);
2027     incfile_name[strlen(includedir[j])]=SLASH;
2028     /* extract the orig include filename */
2029     strcpy(incfile_name+strlen(includedir[j])+1, file_name);
2030     f=fopen(incfile_name,"r");
2031   }
2032   if (incfile_name!=NULL)
2033     free(incfile_name);
2034 
2035   /* If didn't find the file and "." is said to be searched last */
2036   if (f==NULL && CurDirIncLast) {
2037     f = openInCurrentDir(file_name);
2038   }
2039 
2040   if (f==NULL)
2041     bug("Requested include file not found");
2042 
2043   N=C;
2044   C=malloc(sizeof *C);
2045   C->in=f;
2046   C->argc=0;
2047   C->argv=NULL;
2048   C->filename=file_name;
2049   C->out=N->out;
2050   C->lineno=1;
2051   C->bufsize=80;
2052   C->len=0;
2053   C->buf=C->malloced_buf=malloc(C->bufsize);
2054   C->eof=0;
2055   C->namedargs=NULL;
2056   C->in_comment=0;
2057   C->ambience=FLAG_TEXT;
2058   C->may_have_args=0;
2059   PushSpecs(S);
2060   if (autoswitch) {
2061     if (!strcmp(file_name+strlen(file_name)-2,".h")
2062        || !strcmp(file_name+strlen(file_name)-2,".c"))
2063       SetStandardMode(S,"C");
2064   }
2065 
2066   /* Include marker before the included contents */
2067   write_include_marker(N->out->f, 1, C->filename, "1");
2068   ProcessContext();
2069   /* Include marker after the included contents */
2070   write_include_marker(N->out->f, N->lineno, N->filename, "2");
2071   /* Need to leave the blank line in lieu of #include, like cpp does */
2072   replace_directive_with_blank_line(N->out->f);
2073   free(C);
2074   PopSpecs();
2075   C=N;
2076 }
2077 
ParsePossibleMeta(void)2078 int ParsePossibleMeta(void)
2079 {
2080   int cklen,nameend;
2081   int id,expparams,nparam,i,j;
2082   int p1start,p1end,p2start,p2end,macend;
2083   int argc,argb[MAXARGS],arge[MAXARGS];
2084   char *tmpbuf;
2085 
2086   cklen=1;
2087   if (!matchStartSequence(S->Meta.mStart,&cklen)) return -1;
2088   nameend=identifierEnd(cklen);
2089   if (nameend&&!getChar(nameend-1)) return -1;
2090   id=0;
2091   argc=0; /* for #define with named args */
2092   if (idequal(C->buf+cklen,nameend-cklen,"define"))      /* check identifier */
2093     { id=1; expparams=2; argc=1; }
2094   else if (idequal(C->buf+cklen,nameend-cklen,"undef"))
2095     { id=2; expparams=1; }
2096   else if (idequal(C->buf+cklen,nameend-cklen,"ifdef"))
2097     { id=3; expparams=1; }
2098   else if (idequal(C->buf+cklen,nameend-cklen,"ifndef"))
2099     { id=4; expparams=1; }
2100   else if (idequal(C->buf+cklen,nameend-cklen,"else"))
2101     { id=5; expparams=0; }
2102   else if (idequal(C->buf+cklen,nameend-cklen,"endif"))
2103     { id=6; expparams=0; }
2104   else if (idequal(C->buf+cklen,nameend-cklen,"include"))
2105     { id=7; expparams=1; }
2106   else if (idequal(C->buf+cklen,nameend-cklen,"exec"))
2107     { id=8; expparams=1; }
2108   else if (idequal(C->buf+cklen,nameend-cklen,"defeval"))
2109     { id=9; expparams=2; argc=1; }
2110   else if (idequal(C->buf+cklen,nameend-cklen,"ifeq"))
2111     { id=10; expparams=2; }
2112   else if (idequal(C->buf+cklen,nameend-cklen,"ifneq"))
2113     { id=11; expparams=2; }
2114   else if (idequal(C->buf+cklen,nameend-cklen,"eval"))
2115     { id=12; expparams=1; }
2116   else if (idequal(C->buf+cklen,nameend-cklen,"if"))
2117     { id=13; expparams=1; }
2118   else if (idequal(C->buf+cklen,nameend-cklen,"mode"))
2119     { id=14; expparams=2; }
2120   else if (idequal(C->buf+cklen,nameend-cklen,"line"))
2121     { id=15; expparams=0; }
2122   else if (idequal(C->buf+cklen,nameend-cklen,"file"))
2123     { id=16; expparams=0; }
2124   else if (idequal(C->buf+cklen,nameend-cklen,"elif"))
2125     { id=17; expparams=1; }
2126   else if (idequal(C->buf+cklen,nameend-cklen,"error"))
2127     { id=18; expparams=1; }
2128   else if (idequal(C->buf+cklen,nameend-cklen,"warning"))
2129     { id=19; expparams=1; }
2130   else if (idequal(C->buf+cklen,nameend-cklen,"date"))
2131     { id=20; expparams=1; }
2132   else return -1;
2133 
2134   /* #MODE magic : define "..." to be C-style strings */
2135   if (id==14) {
2136     PushSpecs(S);
2137     S->preservelf=1;
2138     delete_comment(S,my_strdup("\""));
2139     add_comment(S,"sss",my_strdup("\""),my_strdup("\""),'\\','\n');
2140   }
2141 
2142   nparam=findMetaArgs(nameend,&p1start,&p1end,&p2start,&p2end,&macend,&argc,argb,arge);
2143   if (nparam==-1) return -1;
2144 
2145   if ((nparam==2)&&iswhitesep(S->Meta.mArgSep))
2146     if (comment_or_white(p2start,p2end,FLAG_META)) nparam=1;
2147   if ((nparam==1)&&iswhitesep(S->Meta.mArgS))
2148     if (comment_or_white(p1start,p1end,FLAG_META)) nparam=0;
2149   if (expparams&&!nparam) bug("Missing argument in meta-macro");
2150 
2151   switch(id) {
2152   case 1: /* DEFINE */
2153     if (!commented[iflevel]) {
2154       whiteout(&p1start,&p1end); /* recall comments are not allowed here */
2155       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
2156         bug("#define requires an identifier (A-Z,a-z,0-9,_ only)");
2157       /* buf starts 1 char before the macro */
2158       i=findIdent(C->buf+p1start,p1end-p1start);
2159       if (i>=0) delete_macro(i);
2160       newmacro(C->buf+p1start,p1end-p1start,1);
2161       if (nparam==1) { p2end=p2start=p1end; }
2162       replace_definition_with_blank_lines(C->buf+1,C->buf+p2end,S->preservelf);
2163       macros[nmacros].macrotext=remove_comments(p2start,p2end,FLAG_META);
2164       macros[nmacros].macrolen=strlen(macros[nmacros].macrotext);
2165       macros[nmacros].defined_in_comment=C->in_comment;
2166 
2167       if (argc) {
2168         for (j=0;j<argc;j++) whiteout(argb+j,arge+j);
2169         /* define with one empty argument */
2170         if ((argc==1)&&(arge[0]==argb[0])) argc=0;
2171         macros[nmacros].argnames=malloc((argc+1)*sizeof(char *));
2172         macros[nmacros].argnames[argc]=NULL;
2173       }
2174       macros[nmacros].nnamedargs=argc;
2175       for (j=0;j<argc;j++) {
2176         if ((argb[j]==arge[j])||(identifierEnd(argb[j])!=arge[j]))
2177           bug("#define with named args needs identifiers as arg names");
2178         macros[nmacros].argnames[j]=malloc(arge[j]-argb[j]+1);
2179         memcpy(macros[nmacros].argnames[j],C->buf+argb[j],arge[j]-argb[j]);
2180         macros[nmacros].argnames[j][arge[j]-argb[j]]=0;
2181       }
2182       lookupArgRefs(nmacros++);
2183     } else
2184       replace_directive_with_blank_line(C->out->f);
2185     break;
2186 
2187   case 2: /* UNDEF */
2188     replace_directive_with_blank_line(C->out->f);
2189     if (!commented[iflevel]) {
2190       if (nparam==2 && WarningLevel > 0)
2191 	warning("Extra argument to #undef ignored");
2192       whiteout(&p1start,&p1end);
2193       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
2194         bug("#undef requires an identifier (A-Z,a-z,0-9,_ only)");
2195       i=findIdent(C->buf+p1start,p1end-p1start);
2196       if (i>=0) delete_macro(i);
2197     }
2198     break;
2199 
2200   case 3: /* IFDEF */
2201     replace_directive_with_blank_line(C->out->f);
2202     iflevel++;
2203     if (iflevel==STACKDEPTH) bug("Too many nested #ifdefs");
2204     commented[iflevel]=commented[iflevel-1];
2205 
2206     if (!commented[iflevel]) {
2207       if (nparam==2 && WarningLevel > 0)
2208 	warning("Extra argument to #ifdef ignored");
2209       whiteout(&p1start,&p1end);
2210       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
2211 	bug("#ifdef requires an identifier (A-Z,a-z,0-9,_ only)");
2212       i=findIdent(C->buf+p1start,p1end-p1start);
2213       commented[iflevel]=(i==-1);
2214     }
2215     break;
2216 
2217   case 4: /* IFNDEF */
2218     replace_directive_with_blank_line(C->out->f);
2219     iflevel++;
2220     if (iflevel==STACKDEPTH) bug("Too many nested #ifdefs");
2221     commented[iflevel]=commented[iflevel-1];
2222     if (!commented[iflevel]) {
2223       if (nparam==2 && WarningLevel > 0)
2224 	warning("Extra argument to #ifndef ignored");
2225       whiteout(&p1start,&p1end);
2226       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
2227         bug("#ifndef requires an identifier (A-Z,a-z,0-9,_ only)");
2228       i=findIdent(C->buf+p1start,p1end-p1start);
2229       commented[iflevel]=(i!=-1);
2230     }
2231     break;
2232 
2233   case 5: /* ELSE */
2234     replace_directive_with_blank_line(C->out->f);
2235     if (!commented[iflevel] && (nparam>0) && WarningLevel > 0)
2236       warning("Extra argument to #else ignored");
2237     if (iflevel==0) bug("#else without #if");
2238     if (!commented[iflevel-1] && commented[iflevel]!=2)
2239       commented[iflevel]=!commented[iflevel];
2240     break;
2241 
2242   case 6: /* ENDIF */
2243     replace_directive_with_blank_line(C->out->f);
2244     if (!commented[iflevel] && (nparam>0) && WarningLevel > 0)
2245       warning("Extra argument to #endif ignored");
2246     if (iflevel==0) bug("#endif without #if");
2247     iflevel--;
2248     break;
2249 
2250   case 7: /* INCLUDE */
2251     if (!commented[iflevel]) {
2252       char *incfile_name;
2253 
2254       if (nparam==2 && WarningLevel > 0)
2255 	warning("Extra argument to #include ignored");
2256       if (!whiteout(&p1start,&p1end)) bug("Missing file name in #include");
2257       /* user may put "" or <> */
2258       if (((getChar(p1start)=='\"')&&(getChar(p1end-1)=='\"'))||
2259 	  ((getChar(p1start)=='<')&&(getChar(p1end-1)=='>')))
2260 	{ p1start++; p1end--; }
2261       if (p1start>=p1end) bug("Missing file name in #include");
2262       incfile_name=malloc(p1end-p1start+1);
2263       /* extract the orig include filename */
2264       for (i=0;i<p1end-p1start;i++)
2265 	incfile_name[i]=getChar(p1start+i);
2266       incfile_name[p1end-p1start]=0;
2267 
2268       DoInclude(incfile_name);
2269     } else
2270       replace_directive_with_blank_line(C->out->f);
2271     break;
2272 
2273   case 8: /* EXEC */
2274     if (!commented[iflevel]) {
2275       if (!execallowed)
2276 	warning("Not allowed to #exec. Command output will be left blank");
2277       else {
2278 	char *s,*t;
2279 	int c;
2280 	FILE *f;
2281 	s=ProcessText(C->buf+p1start,p1end-p1start,FLAG_META);
2282 	if (nparam==2) {
2283 	  t=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
2284 	  i=strlen(s);
2285 	  s=realloc(s,i+strlen(t)+2);
2286 	  s[i]=' ';
2287 	  strcpy(s+i+1,t);
2288 	  free(t);
2289 	}
2290 	f=popen(s,"r");
2291 	free(s);
2292 	if (f==NULL) warning("Cannot #exec. Command not found(?)");
2293 	else {
2294 	  while ((c=fgetc(f)) != EOF) outchar((char)c);
2295 	  pclose(f);
2296 	}
2297       }
2298     }
2299     break;
2300 
2301   case 9: /* DEFEVAL */
2302     if (!commented[iflevel]) {
2303       whiteout(&p1start,&p1end);
2304       if ((p1start==p1end)||(identifierEnd(p1start)!=p1end))
2305         bug("#defeval requires an identifier (A-Z,a-z,0-9,_ only)");
2306       tmpbuf=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
2307       i=findIdent(C->buf+p1start,p1end-p1start);
2308       if (i>=0) delete_macro(i);
2309       newmacro(C->buf+p1start,p1end-p1start,1);
2310       if (nparam==1) { p2end=p2start=p1end; }
2311       replace_definition_with_blank_lines(C->buf+1,C->buf+p2end,S->preservelf);
2312       macros[nmacros].macrotext=tmpbuf;
2313       macros[nmacros].macrolen=strlen(macros[nmacros].macrotext);
2314       macros[nmacros].defined_in_comment=C->in_comment;
2315 
2316       if (argc) {
2317         for (j=0;j<argc;j++) whiteout(argb+j,arge+j);
2318         /* define with one empty argument */
2319         if ((argc==1)&&(arge[0]==argb[0])) argc=0;
2320         macros[nmacros].argnames=malloc((argc+1)*sizeof(char *));
2321         macros[nmacros].argnames[argc]=NULL;
2322       }
2323       macros[nmacros].nnamedargs=argc;
2324       for (j=0;j<argc;j++) {
2325         if ((argb[j]==arge[j])||(identifierEnd(argb[j])!=arge[j]))
2326           bug("#defeval with named args needs identifiers as arg names");
2327         macros[nmacros].argnames[j]=malloc(arge[j]-argb[j]+1);
2328         memcpy(macros[nmacros].argnames[j],C->buf+argb[j],arge[j]-argb[j]);
2329         macros[nmacros].argnames[j][arge[j]-argb[j]]=0;
2330       }
2331       lookupArgRefs(nmacros++);
2332     } else
2333       replace_directive_with_blank_line(C->out->f);
2334     break;
2335 
2336   case 10: /* IFEQ */
2337     replace_directive_with_blank_line(C->out->f);
2338     iflevel++;
2339     if (iflevel==STACKDEPTH) bug("Too many nested #ifeqs");
2340     commented[iflevel]=commented[iflevel-1];
2341     if (!commented[iflevel]) {
2342       char *s,*t;
2343       if (nparam!=2) bug("#ifeq requires two arguments");
2344       s=ProcessText(C->buf+p1start,p1end-p1start,FLAG_META);
2345       t=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
2346       commented[iflevel]=(nowhite_strcmp(s,t)!=0);
2347       free(s); free(t);
2348     }
2349     break;
2350 
2351   case 11: /* IFNEQ */
2352     replace_directive_with_blank_line(C->out->f);
2353     iflevel++;
2354     if (iflevel==STACKDEPTH) bug("Too many nested #ifeqs");
2355     commented[iflevel]=commented[iflevel-1];
2356     if (!commented[iflevel]) {
2357       char *s,*t;
2358       if (nparam!=2) bug("#ifneq requires two arguments");
2359       s=ProcessText(C->buf+p1start,p1end-p1start,FLAG_META);
2360       t=ProcessText(C->buf+p2start,p2end-p2start,FLAG_META);
2361       commented[iflevel]=(nowhite_strcmp(s,t)==0);
2362       free(s); free(t);
2363     }
2364     break;
2365 
2366   case 12: /* EVAL */
2367     if (!commented[iflevel]) {
2368       char *s,*t;
2369       if (nparam==2) p1end=p2end; /* we really want it all ! */
2370       s=ArithmEval(p1start,p1end);
2371       for (t=s;*t;t++) outchar(*t);
2372       free(s);
2373     }
2374     break;
2375 
2376   case 13: /* IF */
2377     replace_directive_with_blank_line(C->out->f);
2378     iflevel++;
2379     if (iflevel==STACKDEPTH) bug("Too many nested #ifs");
2380     commented[iflevel]=commented[iflevel-1];
2381     if (!commented[iflevel]) {
2382       char *s;
2383       if (nparam==2) p1end=p2end; /* we really want it all ! */
2384       s=ArithmEval(p1start,p1end);
2385       commented[iflevel]=((s[0]=='0')&&(s[1]==0));
2386       free(s);
2387     }
2388     break;
2389 
2390   case 14: /* MODE */
2391     replace_directive_with_blank_line(C->out->f);
2392     if (nparam==1) p2start=-1;
2393     if (!commented[iflevel])
2394       ProcessModeCommand(p1start,p1end,p2start,p2end);
2395     PopSpecs();
2396     break;
2397 
2398   case 15: { /* LINE */
2399     char buf[MAX_GPP_NUM_SIZE];
2400     sprintf(buf, "%d", C->lineno);
2401     replace_directive_with_blank_line(C->out->f);
2402     sendout(buf, strlen(buf), 0);
2403   }
2404     break;
2405 
2406   case 16: /* FILE */
2407     replace_directive_with_blank_line(C->out->f);
2408     sendout(C->filename, strlen(C->filename), 0);
2409     break;
2410 
2411   case 17: /* ELIF */
2412     replace_directive_with_blank_line(C->out->f);
2413     if (iflevel==0) bug("#elif without #if");
2414     if (!commented[iflevel-1]) {
2415       if (commented[iflevel]!=1) commented[iflevel]=2;
2416       else {
2417         char *s;
2418         commented[iflevel]=0;
2419         if (nparam==2) p1end=p2end; /* we really want it all ! */
2420         s=ArithmEval(p1start,p1end);
2421         commented[iflevel]=((s[0]=='0')&&(s[1]==0));
2422         free(s);
2423       }
2424     }
2425     break;
2426 
2427   case 18: /* ERROR */
2428     replace_directive_with_blank_line(C->out->f);
2429     if (!commented[iflevel])
2430       bug(ProcessText(C->buf + p1start,
2431 		      (nparam == 2 ? p2end : p1end) - p1start,
2432 		      FLAG_META));
2433     break;
2434 
2435   case 19: /* WARNING */
2436     replace_directive_with_blank_line(C->out->f);
2437     if (!commented[iflevel]) {
2438       char *s;
2439       s=ProcessText(C->buf + p1start,
2440 		    (nparam == 2 ? p2end : p1end) - p1start,
2441 		    FLAG_META);
2442       warning(s);
2443       free(s);
2444     }
2445     break;
2446 
2447   case 20: { /* DATE */
2448     char buf[MAX_GPP_DATE_SIZE];
2449     char *fmt;
2450     time_t now = time(NULL);
2451     fmt=ProcessText(C->buf + p1start,
2452 		    (nparam == 2 ? p2end : p1end) - p1start,
2453 		    FLAG_META);
2454     if (!strftime(buf, MAX_GPP_DATE_SIZE, fmt, localtime(&now)))
2455       bug("date buffer exceeded");
2456     replace_directive_with_blank_line(C->out->f);
2457     sendout(buf, strlen(buf), 0);
2458     free(fmt);
2459   }
2460     break;
2461 
2462   default: bug("Internal meta-macro identification error");
2463   }
2464   shiftIn(macend);
2465   return 0;
2466 }
2467 
ParsePossibleUser(void)2468 int ParsePossibleUser(void)
2469 {
2470   int idstart,idend,sh_end,lg_end,macend;
2471   int argc,id,i,l;
2472   char *argv[MAXARGS];
2473   int argb[MAXARGS],arge[MAXARGS];
2474   struct INPUTCONTEXT *T;
2475 
2476   idstart=1;
2477   id=0;
2478   if (!SplicePossibleUser(&idstart,&idend,&sh_end,&lg_end,
2479                           argb,arge,&argc,1,&id,FLAG_USER))
2480     return -1;
2481   if ((sh_end>=0)&&(C->namedargs!=NULL)) {
2482     i=findNamedArg(C->buf+idstart,idend-idstart);
2483     if (i>=0) {
2484       if (i<C->argc) sendout(C->argv[i],strlen(C->argv[i]),0);
2485       shiftIn(sh_end);
2486       return 0;
2487     }
2488   }
2489 
2490   if (id<0) return -1;
2491   if (lg_end>=0) macend=lg_end; else { macend=sh_end; argc=0; }
2492 
2493   if (macros[id].nnamedargs==-2) { /* defined(...) macro for arithmetic */
2494     char *s,*t;
2495     if (argc!=1) return -1;
2496     s=remove_comments(argb[0],arge[0],FLAG_USER);
2497     t=s+strlen(s)-1;
2498     if (*s!=0) while ((t!=s)&&iswhite(*t)) *(t--)=0;
2499     t=s; while (iswhite(*t)) t++;
2500     if (findIdent(t,strlen(t))>=0) outchar('1');
2501     else outchar('0');
2502     free(s);
2503     shiftIn(macend);
2504     return 0;
2505   }
2506   if (!macros[id].macrotext[0]) { /* the empty macro */
2507     shiftIn(macend);
2508     return 0;
2509   }
2510 
2511   for (i=0;i<argc;i++)
2512     argv[i]=ProcessText(C->buf+argb[i],arge[i]-argb[i],FLAG_USER);
2513   /* process macro text */
2514   T=C;
2515   C=malloc(sizeof *C);
2516   C->out=T->out;
2517   C->in=NULL;
2518   C->argc=argc;
2519   C->argv=argv;
2520   C->filename=T->filename;
2521   C->lineno=T->lineno;
2522   C->may_have_args=1;
2523   if ((macros[id].nnamedargs==-1)&&(lg_end>=0)&&
2524       (macros[id].define_specs->User.mEnd[0]==0)) {
2525     /* build an aliased macro call */
2526     l=strlen(macros[id].macrotext)+2
2527       +strlen(macros[id].define_specs->User.mArgS)
2528       +strlen(macros[id].define_specs->User.mArgE)
2529       +(argc-1)*strlen(macros[id].define_specs->User.mArgSep);
2530     for (i=0;i<argc;i++) l+=strlen(argv[i]);
2531     C->buf=C->malloced_buf=malloc(l);
2532     l=strlen(macros[id].macrotext)+1;
2533     C->buf[0]='\n';
2534     strcpy(C->buf+1,macros[id].macrotext);
2535     while ((l>1)&&iswhite(C->buf[l-1])) l--;
2536     strcpy(C->buf+l,macros[id].define_specs->User.mArgS);
2537     for (i=0;i<argc;i++) {
2538       if (i>0) strcat(C->buf,macros[id].define_specs->User.mArgSep);
2539       strcat(C->buf,argv[i]);
2540     }
2541     strcat(C->buf,macros[id].define_specs->User.mArgE);
2542     C->may_have_args=0;
2543   }
2544   else {
2545     C->buf=C->malloced_buf=malloc(strlen(macros[id].macrotext)+2);
2546     C->buf[0]='\n';
2547     strcpy(C->buf+1,macros[id].macrotext);
2548   }
2549   C->len=strlen(C->buf);
2550   C->bufsize=C->len+1;
2551   C->eof=0;
2552   C->namedargs=macros[id].argnames;
2553   C->in_comment=macros[id].defined_in_comment;
2554   C->ambience=FLAG_META;
2555   PushSpecs(macros[id].define_specs);
2556   ProcessContext();
2557   PopSpecs();
2558   free(C);
2559   C=T;
2560 
2561   for (i=0;i<argc;i++) free(argv[i]);
2562   shiftIn(macend);
2563   return 0;
2564 }
2565 
ParseText(void)2566 void ParseText(void)
2567 {
2568   int l,cs,ce;
2569   char c,*s;
2570   struct COMMENT *p;
2571 
2572   /* look for comments first */
2573   if (!C->in_comment) {
2574     cs=1;
2575     for (p=S->comments;p!=NULL;p=p->next)
2576       if (!(p->flags[C->ambience]&FLAG_IGNORE))
2577         if (matchStartSequence(p->start,&cs)) {
2578           l=ce=findCommentEnd(p->end,p->quote,p->warn,cs,p->flags[C->ambience]);
2579           matchEndSequence(p->end,&l);
2580           if (p->flags[C->ambience]&OUTPUT_DELIM)
2581             sendout(C->buf+1,cs-1,0);
2582           if (!(p->flags[C->ambience]&OUTPUT_TEXT))
2583             replace_definition_with_blank_lines(C->buf+1, C->buf+ce-1, 0);
2584           if (p->flags[C->ambience]&PARSE_MACROS) {
2585             C->in_comment=1;
2586             s=ProcessText(C->buf+cs,ce-cs,C->ambience);
2587             if (p->flags[C->ambience]&OUTPUT_TEXT) sendout(s,strlen(s),0);
2588             C->in_comment=0;
2589             free(s);
2590           }
2591           else if (p->flags[C->ambience]&OUTPUT_TEXT)
2592             sendout(C->buf+cs,ce-cs,0);
2593           if (p->flags[C->ambience]&OUTPUT_DELIM)
2594             sendout(C->buf+ce,l-ce,0);
2595           shiftIn(l);
2596           return;
2597         }
2598   }
2599 
2600   if (ParsePossibleMeta()>=0) return;
2601   if (ParsePossibleUser()>=0) return;
2602 
2603   l=1;
2604   /* If matching numbered macro argument and inside a macro */
2605   if (matchSequence(S->User.mArgRef,&l) && C->may_have_args) {
2606     /* Process macro arguments referenced as #1,#2,... */
2607     c=getChar(l);
2608     if ((c>='1')&&(c<='9')) {
2609       c=c-'1';
2610       if (c<C->argc)
2611         sendout(C->argv[(int)c],strlen(C->argv[(int)c]),0);
2612       shiftIn(l+1);
2613       return;
2614     }
2615   }
2616 
2617   l=identifierEnd(1);
2618   if (l==1) l=2;
2619   sendout(C->buf+1,l-1,1);
2620   shiftIn(l);
2621 }
2622 
ProcessContext(void)2623 void ProcessContext(void)
2624 {
2625   if (C->len==0) { C->buf[0]='\n'; C->len++; }
2626   while (!C->eof) ParseText();
2627   if (C->in!=NULL) fclose(C->in);
2628   free(C->malloced_buf);
2629 }
2630 
2631 /* additions by M. Kifer - revised D.A. 12/16/01 */
2632 
2633 /* copy SLASH-terminated name of the directory of fname */
getDirname(const char * fname,char * dirname)2634 static void getDirname(const char *fname, char *dirname)
2635 {
2636   int i;
2637 
2638   for (i = strlen(fname)-1; i>=0; i--) {
2639     if (fname[i] == SLASH)
2640       break;
2641   }
2642   if (i >= 0) {
2643     strncpy(dirname,fname,i);
2644     dirname[i] = SLASH;
2645   } else
2646     /* just a precaution: i must be -1 in this case anyway */
2647     i = -1;
2648 
2649   dirname[i+1] = '\0';
2650 }
2651 
openInCurrentDir(const char * incfile)2652 static FILE *openInCurrentDir(const char *incfile)
2653 {
2654   char *absfile =
2655     calloc(strlen(C->filename)+strlen(incfile)+1, 1);
2656   FILE *f;
2657   getDirname(C->filename,absfile);
2658   strcat(absfile,incfile);
2659   f=fopen(absfile,"r");
2660   free(absfile);
2661   return f;
2662 }
2663 
2664 /* skip = # of \n's already output by other mechanisms, to be skipped */
replace_definition_with_blank_lines(const char * start,const char * end,int skip)2665 void replace_definition_with_blank_lines(const char *start, const char *end, int skip)
2666 {
2667   if ((include_directive_marker != NULL) && (C->out->f != NULL)) {
2668     while (start <= end) {
2669       if (*start == '\n') {
2670 	if (skip) skip--; else fprintf(C->out->f,"\n");
2671       }
2672       start++;
2673     }
2674   }
2675 }
2676 
2677 /* insert blank line where the metas IFDEF,ELSE,INCLUDE, etc., stood in the
2678    input text
2679 */
replace_directive_with_blank_line(FILE * f)2680 void replace_directive_with_blank_line(FILE *f)
2681 {
2682   if ((include_directive_marker != NULL) && (f != NULL)
2683       && (!S->preservelf) && (S->Meta.mArgE[0]=='\n')) {
2684     fprintf(f,"\n");
2685   }
2686 }
2687 
2688 
2689 /* If lineno is > 15 digits - the number won't be printed correctly */
write_include_marker(FILE * f,int lineno,char * filename,const char * marker)2690 void write_include_marker(FILE *f, int lineno, char *filename, const char *marker)
2691 {
2692   static char lineno_buf[MAX_GPP_NUM_SIZE];
2693   static char *escapedfilename = NULL;
2694 
2695   if ((include_directive_marker != NULL) && (f != NULL)) {
2696 #ifdef WIN_NT
2697     escape_backslashes(filename,&escapedfilename);
2698 #else
2699     escapedfilename = filename;
2700 #endif
2701     sprintf(lineno_buf,"%d", lineno);
2702     fprintf(f, include_directive_marker, lineno_buf, escapedfilename, marker);
2703   }
2704 }
2705 
2706 
2707 /* Under windows, files can have backslashes in them.
2708    These should be escaped.
2709 */
escape_backslashes(const char * instr,char ** outstr)2710 void escape_backslashes(const char *instr, char **outstr)
2711 {
2712   int out_idx=0;
2713 
2714   if (*outstr != NULL) free(*outstr);
2715   *outstr = malloc(2*strlen(instr));
2716 
2717   while (*instr != '\0') {
2718     if (*instr=='\\') {
2719       *(*outstr+out_idx) = '\\';
2720       out_idx++;
2721     }
2722     *(*outstr+out_idx) = *instr;
2723     out_idx++;
2724     instr++;
2725   }
2726   *(*outstr+out_idx) = '\0';
2727 }
2728 
2729 
2730 /* includemarker_input should have 3 ?-marks, which are replaced with %s.
2731    Also, @ is replaced with a space. These symbols can be escaped with a
2732    backslash.
2733 */
construct_include_directive_marker(char ** include_directive_marker,const char * includemarker_input)2734 void construct_include_directive_marker(char **include_directive_marker,
2735 					const char *includemarker_input)
2736 {
2737   int len = strlen(includemarker_input);
2738   char ch;
2739   int in_idx=0, out_idx=0;
2740   int quoted = 0, num_repl = 0;
2741 
2742   /* only 6 extra chars are needed: 3 for the three %'s, 2 for \n, 1 for \0 */
2743   *include_directive_marker = malloc(len+18);
2744 
2745   ch = *includemarker_input;
2746   while (ch != '\0' && in_idx < len) {
2747     if (quoted) {
2748       *(*include_directive_marker+out_idx) = ch;
2749       out_idx++;
2750       quoted = 0;
2751     } else {
2752       switch (ch) {
2753       case '\\':
2754 	quoted = 1;
2755 	break;
2756       case '@':
2757 	*(*include_directive_marker+out_idx) = ' ';
2758 	out_idx++;
2759 	break;
2760       case '%':
2761       case '?':
2762 	*(*include_directive_marker+out_idx) = '%';
2763 	out_idx++;
2764 	*(*include_directive_marker+out_idx) = 's';
2765 	out_idx++;
2766         if (++num_repl > 3) bug("only 3 substitutions allowed in -includemarker");
2767 	break;
2768       default:
2769 	*(*include_directive_marker+out_idx) = ch;
2770 	out_idx++;
2771       }
2772     }
2773 
2774     in_idx++;
2775     ch = *(includemarker_input+in_idx);
2776   }
2777 
2778   *(*include_directive_marker+out_idx) = '\n';
2779   out_idx++;
2780   *(*include_directive_marker+out_idx) = '\0';
2781 }
2782 
2783 
main(int argc,char ** argv)2784 int main(int argc,char **argv)
2785 {
2786   initthings(argc,argv);
2787   /* The include marker at the top of the file */
2788   if (IncludeFile)
2789     DoInclude(IncludeFile);
2790   write_include_marker(C->out->f, 1, C->filename, "");
2791   ProcessContext();
2792   fclose(C->out->f);
2793   return EXIT_SUCCESS;
2794 }
2795 
2796