xref: /trunk/main/rsc/source/rscpp/cpp4.c (revision 509a48ff)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include        <stdio.h>
25 #include        <ctype.h>
26 #include        "cppdef.h"
27 #include        "cpp.h"
28 /*
29  * parm[], parmp, and parlist[] are used to store #define() argument
30  * lists.  nargs contains the actual number of parameters stored.
31  */
32 static char     parm[NPARMWORK + 1];    /* define param work buffer     */
33 static char     *parmp;                 /* Free space in parm           */
34 static char     *parlist[LASTPARM];     /* -> start of each parameter   */
35 static int      nargs;                  /* Parameters for this macro    */
36 
InitCpp4()37 void InitCpp4()
38 {
39     int i;
40     for( i = 0; i < NPARMWORK; i++ )
41         parm[ i ] = 0;
42     for( i = 0; i < LASTPARM; i++ )
43         parlist[ i ] = NULL;
44 
45     nargs = 0;
46 }
47 
48 
dodefine()49 void dodefine()
50 /*
51  * Called from control when a #define is scanned.  This module
52  * parses formal parameters and the replacement string.  When
53  * the formal parameter name is encountered in the replacement
54  * string, it is replaced by a character in the range 128 to
55  * 128+NPARAM (this allows up to 32 parameters within the
56  * Dec Multinational range).  If cpp is ported to an EBCDIC
57  * machine, you will have to make other arrangements.
58  *
59  * There is some special case code to distinguish
60  *      #define foo     bar
61  * from #define foo()   bar
62  *
63  * Also, we make sure that
64  *      #define foo     foo
65  * expands to "foo" but doesn't put cpp into an infinite loop.
66  *
67  * A warning message is printed if you redefine a symbol to a
68  * different text.  I.e,
69  *      #define foo     123
70  *      #define foo     123
71  * is ok, but
72  *      #define foo     123
73  *      #define foo     +123
74  * is not.
75  *
76  * The following subroutines are called from define():
77  * checkparm    called when a token is scanned.  It checks through the
78  *              array of formal parameters.  If a match is found, the
79  *              token is replaced by a control byte which will be used
80  *              to locate the parameter when the macro is expanded.
81  * textput      puts a string in the macro work area (parm[]), updating
82  *              parmp to point to the first free byte in parm[].
83  *              textput() tests for work buffer overflow.
84  * charput      puts a single character in the macro work area (parm[])
85  *              in a manner analogous to textput().
86  */
87 {
88         register int            c;
89         register DEFBUF         *dp;            /* -> new definition    */
90         int                     isredefine;     /* TRUE if redefined    */
91         char                    *old = NULL;       /* Remember redefined   */
92 
93         if (type[(c = skipws())] != LET)
94             goto bad_define;
95         isredefine = FALSE;                     /* Set if redefining    */
96         if ((dp = lookid(c)) == NULL)           /* If not known now     */
97             dp = defendel(token, FALSE);        /* Save the name        */
98         else {                                  /* It's known:          */
99             isredefine = TRUE;                  /* Remember this fact   */
100             old = dp->repl;                     /* Remember replacement */
101             dp->repl = NULL;                    /* No replacement now   */
102         }
103         parlist[0] = parmp = parm;              /* Setup parm buffer    */
104         if ((c = get()) == '(') {               /* With arguments?      */
105             nargs = 0;                          /* Init formals counter */
106             do {                                /* Collect formal parms */
107                 if (nargs >= LASTPARM)
108                     cfatal("Too many arguments for macro", NULLST);
109                 else if ((c = skipws()) == ')')
110                     break;                      /* Got them all         */
111                 else if (type[c] != LET)        /* Bad formal syntax    */
112                     goto bad_define;
113                 scanid(c);                      /* Get the formal param */
114                 parlist[nargs++] = parmp;       /* Save its start       */
115                 textput(token);                 /* Save text in parm[]  */
116             } while ((c = skipws()) == ',');    /* Get another argument */
117             if (c != ')')                       /* Must end at )        */
118                 goto bad_define;
119             c = ' ';                            /* Will skip to body    */
120         }
121         else {
122             /*
123              * DEF_NOARGS is needed to distinguish between
124              * "#define foo" and "#define foo()".
125              */
126             nargs = DEF_NOARGS;                 /* No () parameters     */
127         }
128         if (type[c] == SPA)                     /* At whitespace?       */
129             c = skipws();                       /* Not any more.        */
130         workp = work;                           /* Replacement put here */
131         inmacro = TRUE;                         /* Keep \<newline> now  */
132         while (c != EOF_CHAR && c != '\n') {    /* Compile macro body   */
133 #if OK_CONCAT
134 #if COMMENT_INVISIBLE
135             if (c == COM_SEP) {                 /* Token concatenation? */
136                 save(TOK_SEP);                  /* Stuff a delimiter    */
137                 c = get();
138 #else
139             if (c == '#') {                     /* Token concatenation? */
140                 while (workp > work && type[(int)workp[-1]] == SPA)
141                     --workp;                    /* Erase leading spaces */
142                 save(TOK_SEP);                  /* Stuff a delimiter    */
143                 c = skipws();                   /* Eat whitespace       */
144 #endif
145                 if (type[c] == LET)             /* Another token here?  */
146                     ;                           /* Stuff it normally    */
147                 else if (type[c] == DIG) {      /* Digit string after?  */
148                     while (type[c] == DIG) {    /* Stuff the digits     */
149                         save(c);
150                         c = get();
151                     }
152                     save(TOK_SEP);              /* Delimit 2nd token    */
153                 }
154                 else {
155 #if ! COMMENT_INVISIBLE
156                     ciwarn("Strange character after # (%d.)", c);
157 #endif
158                 }
159                 continue;
160             }
161 #endif
162             switch (type[c]) {
163             case LET:
164                 checkparm(c, dp);               /* Might be a formal    */
165                 break;
166 
167             case DIG:                           /* Number in mac. body  */
168             case DOT:                           /* Maybe a float number */
169                 scannumber(c, save);            /* Scan it off          */
170                 break;
171 
172             case QUO:                           /* String in mac. body  */
173 #if STRING_FORMAL
174                 stparmscan(c, dp);              /* Do string magic      */
175 #else
176                 stparmscan(c);
177 #endif
178                 break;
179 
180             case BSH:                           /* Backslash            */
181                 save('\\');
182                 if ((c = get()) == '\n')
183                     wrongline = TRUE;
184                 save(c);
185                 break;
186 
187             case SPA:                           /* Absorb whitespace    */
188                 /*
189                  * Note: the "end of comment" marker is passed on
190                  * to allow comments to separate tokens.
191                  */
192                 if (workp[-1] == ' ')           /* Absorb multiple      */
193                     break;                      /* spaces               */
194                 else if (c == '\t')
195                     c = ' ';                    /* Normalize tabs       */
196                 /* Fall through to store character                      */
197             default:                            /* Other character      */
198                 save(c);
199                 break;
200             }
201             c = get();
202         }
203         inmacro = FALSE;                        /* Stop newline hack    */
204         unget();                                /* For control check    */
205         if (workp > work && workp[-1] == ' ')   /* Drop trailing blank  */
206             workp--;
207         *workp = EOS;                           /* Terminate work       */
208         dp->repl = savestring(work);            /* Save the string      */
209         dp->nargs = nargs;                      /* Save arg count       */
210 #if OSL_DEBUG_LEVEL > 1
211         if (debug)
212             dumpadef("macro definition", dp);
213 		else if (bDumpDefs)
214             dumpadef(NULL, dp);
215 #endif
216         if (isredefine) {                       /* Error if redefined   */
217             if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
218              || (old == NULL && dp->repl != NULL)
219              || (old != NULL && dp->repl == NULL)) {
220 #ifdef STRICT_UNDEF
221                 cerror("Redefining defined variable \"%s\"", dp->name);
222 #else
223                 cwarn("Redefining defined variable \"%s\"", dp->name);
224 #endif
225             }
226             if (old != NULL)                    /* We don't need the    */
227                 free(old);                      /* old definition now.  */
228         }
229         return;
230 
231 bad_define:
232         cerror("#define syntax error", NULLST);
233         inmacro = FALSE;                        /* Stop <newline> hack  */
234 }
235 
236 void checkparm(int c, DEFBUF* dp)
237 /*
238  * Replace this param if it's defined.  Note that the macro name is a
239  * possible replacement token.  We stuff DEF_MAGIC in front of the token
240  * which is treated as a LETTER by the token scanner and eaten by
241  * the output routine.  This prevents the macro expander from
242  * looping if someone writes "#define foo foo".
243  */
244 {
245         register int            i;
246         register char           *cp;
247 
248         scanid(c);                              /* Get parm to token[]  */
249         for (i = 0; i < nargs; i++) {           /* For each argument    */
250             if (streq(parlist[i], token)) {     /* If it's known        */
251 #ifdef SOLAR
252                 save(DEL);
253 #endif
254                 save(i + MAC_PARM);             /* Save a magic cookie  */
255                 return;                         /* And exit the search  */
256             }
257         }
258         if (streq(dp->name, token))             /* Macro name in body?  */
259             save(DEF_MAGIC);                    /* Save magic marker    */
260         for (cp = token; *cp != EOS;)           /* And save             */
261             save(*cp++);                        /* The token itself     */
262 }
263 
264 #if STRING_FORMAL
265 void stparmscan(delim, dp)
266 int             delim;
267 register DEFBUF *dp;
268 /*
269  * Scan the string (starting with the given delimiter).
270  * The token is replaced if it is the only text in this string or
271  * character constant.  The algorithm follows checkparm() above.
272  * Note that scanstring() has approved of the string.
273  */
274 {
275         register int            c;
276 
277         /*
278          * Warning -- this code hasn't been tested for a while.
279          * It exists only to preserve compatibility with earlier
280          * implementations of cpp.  It is not part of the Draft
281          * ANSI Standard C language.
282          */
283         save(delim);
284         instring = TRUE;
285         while ((c = get()) != delim
286              && c != '\n'
287              && c != EOF_CHAR) {
288             if (type[c] == LET)                 /* Maybe formal parm    */
289                 checkparm(c, dp);
290             else {
291                 save(c);
292                 if (c == '\\')
293                     save(get());
294             }
295         }
296         instring = FALSE;
297         if (c != delim)
298             cerror("Unterminated string in macro body", NULLST);
299         save(c);
300 }
301 #else
302 void stparmscan(int delim)
303 /*
304  * Normal string parameter scan.
305  */
306 {
307         register char           *wp;
308         register int            i;
309 
310         wp = workp;                     /* Here's where it starts       */
311         if (!scanstring(delim, save))
312             return;                     /* Exit on scanstring error     */
313         workp[-1] = EOS;                /* Erase trailing quote         */
314         wp++;                           /* -> first string content byte */
315         for (i = 0; i < nargs; i++) {
316             if (streq(parlist[i], wp)) {
317 #ifdef SOLAR
318                 *wp++ = DEL;
319                 *wp++ = MAC_PARM + PAR_MAC;     /* Stuff a magic marker */
320                 *wp++ = (char)(i + MAC_PARM);   /* Make a formal marker */
321                 *wp = wp[-4];                   /* Add on closing quote */
322                 workp = wp + 1;                 /* Reset string end     */
323 #else
324                 *wp++ = MAC_PARM + PAR_MAC;     /* Stuff a magic marker */
325                 *wp++ = (i + MAC_PARM);         /* Make a formal marker */
326                 *wp = wp[-3];                   /* Add on closing quote */
327                 workp = wp + 1;                 /* Reset string end     */
328 #endif
329                 return;
330             }
331         }
332         workp[-1] = wp[-1];             /* Nope, reset end quote.       */
333 }
334 #endif
335 
336 void doundef()
337 /*
338  * Remove the symbol from the defined list.
339  * Called from the #control processor.
340  */
341 {
342         register int            c;
343 
344         if (type[(c = skipws())] != LET)
345             cerror("Illegal #undef argument", NULLST);
346         else {
347             scanid(c);                          /* Get name to token[]  */
348             if (defendel(token, TRUE) == NULL) {
349 #ifdef STRICT_UNDEF
350                 cwarn("Symbol \"%s\" not defined in #undef", token);
351 #endif
352             }
353         }
354 }
355 
356 void textput(char* text)
357 /*
358  * Put the string in the parm[] buffer.
359  */
360 {
361         register int    size;
362 
363         size = strlen(text) + 1;
364         if ((parmp + size) >= &parm[NPARMWORK])
365             cfatal("Macro work area overflow", NULLST);
366         else {
367             strcpy(parmp, text);
368             parmp += size;
369         }
370 }
371 
372 void charput(int c)
373 /*
374  * Put the byte in the parm[] buffer.
375  */
376 {
377         if (parmp >= &parm[NPARMWORK])
378             cfatal("Macro work area overflow", NULLST);
379         else {
380             *parmp++ = (char)c;
381         }
382 }
383 
384 /*
385  *              M a c r o   E x p a n s i o n
386  */
387 
388 static DEFBUF   *macro;         /* Catches start of infinite macro      */
389 
390 void expand(DEFBUF* tokenp)
391 /*
392  * Expand a macro.  Called from the cpp mainline routine (via subroutine
393  * macroid()) when a token is found in the symbol table.  It calls
394  * expcollect() to parse actual parameters, checking for the correct number.
395  * It then creates a "file" containing a single line containing the
396  * macro with actual parameters inserted appropriately.  This is
397  * "pushed back" onto the input stream.  (When the get() routine runs
398  * off the end of the macro line, it will dismiss the macro itself.)
399  */
400 {
401         register int            c;
402         register FILEINFO       *file;
403 #ifndef ZTC  /* BP */
404     extern FILEINFO     *getfile();
405 #endif
406 
407 #if OSL_DEBUG_LEVEL > 1
408         if (debug)
409             dumpadef("expand entry", tokenp);
410 #endif
411         /*
412          * If no macro is pending, save the name of this macro
413          * for an eventual error message.
414          */
415         if (recursion++ == 0)
416             macro = tokenp;
417         else if (recursion == RECURSION_LIMIT) {
418             cerror("Recursive macro definition of \"%s\"", tokenp->name);
419             fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
420             if (rec_recover) {
421                 do {
422                     c = get();
423                 } while (infile != NULL && infile->fp == NULL);
424                 unget();
425                 recursion = 0;
426                 return;
427             }
428         }
429         /*
430          * Here's a macro to expand.
431          */
432         nargs = 0;                              /* Formals counter      */
433         parmp = parm;                           /* Setup parm buffer    */
434         switch (tokenp->nargs) {
435         case (-2):                              /* __LINE__             */
436             sprintf(work, "%d", line);
437             ungetstring(work);
438             break;
439 
440         case (-3):                              /* __FILE__             */
441             for (file = infile; file != NULL; file = file->parent) {
442                 if (file->fp != NULL) {
443                     sprintf(work, "\"%s\"", (file->progname != NULL)
444                         ? file->progname : file->filename);
445                     ungetstring(work);
446                     break;
447                 }
448             }
449             break;
450 
451         default:
452             /*
453              * Nothing funny about this macro.
454              */
455             if (tokenp->nargs < 0)
456                 cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
457             while ((c = skipws()) == '\n')      /* Look for (, skipping */
458                 wrongline = TRUE;               /* spaces and newlines  */
459             if (c != '(') {
460                 /*
461                  * If the programmer writes
462                  *      #define foo() ...
463                  *      ...
464                  *      foo [no ()]
465                  * just write foo to the output stream.
466                  */
467                 unget();
468                 cwarn("Macro \"%s\" needs arguments", tokenp->name);
469 		        fputs(tokenp->name, pCppOut );
470                 return;
471             }
472             else if (expcollect()) {            /* Collect arguments    */
473                 if (tokenp->nargs != nargs) {   /* Should be an error?  */
474                     cwarn("Wrong number of macro arguments for \"%s\"",
475                         tokenp->name);
476                 }
477 #if OSL_DEBUG_LEVEL > 1
478                 if (debug)
479                     dumpparm("expand");
480 #endif
481             }                           /* Collect arguments            */
482         case DEF_NOARGS:                /* No parameters just stuffs    */
483             expstuff(tokenp);           /* Do actual parameters         */
484         }                               /* nargs switch                 */
485 }
486 
487 FILE_LOCAL int
488 expcollect()
489 /*
490  * Collect the actual parameters for this macro.  TRUE if ok.
491  */
492 {
493         register int    c;
494         register int    paren;                  /* For embedded ()'s    */
495         for (;;) {
496             paren = 0;                          /* Collect next arg.    */
497             while ((c = skipws()) == '\n')      /* Skip over whitespace */
498                 wrongline = TRUE;               /* and newlines.        */
499             if (c == ')') {                     /* At end of all args?  */
500                 /*
501                  * Note that there is a guard byte in parm[]
502                  * so we don't have to check for overflow here.
503                  */
504                 *parmp = EOS;                   /* Make sure terminated */
505                 break;                          /* Exit collection loop */
506             }
507             else if (nargs >= LASTPARM)
508                 cfatal("Too many arguments in macro expansion", NULLST);
509             parlist[nargs++] = parmp;           /* At start of new arg  */
510             for (;; c = cget()) {               /* Collect arg's bytes  */
511                 if (c == EOF_CHAR) {
512                     cerror("end of file within macro argument", NULLST);
513                     return (FALSE);             /* Sorry.               */
514                 }
515                 else if (c == '\\') {           /* Quote next character */
516                     charput(c);                 /* Save the \ for later */
517                     charput(cget());            /* Save the next char.  */
518                     continue;                   /* And go get another   */
519                 }
520                 else if (type[c] == QUO) {      /* Start of string?     */
521                     scanstring(c, charput);     /* Scan it off          */
522                     continue;                   /* Go get next char     */
523                 }
524                 else if (c == '(')              /* Worry about balance  */
525                     paren++;                    /* To know about commas */
526                 else if (c == ')') {            /* Other side too       */
527                     if (paren == 0) {           /* At the end?          */
528                         unget();                /* Look at it later     */
529                         break;                  /* Exit arg getter.     */
530                     }
531                     paren--;                    /* More to come.        */
532                 }
533                 else if (c == ',' && paren == 0) /* Comma delimits args */
534                     break;
535                 else if (c == '\n')             /* Newline inside arg?  */
536                     wrongline = TRUE;           /* We'll need a #line   */
537                 charput(c);                     /* Store this one       */
538             }                                   /* Collect an argument  */
539             charput(EOS);                       /* Terminate argument   */
540 #if OSL_DEBUG_LEVEL > 1
541             if (debug)
542             fprintf( pCppOut, "parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
543 #endif
544         }                                       /* Collect all args.    */
545         return (TRUE);                          /* Normal return        */
546 }
547 
548 FILE_LOCAL
549 void expstuff(DEFBUF* tokenp)
550 /*
551  * Stuff the macro body, replacing formal parameters by actual parameters.
552  */
553 {
554         register int    c;                      /* Current character    */
555         register char   *inp;                   /* -> repl string       */
556         register char   *defp;                  /* -> macro output buff */
557         int             size;                   /* Actual parm. size    */
558         char            *defend;                /* -> output buff end   */
559         int             string_magic;           /* String formal hack   */
560         FILEINFO        *file;                  /* Funny #include       */
561 #ifndef ZTC  /* BP */
562     extern FILEINFO *getfile();
563 #endif
564 
565         file = getfile(NBUFF, tokenp->name);
566         inp = tokenp->repl;                     /* -> macro replacement */
567         defp = file->buffer;                    /* -> output buffer     */
568         defend = defp + (NBUFF - 1);            /* Note its end         */
569         if (inp != NULL) {
570             while ((c = (*inp++ & 0xFF)) != EOS) {
571 #ifdef SOLAR
572                 if (c == DEL) {
573                     c = (*inp++ & 0xFF);
574 #else
575                 if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
576 #endif
577                     string_magic = (c == (MAC_PARM + PAR_MAC));
578                     if (string_magic)
579                         c = (*inp++ & 0xFF);
580                     /*
581                      * Replace formal parameter by actual parameter string.
582                      */
583                     if ((c -= MAC_PARM) < nargs) {
584                         size = strlen(parlist[c]);
585                         if ((defp + size) >= defend)
586                             goto nospace;
587                         /*
588                          * Erase the extra set of quotes.
589                          */
590                         if (string_magic && defp[-1] == parlist[c][0]) {
591                             strcpy(defp-1, parlist[c]);
592                             defp += (size - 2);
593                         }
594                         else {
595                             strcpy(defp, parlist[c]);
596                             defp += size;
597                         }
598                     }
599                 }
600                 else if (defp >= defend) {
601 nospace:            cfatal("Out of space in macro \"%s\" arg expansion",
602                         tokenp->name);
603                 }
604                 else {
605                     *defp++ = (char)c;
606                 }
607             }
608         }
609         *defp = EOS;
610 #if OSL_DEBUG_LEVEL > 1
611         if (debug > 1)
612             fprintf( pCppOut, "macroline: \"%s\"\n", file->buffer);
613 #endif
614 }
615 
616 #if OSL_DEBUG_LEVEL > 1
617 void dumpparm(char* why)
618 /*
619  * Dump parameter list.
620  */
621 {
622         register int    i;
623 
624     fprintf( pCppOut, "dump of %d parameters (%d bytes total) %s\n",
625             nargs, parmp - parm, why);
626         for (i = 0; i < nargs; i++) {
627         fprintf( pCppOut, "parm[%d] (%d) = \"%s\"\n",
628                 i + 1, (int)strlen(parlist[i]), parlist[i]);
629         }
630 }
631 #endif
632