Quick Search:

View

Revision:
Expand:  
Changeset: MAIN:ragge:20050102175425

Diff

Diff from 1.20 to:

Annotations

Annotate by Age | Author | Mixed | None
/fisheye/browse/pcc/pcc/cc/cpp/cpp.c

Annotated File View

ragge
1.20
1 /*      $Id: cpp.c,v 1.20 2005/01/02 17:54:25 ragge Exp $       */
ragge
1.1
2
3 /*
4  * Copyright (c) 2004 Anders Magnusson (ragge@ludd.luth.se).
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 /*
31  * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions
35  * are met:
36  *
37  * Redistributions of source code and documentation must retain the above
38  * copyright notice, this list of conditions and the following disclaimer.
39  * Redistributions in binary form must reproduce the above copyright
40  * notice, this list of conditions and the following disclaimer in the
41  * documentation and/or other materials provided with the distribution.
42  * All advertising materials mentioning features or use of this software
43  * must display the following acknowledgement:
44  *      This product includes software developed or owned by Caldera
45  *      International, Inc.
46  * Neither the name of Caldera International, Inc. nor the names of other
47  * contributors may be used to endorse or promote products derived from
48  * this software without specific prior written permission.
49  *
50  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
51  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
52  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
53  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54  * DISCLAIMED.  IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE
55  * FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OFLIABILITY, WHETHER IN CONTRACT,
59  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
60  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
61  * POSSIBILITY OF SUCH DAMAGE.
62  */
63 /*
64  * The C preprocessor.
65  * This code originates from the V6 preprocessor with some additions
66  * from V7 cpp, and at last ansi/c99 support.
67  */
68 #include <sys/wait.h>
69
70 #include <fcntl.h>
71 #include <unistd.h>
72 #include <stdio.h>
73 #include <stdarg.h>
74 #include <stdlib.h>
75 #include <string.h>
ragge
1.9
76 #include <time.h>
ragge
1.1
77 #include <unistd.h>
78
79 #include "cpp.h"
80
81 #define MAXARG  250     /* # of args to a macro, limited by char value */
ragge
1.16
82 #define SBSIZE  200000
83 #define SYMSIZ  6000
ragge
1.1
84
85 static usch     sbf[SBSIZE];
86 /* C command */
87
88 int tflag;      /* traditional cpp syntax */
89 #ifdef CPP_DEBUG
90 int dflag;      /* debug printouts */
91 #endif
92 FILE *obuf;
93 static int exfail;
ragge
1.10
94 struct symtab symtab[SYMSIZ];
ragge
1.1
95
96 /* avoid recursion */
97 struct recur {
98         struct recur *next;
99         struct symtab *sp;
100 };
101
ragge
1.10
102 /* include dirs */
103 struct incs {
104         struct incs *next;
105         char *dir;
ragge
1.20
106 } *incdir[2];
107 #define INCINC 0
108 #define SYSINC 1
ragge
1.10
109
ragge
1.1
110 static struct symtab *filloc;
111 static struct symtab *linloc;
ragge
1.10
112 int     trulvl;
113 int     flslvl;
114 int     elflvl;
115 int     elslvl;
116 usch *stringbuf = sbf;
ragge
1.1
117
118 /*
119  * Macro replacement list syntax:
120  * - For object-type macros, replacement strings are stored as-is.
121  * - For function-type macros, macro args are substituted for the
122  *   character WARN followed by the argument number.
ragge
1.12
123  * - The value element points to the end of the string, to simplify
124  *   pushback onto the input queue.
ragge
1.1
125  * 
ragge
1.12
126  * The first character (from the end) in the replacement list is
127  * the number of arguments:
ragge
1.1
128  *   OBJCT - object-type macro
129  *   0     - empty parenthesis, foo()
130  *   1->   - number of args.
131  */
132
133 #define OBJCT   0xff
134 #define WARN    1       /* SOH, not legal char */
135 #define CONC    2       /* STX, not legal char */
136 #define SNUFF   3       /* ETX, not legal char */
137 #define NOEXP   4       /* EOT, not legal char */
138 #define EXPAND  5       /* ENQ, not legal char */
139
140 /* args for lookup() */
141 #define FIND    0
142 #define ENTER   1
143
144 static void expdef(usch *protostruct recur *, int gotwarn);
145 static void savch(int c);
146 static void control(void);
147 static usch *savstr(usch *str);
148 static void define(void);
149 static void expmac(struct recur *);
150 static int canexpand(struct recur *, struct symtab *np);
ragge
1.3
151 static void include(void);
ragge
1.9
152 static void line(void);
ragge
1.1
153
154 int
155 main(int argcchar **argv)
156 {
ragge
1.20
157         struct incs *w, *w2;
ragge
1.7
158         struct symtab *nl, *thisnl;
159         register int cgotspcch;
160         usch *osp;
ragge
1.15
161
ragge
1.7
162         while ((ch = getopt(argcargv"D:I:S:U:td")) != -1)
ragge
1.1
163                 switch (ch) {
ragge
1.7
164                 case 'D'/* Define something */
165                         osp = optarg;
166                         while (*osp && *osp != '=')
167                                 osp++;
168                         if (*osp == '=') {
ragge
1.10
169                                 *osp++ = 0;
170                                 while (*osp)
171                                         osp++;
172                                 *osp = OBJCT;
ragge
1.7
173                         } else {
ragge
1.10
174                                 static char c[3] = { 0'1'OBJCT };
175                                 osp = &c[2];
ragge
1.7
176                         }
177                         nl = lookup(optargENTER);
178                         if (nl->value)
179                                 error("%s redefined"optarg);
180                         nl->value = osp;
181                         break;
182
183                 case 'S':
ragge
1.10
184                 case 'I':
185                         w = calloc(sizeof(struct incs), 1);
186                         w->dir = optarg;
187                         w2 = incdir[ch == 'I' ? INCINC : SYSINC];
188                         if (w2 != NULL) {
189                                 while (w2->next)
190                                         w2 = w2->next;
191                                 w2->next = w;
192                         } else
193                                 incdir[ch == 'I' ? INCINC : SYSINC] = w;
ragge
1.7
194                         break;
195
196                 case 'U':
197                         nl = lookup(optargFIND);
ragge
1.15
198                         if ((nl = lookup(optargFIND)))
ragge
1.7
199                                 nl->value = NULL;
200                         break;
ragge
1.1
201 #ifdef CPP_DEBUG
202                 case 'd':
ragge
1.18
203                         dflag++;
ragge
1.1
204                         break;
205 #endif
206                 case 't':
207                         tflag = 1;
208                         break;
209
210                 default:
211                         fprintf(stderr"bad arg %c\n"ch);
212                         exit(1);
213                 }
214         argc -= optind;
215         argv += optind;
216
217         exfail = 0;
ragge
1.7
218         if (argc) {
219                 if (freopen(argv[0], "r"stdin) == NULL) {
220                         fprintf(stderr"Can't open %s"argv[0]);
ragge
1.1
221                         exit(8);
222                 }
223         }
ragge
1.16
224
ragge
1.10
225         if (pushfile(argc ? argv[0] : "<stdin>"))
226                 error("cannot open %s"argv[0]);
ragge
1.1
227
ragge
1.7
228         if (argc == 2) {
229                 if ((obuf = fopen(argv[1], "w")) == 0) {
230                         fprintf(stderr"Can't creat %s\n"argv[1]);
ragge
1.1
231                         exit(8);
232                 }
233         } else
234                 obuf = stdout;
235
236         prtline();
237
ragge
1.12
238         filloc = lookup("__FILE__"ENTER);
239         linloc = lookup("__LINE__"ENTER);
ragge
1.15
240         filloc->value = linloc->value = ""/* Just something */
241
ragge
1.12
242         if (tflag == 0) {
243                 time_t t = time(NULL);
244                 char *n = ctime(&t);
245
246                 /*
247                  * Manually move in the predefined macros.
248                  */
249                 nl = lookup("__TIME__"ENTER);
ragge
1.13
250                 savch(0); savch('"');  n[19] = 0savstr(&n[11]); savch('"');
ragge
1.12
251                 savch(OBJCT);
252                 nl->value = stringbuf-1;
253
254                 nl = lookup("__DATE__"ENTER);
ragge
1.13
255                 savch(0); savch('"'); n[24] = n[11] = 0savstr(&n[4]);
256                 savstr(&n[20]); savch('"'); savch(OBJCT);
ragge
1.12
257                 nl->value = stringbuf-1;
258
259                 nl = lookup("__STDC__"ENTER);
ragge
1.13
260                 savch(0); savch('1'); savch(OBJCT);
ragge
1.12
261                 nl->value = stringbuf-1;
262         }
ragge
1.1
263
264         thisnl = NULL;
265         while ((c = yylex()) != 0) {
266                 switch (c) {
267                 case CONTROL:
268                         control();
269                         break;
270
271                 case IDENT:
ragge
1.8
272                         if (flslvl)
273                                 break;
ragge
1.1
274                         osp = stringbuf;
ragge
1.15
275                         nl = lookup(yystrFIND);
ragge
1.1
276                         if (nl == 0 || thisnl == 0)
277                                 goto found;
278                         if (thisnl == nl) {
279                                 nl = 0;
280                                 goto found;
281                         }
ragge
1.2
282                         gotspc = 0;
ragge
1.1
283                         if ((c = yylex()) == WSPACE)
ragge
1.2
284                                 gotspc = 1c = yylex();
ragge
1.1
285                         if (c != EXPAND) {
ragge
1.15
286                                 unpstr(yystr);
ragge
1.2
287                                 if (gotspc)
288                                         cunput(' ');
ragge
1.1
289                                 unpstr(nl->namep);
ragge
1.15
290                                 (void)yylex(); /* get yystr correct */
ragge
1.1
291                                 nl = 0/* ignore */
292                         } else
293                                 thisnl = NULL;
294
ragge
1.15
295 found:                  if (nl == 0 || subst(yystrnlNULL) == 0) {
296                                 fputs(yystrobuf);
ragge
1.1
297                         } else if (osp != stringbuf) {
298                                 cunput(EXPAND);
299                                 while (stringbuf > osp)
300                                         cunput(*--stringbuf);
301                                 thisnl = nl;
302                         }
303                         stringbuf = osp/* clean up heap */
304                         break;
305
306                 case EXPAND:
307                         thisnl = NULL;
308                         break;
309
ragge
1.15
310                 case NL:
311                         if (flslvl == 0)
312                                 putc('\n'obuf);
313                         break;
314
ragge
1.1
315                 case CHARCON:
316                 case NUMBER:
317                 case FPOINT:
318                 case STRING:
319                 case WSPACE:
ragge
1.3
320                 default:
ragge
1.8
321                         if (flslvl == 0)
ragge
1.15
322                                 fputs(yystrobuf);
ragge
1.1
323                         break;
324                 }
325         }
326         fclose(obuf);
ragge
1.16
327         if (trulvl || flslvl)
328                 error("unterminated conditional");
ragge
1.7
329         return exfail;
ragge
1.1
330 }
331
332 /*
333  * do something when a '#' is found.
334  */
335 void
336 control()
337 {
338         struct symtab *np;
339         int t;
340
ragge
1.15
341 #define CHECK(x) (yystr[0] == #x[0]) && strcmp(yystr, #x) == 0
ragge
1.8
342
ragge
1.1
343         if ((t = yylex()) == WSPACE)
344                 t = yylex();
ragge
1.14
345         if (t == NL) {
346                 /* Just ignore */
347                 putc('\n'obuf);
348                 return;
349         }
ragge
1.1
350         if (t != IDENT)
ragge
1.15
351                 return error("bad control '%s'"yystr);
ragge
1.1
352
ragge
1.8
353         if (CHECK(include)) {
ragge
1.1
354                 if (flslvl)
355                         goto exit;
ragge
1.3
356                 include();
ragge
1.1
357                 return;
ragge
1.8
358         } else if (CHECK(else)) {
ragge
1.1
359                 if (flslvl) {
ragge
1.10
360                         if (elflvl > trulvl)
361                                 ;
362                         else if (--flslvl!=0) {
ragge
1.1
363                                 flslvl++;
ragge
1.8
364                         } else {
ragge
1.1
365                                 trulvl++;
ragge
1.8
366                                 prtline();
367                         }
ragge
1.1
368                 } else if (trulvl) {
369                         flslvl++;
370                         trulvl--;
371                 } else
372                         error("If-less else");
ragge
1.10
373                 if (elslvl==trulvl+flslvlerror("Too many else");
374                 elslvl=trulvl+flslvl;
ragge
1.8
375         } else if (CHECK(endif)) {
376                 if (flslvl) {
377                         flslvl--;
378                         if (flslvl == 0)
379                                 prtline();
380                 } else if (trulvl)
381                         trulvl--;
382                 else
383                         error("If-less endif");
ragge
1.10
384                 if (flslvl == 0)
385                         elflvl = 0;
386                 elslvl = 0;
ragge
1.8
387         } else if (CHECK(error)) {
388                 usch *ch = stringbuf;
ragge
1.11
389                 if (flslvl)
390                         goto exit;
ragge
1.8
391                 while (yylex() != NL)
ragge
1.15
392                         savstr(yystr);
ragge
1.8
393                 savch('\n');
394                 error("error: %s"ch);
ragge
1.9
395 #define GETID() if (yylex() != WSPACE || yylex() != IDENT) goto cfail
396         } else if (CHECK(define)) {
ragge
1.10
397                 if (flslvl)
398                         goto exit;
ragge
1.9
399                 GETID();
400                 define();
401         } else if (CHECK(ifdef)) {
402                 GETID();
ragge
1.15
403                 if (flslvl == 0 && lookup(yystrFIND) != 0)
ragge
1.9
404                         trulvl++;
405                 else
406                         flslvl++;
407         } else if (CHECK(ifndef)) {
408                 GETID();
ragge
1.15
409                 if (flslvl == 0 && lookup(yystrFIND) == 0)
ragge
1.9
410                         trulvl++;
411                 else
412                         flslvl++;
413         } else if (CHECK(undef)) {
414                 GETID();
ragge
1.15
415                 if (flslvl == 0 && (np = lookup(yystrFIND)))
ragge
1.9
416                         np->value = 0;
417         } else if (CHECK(line)) {
ragge
1.10
418                 if (flslvl)
419                         goto exit;
ragge
1.9
420                 line();
ragge
1.10
421         } else if (CHECK(if)) {
422                 if (flslvl==0 && yyparse())
423                         ++trulvl;
424                 else
425                         ++flslvl;
ragge
1.16
426         } else if (CHECK(pragma)) {
427                 goto exit;
ragge
1.10
428         } else if (CHECK(elif)) {
429                 if (flslvl == 0)
430                         elflvl = trulvl;
431                 if (flslvl) {
432                         if (elflvl > trulvl)
433                                 ;
434                         else if (--flslvl!=0)
435                                 ++flslvl;
436                         else {
437                                 if (yyparse()) {
438                                         ++trulvl;
439                                         prtline();
440                                 } else
441                                         ++flslvl;
442                         }
443                 } else if (trulvl) {
444                         ++flslvl;
445                         --trulvl;
446                 } else
447                         error("If-less elif");
ragge
1.9
448         } else
ragge
1.17
449 #if 0
ragge
1.15
450                 error("undefined control '%s'"yystr);
ragge
1.17
451 #else
452                 goto exit;
453 #endif
ragge
1.9
454
455         return;
456
457 cfail:
458         error("control line syntax error");
ragge
1.1
459
460 exit:
461         while (yylex() != NL)
462                 ;
ragge
1.10
463         putc('\n'obuf);
ragge
1.8
464 #undef CHECK
ragge
1.1
465 }
466
467 void
ragge
1.9
468 line()
469 {
470         struct symtab *nl;
471         int c;
472
473         if (yylex() != WSPACE)
474                 goto bad;
475         if ((c = yylex()) == IDENT) {
476                 /* Do macro preprocessing first */
477                 usch *osp = stringbuf;
ragge
1.15
478                 if ((nl = lookup(yystrFIND)) == NULL)
ragge
1.9
479                         goto bad;
ragge
1.15
480                 if (subst(yystrnlNULL) == 0)
ragge
1.9
481                         goto bad;
482                 while (stringbuf > osp)
483                         cunput(*--stringbuf);
484                 c = yylex();
485         }
486
487         if (c != NUMBER)
488                 goto bad;
ragge
1.15
489         setline(atoi(yystr));
ragge
1.9
490
491         if ((c = yylex()) != NL && c != WSPACE)
492                 goto bad;
493         if (c == NL)
494                 return setline(curline()+1);
495         if (yylex() != STRING)
496                 goto bad;
ragge
1.15
497         yystr[strlen(yystr)-1] = 0;
498         setfile(&yystr[1]);
ragge
1.9
499         return;
500
501 bad:    error("bad line directive");
502 }
503
ragge
1.10
504 /*
505  * Include a file. Include order:
ragge
1.20
506  * - For <...> files, first search -I directories, then system directories.
507  * - For "..." files, first search "current" dir, then as <...> files.
ragge
1.10
508  */
ragge
1.9
509 void
ragge
1.3
510 include()
511 {
ragge
1.10
512         struct incs *w;
ragge
1.3
513         struct symtab *nl;
ragge
1.10
514         usch *osp;
515         char *fn;
516         int icit;
ragge
1.3
517
ragge
1.10
518         osp = stringbuf;
ragge
1.3
519         if (yylex() != WSPACE)
520                 goto bad;
ragge
1.10
521 again:  if ((c = yylex()) != STRING && c != '<' && c != IDENT)
ragge
1.3
522                 goto bad;
523
524         if (c == IDENT) {
ragge
1.15
525                 if ((nl = lookup(yystrFIND)) == NULL)
ragge
1.3
526                         goto bad;
ragge
1.15
527                 if (subst(yystrnlNULL) == 0)
ragge
1.3
528                         goto bad;
529                 savch('\0');
ragge
1.10
530                 unpstr(osp);
531                 goto again;
532         } else if (c == '<') {
533                 fn = stringbuf;
534                 while ((c = yylex()) != '>' && c != NL) {
535                         if (c == NL)
536                                 goto bad;
ragge
1.15
537                         savstr(yystr);
ragge
1.10
538                 }
539                 savch('\0');
ragge
1.20
540                 it = SYSINC;
ragge
1.3
541         } else {
ragge
1.20
542                 usch *nm = stringbuf;
543
ragge
1.15
544                 yystr[strlen(yystr)-1] = 0;
545                 fn = &yystr[1];
ragge
1.20
546                 /* first try to open file relative to previous file */
547                 savstr(curfile());
548                 if ((stringbuf = strrchr(nm'/')) == NULL)
549                         stringbuf = nm;
550                 else
551                         stringbuf++;
552                 savstr(fn); savch(0);
553                 if (pushfile(nm) == 0)
554                         goto ret;
555                 stringbuf = nm;
ragge
1.10
556         }
557
558         /* create search path and try to open file */
ragge
1.20
559         for (i = 0i < 2i++) {
ragge
1.10
560                 for (w = incdir[i]; ww = w->next) {
561                         usch *nm = stringbuf;
562
563                         savstr(w->dir); savch('/');
564                         savstr(fn); savch(0);
565                         if (pushfile(nm) == 0)
566                                 goto ret;
567                         stringbuf = nm;
568                 }
ragge
1.3
569         }
ragge
1.10
570         error("cannot find '%s'"fn);
571         stringbuf = osp;
ragge
1.3
572         return;
573
574 bad:    error("bad include");
ragge
1.10
575 ret:    prtline();
576         stringbuf = osp;
ragge
1.3
577 }
578
579 void
ragge
1.1
580 define()
581 {
582         struct symtab *np;
ragge
1.15
583         usch *args[MAXARG], *ubuf, *sbeg;
584         int ciredef;
ragge
1.1
585         int mkstr = 0narg = -1;
586
ragge
1.15
587         np = lookup(yystrENTER);
588         redef = np->value != NULL;
ragge
1.1
589
ragge
1.15
590         sbeg = stringbuf;
ragge
1.1
591         if ((c = yylex()) == '(') {
592                 narg = 0;
593                 /* function-like macros, deal with identifiers */
594                 while ((c = yylex()) != ')') {
595                         if (c == WSPACEc = yylex();
596                         if (c == ','c = yylex();
597                         if (c == WSPACEc = yylex();
598                         if (c == ')')
599                                 break;
ragge
1.15
600                         if (c != IDENT)
601                                 error("define error");
602                         args[narg] = alloca(strlen(yystr)+1);
603                         strcpy(args[narg], yystr);
ragge
1.1
604                         narg++;
605                 }
ragge
1.8
606         } else if (c == NL) {
607                 /* #define foo */
608                 cunput('\n');
ragge
1.1
609         } else if (c != WSPACE)
610                 error("bad define");
611
612         if ((c = yylex()) == WSPACE)
613                 c = yylex();
614
615         /* parse replacement-list, substituting arguments */
616         savch('\0');
617         while (c != NL) {
618                 switch (c) {
619                 case WSPACE:
620                         /* remove spaces if it surrounds a ## directive */
ragge
1.2
621                         ubuf = stringbuf;
ragge
1.15
622                         savstr(yystr);
ragge
1.1
623                         c = yylex();
624                         if (c == CONCAT) {
ragge
1.2
625                                 stringbuf = ubuf;
ragge
1.1
626                                 savch(CONC);
627                                 if ((c = yylex()) == WSPACE)
628                                         c = yylex();
629                         }
630                         continue;
631
632                 case CONCAT:
633                         /* No spaces before concat op */
634                         savch(CONC);
635                         if ((c = yylex()) == WSPACE)
636                                 c = yylex();
637                         continue;
638
639                 case MKSTR:
640                         if (narg < 0) {
641                                 /* no meaning in object-type macro */
642                                 savch('#');
643                                 break;
644                         }
645                         /* remove spaces between # and arg */
646                         savch(SNUFF);
647                         if ((c = yylex()) == WSPACE)
648                                 c = yylex(); /* whitespace, ignore */
649                         mkstr = 1;
650
651                         /* FALLTHROUGH */
652                 case IDENT:
653                         if (narg < 0)
654                                 goto id/* just add it if object */
655                         /* check if its an argument */
656                         for (i = 0i < nargi++)
ragge
1.15
657                                 if (strcmp(yystrargs[i]) == 0)
ragge
1.1
658                                         break;
659                         if (i == narg) {
660                                 if (mkstr)
661                                         error("not argument");
662                                 goto id;
663                         }
664                         savch(i);
665                         savch(WARN);
666                         if (mkstr)
667                                 savch(SNUFF), mkstr = 0;
668                         break;
669
670                 default:
ragge
1.15
671 id:                     savstr(yystr);
ragge
1.1
672                         break;
673                 }
674                 c = yylex();
675         }
ragge
1.20
676         /* remove trailing whitespace */
677         while (stringbuf > sbeg) {
678                 if (stringbuf[-1] == ' ' || stringbuf[-1] == '\t')
679                         stringbuf--;
680                 else
681                         break;
682         }
ragge
1.1
683         savch(narg < 0 ? OBJCT : narg);
ragge
1.15
684         if (redef) {
685                 usch *o = np->value, *n = stringbuf-1;
686
687                 /* Redefinition to identical replacement-list is allowed */
688                 while (*o && *o == *n)
689                         o--, n--;
690                 if (*o || *o != *n)
691                         error("%s redefined"np->namep);
692                 stringbuf = sbeg;  /* forget this space */
693         } else
694                 np->value = stringbuf-1;
ragge
1.2
695         putc('\n'obuf);
ragge
1.1
696
697 #ifdef CPP_DEBUG
698         if (dflag) {
699                 usch *w = np->value;
700
701                 printf("!define: ");
702                 if (*w == OBJCT)
703                         printf("[object]");
704                 else
705                         printf("[%d]", *w);
706                 while (*--w) {
707                         switch (*w) {
708                         case WARNprintf("<%d>", *--w); break;
709                         case CONCprintf("<##>"); break;
710                         case SNUFFprintf("<\">"); break;
711                         defaultputchar(*w); break;
712                         }
713                 }
714                 putchar('\n');
715         }
716 #endif
717 }
718
719 void
720 error(char *s, ...)
721 {
722         va_list ap;
723
724         va_start(aps);
725         fprintf(stderr"%s:%d: "curfile(), curline());
726         vfprintf(stderrsap);
727         fputc('\n'stderr);
728         exfail++;
729         va_end(ap);
730         exit(8);
731 }
732
733 /*
734  * store a character into the "define" buffer.
735  */
736 void
737 savch(c)
738 {
739         *stringbuf++ = c;
740         if (stringbuf-sbf < SBSIZE)
741                 return;
742         error("Too much defining");
743         exit(1);
744 }
745
746 /*
747  * Do a symbol lookup.
ragge
1.15
748  * If enterf == ENTER, create a new entry.
749  * will return NULL if symbol not found and FIND is given.
ragge
1.1
750  */
751 struct symtab *
752 lookup(namepenterf)
753         char *namep;
754 {
755         register char *np;
756         register struct symtab *sp;
757         int icaround;
758
ragge
1.3
759 if (dflag)printf("lookup '%s'\n"namep);
ragge
1.1
760         np = namep;
761         around = i = 0;
762         while ((c = *np++))
ragge
1.18
763                 i += c;
ragge
1.1
764         i %= SYMSIZ;
765         sp = &symtab[i];
766
767         while (sp->namep) {
768                 if (*sp->namep == *namep && strcmp(sp->namepnamep) == 0)
ragge
1.15
769                         return sp->value || enterf == ENTER ? sp : NULL;
ragge
1.1
770                 if (++sp >= &symtab[SYMSIZ]) {
771                         if (around++)
772                                 error("too many defines");
773                         else
774                                 sp = symtab;
775                 }
776         }
ragge
1.15
777         if (enterf == ENTER)
ragge
1.12
778                 sp->namep = savstr(namep), savch('\0'), sp->value = NULL;
ragge
1.15
779
ragge
1.1
780         return(sp->namep ? sp : 0);
781 }
782
783 /*
784  * substitute namep for sp->value.
785  */
786 int
787 subst(npsprp)
788 char *np;
789 struct symtab *sp;
790 struct recur *rp;
791 {
792         struct recur rp2;
793         register usch *vp, *cp;
794         int crv = 0;
795
ragge
1.17
796 if (dflag)printf("subst: %s\n"sp->namep);
797         /*
798          * First check for special macros.
799          */
800         if (sp == filloc) {
801                 savch('"');
802                 savstr(curfile());
803                 savch('"');
804                 return 1;
805         } else if (sp == linloc) {
806                 char buf[12];
807                 sprintf(buf"%d"curline());
808                 savstr(buf);
ragge
1.1
809                 return 1;
810         }
ragge
1.17
811         vp = sp->value;
ragge
1.1
812
813         rp2.next = rp;
814         rp2.sp = sp;
815
816         if (*vp-- != OBJCT) {
817                 int gotwarn = 0;
818
819                 /* should we be here at all? */
820                 /* check if identifier is followed by parentheses */
821                 rv = 1;
822                 do {
823                         if ((c = yylex()) == NL)
824                                 putc('\n'obuf);
825                         if (c == WARN) {
826                                 gotwarn++;
827                                 if (rp == NULL)
828                                         goto noid;
829                         }
830                 } while (c == WSPACE || c == NL || c == WARN);
831
ragge
1.15
832                 cp = yystr;
ragge
1.1
833                 while (*cp)
834                         cp++;
ragge
1.15
835                 while (cp > (usch *)yystr)
ragge
1.1
836                         cunput(*--cp);
837 if (dflag)printf("c %d\n"c);
838                 if (c == '(' ) {
839                         expdef(vp, &rp2gotwarn);
840                         return rv;
841                 } else {
842                         /* restore identifier */
843 noid:                   while (gotwarn--)
844                                 cunput(WARN);
845                         cunput(' ');
846                         cp = sp->namep;
847                         while (*cp)
848                                 cp++;
849                         while (cp > sp->namep)
850                                 cunput(*--cp);
851                         if ((c = yylex()) != IDENT)
852                                 error("internal sync error");
853                         return 0;
854                 }
855         } else {
856                 cunput(WARN);
857                 cp = vp;
858                 while (*cp) {
859                         if (*cp != CONC)
860                                 cunput(*cp);
861                         cp--;
862                 }
863                 expmac(&rp2);
864         }
865         return 1;
866 }
867
868 /*
869  * do macro-expansion until WARN character read.
870  * will recurse into lookup() for recursive expansion.
871  * when returning all expansions on the token list is done.
872  */
873 void
874 expmac(struct recur *rp)
875 {
876         struct symtab *nl;
ragge
1.2
877         int cnoexp = 0gotspc;
ragge
1.4
878         usch *och;
ragge
1.1
879
880 if (dflag)printf("expmac\n");
881 if (dflag && rp)printf("do not expand %s\n"rp->sp->namep);
882         while ((c = yylex()) != WARN) {
883                 switch (c) {
884                 case NOEXPnoexp++; break;
885                 case EXPANDnoexp--; break;
886
887                 case IDENT:
ragge
1.4
888                         /* workaround if an arg will be concatenated */
889                         och = stringbuf;
ragge
1.15
890                         savstr(yystr);
ragge
1.4
891                         savch('\0');
ragge
1.18
892 if (dflag > 1)printf("id: str %s\n"och);
ragge
1.4
893                         if ((c = yylex()) == EXPAND) {
ragge
1.18
894 if (dflag > 1)printf("funnet expand\n");
ragge
1.4
895                                 if ((c = yylex()) == NOEXP) {
ragge
1.18
896 if (dflag > 1)printf("funnet noexp\n");
ragge
1.4
897                                         if ((c = yylex()) == IDENT) {
ragge
1.18
898 yid:
899 if (dflag > 1)printf("funnet ident %s%s\n"ochyystr);
ragge
1.4
900                                                 stringbuf--;
ragge
1.15
901                                                 savstr(yystr);
ragge
1.4
902                                                 savch('\0');
903                                                 cunput(NOEXP);
904                                                 unpstr(och);
905                                                 noexp--;
906                                                 stringbuf = och;
907                                                 continue;
908                                         } else {
ragge
1.18
909 if (dflag > 1)printf("ofunnet ident\n");
ragge
1.15
910                                                 unpstr(yystr);
ragge
1.4
911                                                 unpstr(och);
912                                                 stringbuf = och;
913                                                 continue;
914                                         }
915                                 } else {
ragge
1.18
916                                         if (c == IDENT)
917                                                 goto yid;
918 if (dflag > 1)printf("ofunnet inoexp\n");
ragge
1.15
919                                         unpstr(yystr);
ragge
1.4
920                                         cunput(EXPAND);
921                                         unpstr(och);
922                                         yylex();
923                                 }
924                         } else {
ragge
1.18
925 if (dflag > 1)printf("ofunnet expand got (%d)\n"c);
926                                 if (c == NOEXP) {
927                                         if ((c = yylex()) == IDENT) {
928                                                 noexp++;
929                                                 goto yid;
930                                         }
931                                         unpstr(yystr);
932                                         cunput(NOEXP);
933                                 } else
934                                         unpstr(yystr);
935 if (dflag > 1)printf("ofunnet expand yys (%d)\n", *yystr);
ragge
1.4
936                                 unpstr(och);
937                                 yylex();
ragge
1.18
938 if (dflag > 1)printf("ofunnet expand: yystr %s\n"yystr);
ragge
1.4
939                         }
940                         stringbuf = och;
941
ragge
1.15
942                         if ((nl = lookup(yystrFIND)) == NULL)
ragge
1.1
943                                 goto def;
ragge
1.4
944
ragge
1.1
945                         if (canexpand(rpnl) == 0)
946                                 goto def;
947                         if (noexp == 0) {
948                                 if ((c = subst(nl->namepnlrp)) == 0)
949                                         goto def;
950                                 break;
951                         }
952                         if (noexp != 1)
953                                 error("bad noexp %d"noexp);
ragge
1.2
954                         gotspc = 0;
ragge
1.1
955                         if ((c = yylex()) == WSPACE)
ragge
1.2
956                                 gotspc = 1c = yylex();
ragge
1.1
957                         if (c == EXPAND) {
958                                 noexp--;
959                                 if (subst(nl->namepnlrp))
960                                         break;
961                                 savstr(nl->namep);
ragge
1.2
962                                 if (gotspc)
963                                         savch(' ');
ragge
1.1
964                         } else {
ragge
1.15
965                                 unpstr(yystr);
ragge
1.2
966                                 if (gotspc)
967                                         cunput(' ');
ragge
1.1
968                                 savstr(nl->namep);
969                         }
970                         break;
971
ragge
1.15
972                 case STRING:
973                         /* remove EXPAND/NOEXP from strings */
974                         if (yystr[1] == NOEXP) {
975                                 savch('"');
976                                 och = &yystr[2];
977                                 while (*och != EXPAND)
978                                         savch(*och++);
979                                 savch('"');
980                                 break;
981                         }
982                         /* FALLTHROUGH */
983
ragge
1.1
984 def:            default:
ragge
1.15
985                         savstr(yystr);
ragge
1.1
986                         break;
987                 }
988         }
989 if (dflag)printf("return from expmac\n");
990 }
991
992 /*
993  * expand a function-like macro.
994  * vp points to end of replacement-list
995  * reads function arguments from yylex()
996  * result is written on top of heap
997  */
998 void
999 expdef(vprpgotwarn)
1000         usch *vp;
1001         struct recur *rp;
1002 {
ragge
1.6
1003         usch **args, *sptr, *ap, *bp, *sp;
ragge
1.5
1004         int nargciplevsnuffinstr;
ragge
1.1
1005