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