Quick Search:

View

Revision:
Expand:  
Changeset: MAIN:ragge:20040828151957

Diff

Diff from 1.13 to:

Annotations

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

Annotated File View

ragge
1.13
1 /*      $Id: cpp.c,v 1.13 2004/08/28 15:19:57 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.11
82 #define SBSIZE  20000
83 #define SYMSIZ  2000
ragge
1.1
84
85 static usch     sbf[SBSIZE];
86 /* C command */
87
88 /* buffer used internally */
89 #ifndef CPPBUF
90 #define CPPBUF  BUFSIZ
91 #endif
92
93 int tflag;      /* traditional cpp syntax */
94 #ifdef CPP_DEBUG
95 int dflag;      /* debug printouts */
96 #endif
97 FILE *obuf;
98 static int exfail;
ragge
1.10
99 struct symtab symtab[SYMSIZ];
ragge
1.1
100
101 /* avoid recursion */
102 struct recur {
103         struct recur *next;
104         struct symtab *sp;
105 };
106
ragge
1.10
107 /* include dirs */
108 struct incs {
109         struct incs *next;
110         char *dir;
111 } *incdir[2];
112 #define INCINC 0
113 #define SYSINC 1
114
ragge
1.1
115 static struct symtab *filloc;
116 static struct symtab *linloc;
ragge
1.10
117 int     trulvl;
118 int     flslvl;
119 int     elflvl;
120 int     elslvl;
121 usch *stringbuf = sbf;
ragge
1.1
122
123 /*
124  * Macro replacement list syntax:
125  * - For object-type macros, replacement strings are stored as-is.
126  * - For function-type macros, macro args are substituted for the
127  *   character WARN followed by the argument number.
ragge
1.12
128  * - The value element points to the end of the string, to simplify
129  *   pushback onto the input queue.
ragge
1.1
130  * 
ragge
1.12
131  * The first character (from the end) in the replacement list is
132  * the number of arguments:
ragge
1.1
133  *   OBJCT - object-type macro
134  *   0     - empty parenthesis, foo()
135  *   1->   - number of args.
136  */
137
138 #define OBJCT   0xff
139 #define WARN    1       /* SOH, not legal char */
140 #define CONC    2       /* STX, not legal char */
141 #define SNUFF   3       /* ETX, not legal char */
142 #define NOEXP   4       /* EOT, not legal char */
143 #define EXPAND  5       /* ENQ, not legal char */
144
145 /* args for lookup() */
146 #define FIND    0
147 #define ENTER   1
148 #define FORGET  3
149
150 static void expdef(usch *protostruct recur *, int gotwarn);
151 static void savch(int c);
152 static void control(void);
153 static usch *savstr(usch *str);
154 static void define(void);
155 static void expmac(struct recur *);
156 static int canexpand(struct recur *, struct symtab *np);
ragge
1.3
157 static void include(void);
ragge
1.9
158 static void line(void);
ragge
1.1
159
160 int
161 main(int argcchar **argv)
162 {
ragge
1.10
163         struct incs *w, *w2;
ragge
1.7
164         struct symtab *nl, *thisnl;
165         register int cgotspcch;
166         usch *osp;
ragge
1.1
167
ragge
1.7
168         while ((ch = getopt(argcargv"D:I:S:U:td")) != -1)
ragge
1.1
169                 switch (ch) {
ragge
1.7
170                 case 'D'/* Define something */
171                         osp = optarg;
172                         while (*osp && *osp != '=')
173                                 osp++;
174                         if (*osp == '=') {
ragge
1.10
175                                 *osp++ = 0;
176                                 while (*osp)
177                                         osp++;
178                                 *osp = OBJCT;
ragge
1.7
179                         } else {
ragge
1.10
180                                 static char c[3] = { 0'1'OBJCT };
181                                 osp = &c[2];
ragge
1.7
182                         }
183                         nl = lookup(optargENTER);
184                         if (nl->value)
185                                 error("%s redefined"optarg);
186                         nl->value = osp;
187                         break;
188
189                 case 'S':
ragge
1.10
190                 case 'I':
191                         w = calloc(sizeof(struct incs), 1);
192                         w->dir = optarg;
193                         w2 = incdir[ch == 'I' ? INCINC : SYSINC];
194                         if (w2 != NULL) {
195                                 while (w2->next)
196                                         w2 = w2->next;
197                                 w2->next = w;
198                         } else
199                                 incdir[ch == 'I' ? INCINC : SYSINC] = w;
ragge
1.7
200                         break;
201
202                 case 'U':
203                         nl = lookup(optargFIND);
204                         if (nl && nl->value)
205                                 nl->value = NULL;
206                         break;
ragge
1.1
207 #ifdef CPP_DEBUG
208                 case 'd':
209                         dflag = 1;
210                         break;
211 #endif
212                 case 't':
213                         tflag = 1;
214                         break;
215
216                 default:
217                         fprintf(stderr"bad arg %c\n"ch);
218                         exit(1);
219                 }
220         argc -= optind;
221         argv += optind;
222
223         exfail = 0;
ragge
1.7
224         if (argc) {
225                 if (freopen(argv[0], "r"stdin) == NULL) {
226                         fprintf(stderr"Can't open %s"argv[0]);
ragge
1.1
227                         exit(8);
228                 }
229         }
ragge
1.10
230         if (pushfile(argc ? argv[0] : "<stdin>"))
231                 error("cannot open %s"argv[0]);
ragge
1.1
232
ragge
1.7
233         if (argc == 2) {
234                 if ((obuf = fopen(argv[1], "w")) == 0) {
235                         fprintf(stderr"Can't creat %s\n"argv[1]);
ragge
1.1
236                         exit(8);
237                 }
238         } else
239                 obuf = stdout;
240
241         prtline();
242
ragge
1.12
243         filloc = lookup("__FILE__"ENTER);
244         linloc = lookup("__LINE__"ENTER);
245         if (tflag == 0) {
246                 time_t t = time(NULL);
247                 char *n = ctime(&t);
248
249                 /*
250                  * Manually move in the predefined macros.
251                  */
252                 nl = lookup("__TIME__"ENTER);
ragge
1.13
253                 savch(0); savch('"');  n[19] = 0savstr(&n[11]); savch('"');
ragge
1.12
254                 savch(OBJCT);
255                 nl->value = stringbuf-1;
256
257                 nl = lookup("__DATE__"ENTER);
ragge
1.13
258                 savch(0); savch('"'); n[24] = n[11] = 0savstr(&n[4]);
259                 savstr(&n[20]); savch('"'); savch(OBJCT);
ragge
1.12
260                 nl->value = stringbuf-1;
261
262                 nl = lookup("__STDC__"ENTER);
ragge
1.13
263                 savch(0); savch('1'); savch(OBJCT);
ragge
1.12
264                 nl->value = stringbuf-1;
265         }
ragge
1.1
266
267         thisnl = NULL;
268         while ((c = yylex()) != 0) {
269                 switch (c) {
270                 case CONTROL:
271                         control();
272                         break;
273
274                 case IDENT:
ragge
1.8
275                         if (flslvl)
276                                 break;
ragge
1.1
277                         osp = stringbuf;
278                         nl = lookup(yytextFIND);
279                         if (nl == 0 || thisnl == 0)
280                                 goto found;
281                         if (thisnl == nl) {
282                                 nl = 0;
283                                 goto found;
284                         }
ragge
1.2
285                         gotspc = 0;
ragge
1.1
286                         if ((c = yylex()) == WSPACE)
ragge
1.2
287                                 gotspc = 1c = yylex();
ragge
1.1
288                         if (c != EXPAND) {
289                                 unpstr(yytext);
ragge
1.2
290                                 if (gotspc)
291                                         cunput(' ');
ragge
1.1
292                                 unpstr(nl->namep);
293                                 (void)yylex(); /* get yytext correct */
294                                 nl = 0/* ignore */
295                         } else
296                                 thisnl = NULL;
297
298 found:                  if (nl == 0 || subst(yytextnlNULL) == 0) {
299                                 fputs(yytextobuf);
300                         } else if (osp != stringbuf) {
301                                 cunput(EXPAND);
302                                 while (stringbuf > osp)
303                                         cunput(*--stringbuf);
304                                 thisnl = nl;
305                         }
306                         stringbuf = osp/* clean up heap */
307                         break;
308
309                 case EXPAND:
310                         thisnl = NULL;
311                         break;
312
313                 case CHARCON:
314                 case NUMBER:
315                 case FPOINT:
316                 case STRING:
317                 case WSPACE:
318                 case NL:
ragge
1.3
319                 default:
ragge
1.8
320                         if (flslvl == 0)
321                                 fputs(yytextobuf);
ragge
1.1
322                         break;
323                 }
324         }
325         fclose(obuf);
ragge
1.7
326         return exfail;
ragge
1.1
327 }
328
329 /*
330  * do something when a '#' is found.
331  */
332 void
333 control()
334 {
335         struct symtab *np;
336         int t;
337
ragge
1.8
338 #define CHECK(x) (yytext[0] == #x[0]) && strcmp(yytext, #x) == 0
339
ragge
1.1
340         if ((t = yylex()) == WSPACE)
341                 t = yylex();
342         if (t != IDENT)
343                 return error("bad control '%s'"yytext);
344
ragge
1.8
345         if (CHECK(include)) {
ragge
1.1
346                 if (flslvl)
347                         goto exit;
ragge
1.3
348                 include();
ragge
1.1
349                 return;
ragge
1.8
350         } else if (CHECK(else)) {
ragge
1.1
351                 if (flslvl) {
ragge
1.10
352                         if (elflvl > trulvl)
353                                 ;
354                         else if (--flslvl!=0) {
ragge
1.1
355                                 flslvl++;
ragge
1.8
356                         } else {
ragge
1.1
357                                 trulvl++;
ragge
1.8
358                                 prtline();
359                         }
ragge
1.1
360                 } else if (trulvl) {
361                         flslvl++;
362                         trulvl--;
363                 } else
364                         error("If-less else");
ragge
1.10
365                 if (elslvl==trulvl+flslvlerror("Too many else");
366                 elslvl=trulvl+flslvl;
ragge
1.8
367         } else if (CHECK(endif)) {
368                 if (flslvl) {
369                         flslvl--;
370                         if (flslvl == 0)
371                                 prtline();
372                 } else if (trulvl)
373                         trulvl--;
374                 else
375                         error("If-less endif");
ragge
1.10
376                 if (flslvl == 0)
377                         elflvl = 0;
378                 elslvl = 0;
ragge
1.8
379         } else if (CHECK(error)) {
380                 usch *ch = stringbuf;
ragge
1.11
381                 if (flslvl)
382                         goto exit;
ragge
1.8
383                 while (yylex() != NL)
384                         savstr(yytext);
385                 savch('\n');
386                 error("error: %s"ch);
ragge
1.9
387 #define GETID() if (yylex() != WSPACE || yylex() != IDENT) goto cfail
388         } else if (CHECK(define)) {
ragge
1.10
389                 if (flslvl)
390                         goto exit;
ragge
1.9
391                 GETID();
392                 define();
393         } else if (CHECK(ifdef)) {
394                 GETID();
395                 if (flslvl == 0 && lookup(yytextFIND) != 0)
396                         trulvl++;
397                 else
398                         flslvl++;
399         } else if (CHECK(ifndef)) {
400                 GETID();
401                 if (flslvl == 0 && lookup(yytextFIND) == 0)
402                         trulvl++;
403                 else
404                         flslvl++;
405         } else if (CHECK(undef)) {
406                 GETID();
407                 if (flslvl == 0 && (np = lookup(yytextFIND)))
408                         np->value = 0;
409         } else if (CHECK(line)) {
ragge
1.10
410                 if (flslvl)
411                         goto exit;
ragge
1.9
412                 line();
ragge
1.10
413         } else if (CHECK(if)) {
414                 if (flslvl==0 && yyparse())
415                         ++trulvl;
416                 else
417                         ++flslvl;
418         } else if (CHECK(elif)) {
419                 if (flslvl == 0)
420                         elflvl = trulvl;
421                 if (flslvl) {
422                         if (elflvl > trulvl)
423                                 ;
424                         else if (--flslvl!=0)
425                                 ++flslvl;
426                         else {
427                                 if (yyparse()) {
428                                         ++trulvl;
429                                         prtline();
430                                 } else
431                                         ++flslvl;
432                         }
433                 } else if (trulvl) {
434                         ++flslvl;
435                         --trulvl;
436                 } else
437                         error("If-less elif");
ragge
1.9
438         } else
439                 error("undefined control '%s'"yytext);
440
441         return;
442
443 cfail:
444         error("control line syntax error");
ragge
1.1
445
446 exit:
447         while (yylex() != NL)
448                 ;
ragge
1.10
449         putc('\n'obuf);
ragge
1.8
450 #undef CHECK
ragge
1.1
451 }
452
453 void
ragge
1.9
454 line()
455 {
456         struct symtab *nl;
457         int c;
458
459         if (yylex() != WSPACE)
460                 goto bad;
461         if ((c = yylex()) == IDENT) {
462                 /* Do macro preprocessing first */
463                 usch *osp = stringbuf;
464                 if ((nl = lookup(yytextFIND)) == NULL)
465                         goto bad;
466                 if (subst(yytextnlNULL) == 0)
467                         goto bad;
468                 while (stringbuf > osp)
469                         cunput(*--stringbuf);
470                 c = yylex();
471         }
472
473         if (c != NUMBER)
474                 goto bad;
475         setline(atoi(yytext));
476
477         if ((c = yylex()) != NL && c != WSPACE)
478                 goto bad;
479         if (c == NL)
480                 return setline(curline()+1);
481         if (yylex() != STRING)
482                 goto bad;
483         yytext[strlen(yytext)-1] = 0;
484         setfile(&yytext[1]);
485         return;
486
487 bad:    error("bad line directive");
488 }
489
ragge
1.10
490 /*
491  * Include a file. Include order:
492  * - if name inside <>, only search system includes.
493  * - if name inside "", first search current dir, then -I dirs, 
494  *   then system includes.
495  */
ragge
1.9
496 void
ragge
1.3
497 include()
498 {
ragge
1.10
499         struct incs *w;
ragge
1.3
500         struct symtab *nl;
ragge
1.10
501         usch *osp;
502         char *fn;
503         int icit;
ragge
1.3
504
ragge
1.10
505         osp = stringbuf;
ragge
1.3
506         if (yylex() != WSPACE)
507                 goto bad;
ragge
1.10
508 again:  if ((c = yylex()) != STRING && c != '<' && c != IDENT)
ragge
1.3
509                 goto bad;
510
511         if (c == IDENT) {
512                 if ((nl = lookup(yytextFIND)) == NULL)
513                         goto bad;
514                 if (subst(yytextnlNULL) == 0)
515                         goto bad;
516                 savch('\0');
ragge
1.10
517                 unpstr(osp);
518                 goto again;
519         } else if (c == '<') {
520                 fn = stringbuf;
521                 while ((c = yylex()) != '>' && c != NL) {
522                         if (c == NL)
523                                 goto bad;
524                         savstr(yytext);
525                 }
526                 savch('\0');
527                 it = SYSINC;
ragge
1.3
528         } else {
529                 yytext[strlen(yytext)-1] = 0;
ragge
1.10
530                 fn = &yytext[1];
531                 it = INCINC;
532         }
533
534         /* create search path and try to open file */
535         for (i = iti < 2i++) {
536                 for (w = incdir[i]; ww = w->next) {
537                         usch *nm = stringbuf;
538
539                         savstr(w->dir); savch('/');
540                         savstr(fn); savch(0);
541                         if (pushfile(nm) == 0)
542                                 goto ret;
543                         stringbuf = nm;
544                 }
ragge
1.3
545         }
ragge
1.10
546         error("cannot find '%s'"fn);
547         stringbuf = osp;
ragge
1.3
548         return;
549
550 bad:    error("bad include");
ragge
1.10
551 ret:    prtline();
552         stringbuf = osp;
ragge
1.3
553 }
554
555 void
ragge
1.1
556 define()
557 {
558         struct symtab *np;
ragge
1.2
559         usch *args[MAXARG], *ubuf;
ragge
1.1
560         int ci;
561         int mkstr = 0narg = -1;
562
563         np = lookup(yytextENTER);
ragge
1.7
564         if (np->value)
565                 error("%s redefined"np->namep);
ragge
1.1
566
567         if ((c = yylex()) == '(') {
568                 narg = 0;
569                 /* function-like macros, deal with identifiers */
570                 while ((c = yylex()) != ')') {
571                         if (c == WSPACEc = yylex();
572                         if (c == ','c = yylex();
573                         if (c == WSPACEc = yylex();
574                         if (c == ')')
575                                 break;
576                                 if (c != IDENT)
577                         error("define error");
578                         args[narg] = alloca(strlen(yytext)+1);
579                         strcpy(args[narg], yytext);
580                         narg++;
581                 }
ragge
1.8
582         } else if (c == NL) {
583                 /* #define foo */
584                 cunput('\n');
ragge
1.1
585         } else if (c != WSPACE)
586                 error("bad define");
587
588         if ((c = yylex()) == WSPACE)
589                 c = yylex();
590
591         /* parse replacement-list, substituting arguments */
592         savch('\0');
593         while (c != NL) {
594                 switch (c) {
595                 case WSPACE:
596                         /* remove spaces if it surrounds a ## directive */
ragge
1.2
597                         ubuf = stringbuf;
ragge
1.1
598                         savstr(yytext);
599                         c = yylex();
600                         if (c == CONCAT) {
ragge
1.2
601                                 stringbuf = ubuf;
ragge
1.1
602                                 savch(CONC);
603                                 if ((c = yylex()) == WSPACE)
604                                         c = yylex();
605                         }
606                         continue;
607
608                 case CONCAT:
609                         /* No spaces before concat op */
610                         savch(CONC);
611                         if ((c = yylex()) == WSPACE)
612                                 c = yylex();
613                         continue;
614
615                 case MKSTR:
616                         if (narg < 0) {
617                                 /* no meaning in object-type macro */
618                                 savch('#');
619                                 break;
620                         }
621                         /* remove spaces between # and arg */
622                         savch(SNUFF);
623                         if ((c = yylex()) == WSPACE)
624                                 c = yylex(); /* whitespace, ignore */
625                         mkstr = 1;
626
627                         /* FALLTHROUGH */
628                 case IDENT:
629                         if (narg < 0)
630                                 goto id/* just add it if object */
631                         /* check if its an argument */
632                         for (i = 0i < nargi++)
633                                 if (strcmp(yytextargs[i]) == 0)
634                                         break;
635                         if (i == narg) {
636                                 if (mkstr)
637                                         error("not argument");
638                                 goto id;
639                         }
640                         savch(i);
641                         savch(WARN);
642                         if (mkstr)
643                                 savch(SNUFF), mkstr = 0;
644                         break;
645
646                 default:
647 id:                     savstr(yytext);
648                         break;
649                 }
650                 c = yylex();
651         }
652         savch(narg < 0 ? OBJCT : narg);
653         np->value = stringbuf-1;
ragge
1.2
654         putc('\n'obuf);
ragge
1.1
655
656 #ifdef CPP_DEBUG
657         if (dflag) {
658                 usch *w = np->value;
659
660                 printf("!define: ");
661                 if (*w == OBJCT)
662                         printf("[object]");
663                 else
664                         printf("[%d]", *w);
665                 while (*--w) {
666                         switch (*w) {
667                         case WARNprintf("<%d>", *--w); break;
668                         case CONCprintf("<##>"); break;
669                         case SNUFFprintf("<\">"); break;
670                         defaultputchar(*w); break;
671                         }
672                 }
673                 putchar('\n');
674         }
675 #endif
676 }
677
678 void
679 error(char *s, ...)
680 {
681         va_list ap;
682
683         va_start(aps);
684         fprintf(stderr"%s:%d: "curfile(), curline());
685         vfprintf(stderrsap);
686         fputc('\n'stderr);
687         exfail++;
688         va_end(ap);
689         exit(8);
690 }
691
692 /*
693  * store a character into the "define" buffer.
694  */
695 void
696 savch(c)
697 {
698         *stringbuf++ = c;
699         if (stringbuf-sbf < SBSIZE)
700                 return;
701         error("Too much defining");
702         exit(1);
703 }
704
705 /*
706  * Do a symbol lookup.
707  * If enterf == FIND, only lookup.
708  */
709 struct symtab *
710 lookup(namepenterf)
711         char *namep;
712 {
713         register char *np;
714         register struct symtab *sp;
715         int icaround;
716
ragge
1.3
717 if (dflag)printf("lookup '%s'\n"namep);
ragge
1.1
718         np = namep;
719         around = i = 0;
720         while ((c = *np++))
721                 i =+ c;
722         i %= SYMSIZ;
723         sp = &symtab[i];
724
725         while (sp->namep) {
726                 if (*sp->namep == *namep && strcmp(sp->namepnamep) == 0)
727                         return(sp);
728                 if (++sp >= &symtab[SYMSIZ]) {
729                         if (around++)
730                                 error("too many defines");
731                         else
732                                 sp = symtab;
733                 }
734         }
ragge
1.7
735         if (enterf == ENTER) {
ragge
1.12
736                 sp->namep = savstr(namep), savch('\0'), sp->value = NULL;
ragge
1.7
737         }
ragge
1.1
738         return(sp->namep ? sp : 0);
739 }
740
741 /*
742  * substitute namep for sp->value.
743  */
744 int
745 subst(npsprp)
746 char *np;
747 struct symtab *sp;
748 struct recur *rp;
749 {
750         struct recur rp2;
751         register usch *vp, *cp;
752         int crv = 0;
753
754 if (dflag)printf("subst\n");
ragge
1.9
755         if ((vp = sp->value) == 0) {
756                 /*
757                  * If no value is assigned, it may be a special macro,
758                  * otherwise a deleted macro.
759                  */
760                 if (sp == filloc) {
761                         savch('"');
762                         savstr(curfile());
763                         savch('"');
764                 } else if (sp == linloc) {
765                         char buf[12];
766                         sprintf(buf"%d"curline());
767                         savstr(buf);
768                 } else
769                         return 0;
ragge
1.1
770                 return 1;
771         }
772
773         rp2.next = rp;
774         rp2.sp = sp;
775
776         if (*vp-- != OBJCT) {
777                 int gotwarn = 0;
778
779                 /* should we be here at all? */
780                 /* check if identifier is followed by parentheses */
781                 rv = 1;
782                 do {
783                         if ((c = yylex()) == NL)
784                                 putc('\n'obuf);
785                         if (c == WARN) {
786                                 gotwarn++;
787                                 if (rp == NULL)
788                                         goto noid;
789                         }
790                 } while (c == WSPACE || c == NL || c == WARN);
791
792                 cp = yytext;
793                 while (*cp)
794                         cp++;
795                 while ((char *)cp > yytext)
796                         cunput(*--cp);
797 if (dflag)printf("c %d\n"c);
798                 if (c == '(' ) {
799                         expdef(vp, &rp2gotwarn);
800                         return rv;
801                 } else {
802                         /* restore identifier */
803 noid:                   while (gotwarn--)
804                                 cunput(WARN);
805                         cunput(' ');
806                         cp = sp->namep;
807                         while (*cp)
808                                 cp++;
809                         while (cp > sp->namep)
810                                 cunput(*--cp);
811                         if ((c = yylex()) != IDENT)
812                                 error("internal sync error");
813                         return 0;
814                 }
815         } else {
816                 cunput(WARN);
817                 cp = vp;
818                 while (*cp) {
819                         if (*cp != CONC)
820                                 cunput(*cp);
821                         cp--;
822                 }
823                 expmac(&rp2);
824         }
825         return 1;
826 }
827
828 /*
829  * do macro-expansion until WARN character read.
830  * will recurse into lookup() for recursive expansion.
831  * when returning all expansions on the token list is done.
832  */
833 void
834 expmac(struct recur *rp)
835 {
836         struct symtab *nl;
ragge
1.2
837         int cnoexp = 0gotspc;
ragge
1.4
838         usch *och;
ragge
1.1
839
840 if (dflag)printf("expmac\n");
841 if (dflag && rp)printf("do not expand %s\n"rp->sp->namep);
842         while ((c = yylex()) != WARN) {
843                 switch (c) {
844                 case NOEXPnoexp++; break;
845                 case EXPANDnoexp--; break;
846
847                 case IDENT:
ragge
1.4
848                         /* workaround if an arg will be concatenated */
849                         och = stringbuf;
850                         savstr(yytext);
851                         savch('\0');
852 //printf("id: str %s\n", och);
853                         if ((c = yylex()) == EXPAND) {
854 //printf("funnet expand\n");
855                                 if ((c = yylex()) == NOEXP) {
856 //printf("funnet noexp\n");
857                                         if ((c = yylex()) == IDENT) {
858 //printf("funnet ident %s%s\n", och, yytext);
859                                                 stringbuf--;
860                                                 savstr(yytext);
861                                                 savch('\0');
862                                                 cunput(NOEXP);
863                                                 unpstr(och);
864                                                 noexp--;
865                                                 stringbuf = och;
866                                                 continue;
867                                         } else {
868 //printf("ofunnet ident\n");
869                                                 unpstr(yytext);
870                                                 unpstr(och);
871                                                 stringbuf = och;
872                                                 continue;
873                                         }
874                                 } else {
875 //printf("ofunnet inoexp\n");
876                                         unpstr(yytext);
877                                         cunput(EXPAND);
878                                         unpstr(och);
879                                         yylex();
880                                 }
881                         } else {
882                                 unpstr(yytext);
883                                 unpstr(och);
884                                 yylex();
885 //printf("ofunnet expand: yytext %s\n", yytext);
886                         }
887                         stringbuf = och;
888
ragge
1.1
889                         if ((nl = lookup(yytextFIND)) == NULL)
890                                 goto def;
ragge
1.4
891
ragge
1.1
892                         if (canexpand(rpnl) == 0)
893                                 goto def;
894                         if (noexp == 0) {
895                                 if ((c = subst(nl->namepnlrp)) == 0)
896                                         goto def;
897                                 break;
898                         }
899                         if (noexp != 1)
900                                 error("bad noexp %d"noexp);
ragge
1.2
901                         gotspc = 0;
ragge
1.1
902                         if ((c = yylex()) == WSPACE)
ragge
1.2
903                                 gotspc = 1c = yylex();
ragge
1.1
904                         if (c == EXPAND) {
905                                 noexp--;
906                                 if (subst(nl->namepnlrp))
907                                         break;
908                                 savstr(nl->namep);
ragge
1.2
909                                 if (gotspc)
910                                         savch(' ');
ragge
1.1
911                         } else {
912                                 unpstr(yytext);
ragge
1.2
913                                 if (gotspc)
914                                         cunput(' ');
ragge
1.1
915                                 savstr(nl->namep);
916                         }
917                         break;
918
919 def:            default:
920                         savstr(yytext);
921                         break;
922                 }
923         }
924 if (dflag)printf("return from expmac\n");
925 }
926
927 /*
928  * expand a function-like macro.
929  * vp points to end of replacement-list
930  * reads function arguments from yylex()
931  * result is written on top of heap
932  */
933 void
934 expdef(vprpgotwarn)
935         usch *vp;
936         struct recur *rp;
937 {
ragge
1.6
938         usch **args, *sptr, *ap, *bp, *sp;
ragge
1.5
939         int nargciplevsnuffinstr;
ragge
1.1
940
941 if (dflag)printf("expdef %s rp %s\n"vp, (rp ? (char *)rp->sp->namep : ""));
942         if ((c = yylex()) != '(')
943                 error("got %c, expected )"c);
944         narg = vp[1];
945         args = alloca(sizeof(usch *) * narg);
946
947
948         /*
949          * read arguments and store them on heap.
950          * will be removed just before return from this function.
951          */
ragge
1.6
952         sptr = stringbuf;
ragge
1.1
953         for (i = 0i < narg && c != ')'i++) {
954                 args[i] = stringbuf;
955                 plev = 0;
ragge
1.2
956                 if ((c = yylex()) == WSPACE)
957                         c = yylex();
ragge
1.1
958                 for (;;) {
959                         if (plev == 0 && (c == ')' || c == ','))
960                                 break;
961                         if (c == '(')
962                                 plev++;
963                         if (c == ')')
964                                 plev--;
965                         savstr(yytext);
ragge
1.2
966                         c = yylex();
ragge
1.1
967                 }
ragge
1.2
968                 while (args[i] < stringbuf &&
969                     (stringbuf[-1] == ' ' || stringbuf[-1] == '\t'))
970                         stringbuf--;
ragge
1.1
971                 savch('\0');
972         }
973         if (narg == 0)
974                 c = yylex();
975         if (c != ')' || i != narg)
976                 error("wrong arg count");
977
978         while (gotwarn--)
979                 cunput(WARN);
980
981 #ifdef CPP_DEBUG
982         if (dflag) {
983
984         }
985 #endif
986         sp = vp;
ragge
1.5
987         instr = snuff = 0;
ragge
1.1
988
989         /*
990          * push-back replacement-list onto lex buffer while replacing
991          * arguments. 
992          */
993         cunput(WARN);
994         while (*sp != 0) {
995                 if (*sp == SNUFF)
996                         cunput('\"'), snuff ^= 1;
997                 else if (*sp == CONC)
998                         ;
999                 else if (*sp == WARN) {
1000
1001                         bp = ap = args[(int)*--sp];
1002                         if (sp[2] != CONC && !snuff && sp[-1] != CONC) {
1003                                 cunput(WARN);
1004                                 while (*bp)
1005                                         bp++;
1006                                 while (bp > ap)
1007                                         cunput(*--bp);
1008 if (dflagprintf("expand arg %d string %s\n", *spap);
1009                                 bp = ap = stringbuf;
1010                                 savch(NOEXP);
1011                                 expmac(NULL);
1012                                 savch(EXPAND);
1013                                 savch('\0');
1014                         }
1015                         while (*bp)
1016                                 bp++;
1017                         while (bp > ap) {
ragge
1.6
1018                                 bp--;
1019 //printf("*bp %d\n", *bp);
1020                                 if (snuff && !instr && 
1021                                     (*bp == ' ' || *bp == '\t' || *bp == '\n')){
1022                                         while (*bp == ' ' || *bp == '\t' ||
1023                                             *bp == '\n') {
1024                                                 if (*bp == '\n')
1025                                                         putc('\n'obuf);
1026                                                 bp--;
1027                                         }
1028                                         cunput(' ');
1029                                 }
1030                                 cunput(*bp);
1031                                 if ((*bp == '\'' || *bp == '"')
1032                                      && bp[-1] != '\\' && snuff) {
ragge
1.5
1033                                         instr ^= 1;
ragge
1.6
1034                                         if (instr == 0 && *bp == '"')
ragge
1.5
1035                                                 cunput('\\');
1036                                 }
1037                                 if (instr && (*bp == '\\' || *bp == '"'))
ragge
1.1
1038                                         cunput('\\');
1039                         }
ragge
1.4
1040                 } else
ragge
1.1
1041                         cunput(*sp);
1042                 sp--;
1043         }
ragge
1.6
1044         stringbuf = sptr;
ragge
1.1
1045
1046         /* scan the input buffer (until WARN) and save result on heap */
1047         expmac(rp);
1048 }
1049
1050 usch *
1051 savstr(usch *str)
1052 {
1053         char *rv = stringbuf;
1054
1055         while ((*stringbuf++ = *str++))
1056                 if (stringbuf >= &sbf[SBSIZE])
1057                         error("out of macro space!");
1058         stringbuf--;