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