1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/types.h>
26 * parm[], parmp, and parlist[] are used to store #define() argument
27 * lists. nargs contains the actual number of parameters stored.
29 static char parm
[NPARMWORK
+ 1]; /* define param work buffer */
30 static char* parmp
; /* Free space in parm */
31 static char* parlist
[LASTPARM
]; /* -> start of each parameter */
32 static int nargs
; /* Parameters for this macro */
37 for( i
= 0; i
< NPARMWORK
; i
++ )
39 for( i
= 0; i
< LASTPARM
; i
++ )
47 * Called from control when a #define is scanned. This module
48 * parses formal parameters and the replacement string. When
49 * the formal parameter name is encountered in the replacement
50 * string, it is replaced by a character in the range 128 to
51 * 128+NPARAM (this allows up to 32 parameters within the
52 * Dec Multinational range).
54 * There is some special case code to distinguish
56 * from #define foo() bar
58 * Also, we make sure that
60 * expands to "foo" but doesn't put cpp into an infinite loop.
62 * A warning message is printed if you redefine a symbol to a
63 * different text. I.e,
71 * The following subroutines are called from define():
72 * checkparm called when a token is scanned. It checks through the
73 * array of formal parameters. If a match is found, the
74 * token is replaced by a control byte which will be used
75 * to locate the parameter when the macro is expanded.
76 * textput puts a string in the macro work area (parm[]), updating
77 * parmp to point to the first free byte in parm[].
78 * textput() tests for work buffer overflow.
79 * charput puts a single character in the macro work area (parm[])
80 * in a manner analogous to textput().
85 DEFBUF
* dp
; /* -> new definition */
86 int isredefine
; /* TRUE if redefined */
87 char* old
= 0; /* Remember redefined */
89 if (type
[(c
= skipws())] != LET
)
91 isredefine
= FALSE
; /* Set if redefining */
92 if ((dp
= lookid(c
)) == NULL
) /* If not known now */
93 dp
= defendel(token
, FALSE
); /* Save the name */
94 else /* It's known: */
96 isredefine
= TRUE
; /* Remember this fact */
97 old
= dp
->repl
; /* Remember replacement */
98 dp
->repl
= NULL
; /* No replacement now */
100 parlist
[0] = parmp
= parm
; /* Setup parm buffer */
101 if ((c
= get()) == '(') /* With arguments? */
103 nargs
= 0; /* Init formals counter */
104 do /* Collect formal parms */
106 if (nargs
>= LASTPARM
)
107 cfatal("Too many arguments for macro", NULLST
);
108 else if ((c
= skipws()) == ')')
109 break; /* Got them all */
110 else if (type
[c
] != LET
) /* Bad formal syntax */
112 scanid(c
); /* Get the formal param */
113 parlist
[nargs
++] = parmp
; /* Save its start */
114 textput(token
); /* Save text in parm[] */
116 while ((c
= skipws()) == ','); /* Get another argument */
117 if (c
!= ')') /* Must end at ) */
119 c
= ' '; /* Will skip to body */
124 * DEF_NOARGS is needed to distinguish between
125 * "#define foo" and "#define foo()".
127 nargs
= DEF_NOARGS
; /* No () parameters */
129 if (type
[c
] == SPA
) /* At whitespace? */
130 c
= skipws(); /* Not any more. */
131 workp
= work
; /* Replacement put here */
132 inmacro
= TRUE
; /* Keep \<newline> now */
133 while (c
!= EOF_CHAR
&& c
!= '\n') /* Compile macro body */
135 if (c
== '#') /* Token concatenation? */
137 while (workp
> work
&& type
[(int)workp
[-1]] == SPA
)
138 --workp
; /* Erase leading spaces */
139 save(TOK_SEP
); /* Stuff a delimiter */
140 c
= skipws(); /* Eat whitespace */
141 if (type
[c
] == LET
) /* Another token here? */
142 ; /* Stuff it normally */
143 else if (type
[c
] == DIG
) /* Digit string after? */
145 while (type
[c
] == DIG
) /* Stuff the digits */
150 save(TOK_SEP
); /* Delimit 2nd token */
154 ciwarn("Strange character after # (%d.)", c
);
161 checkparm(c
, dp
); /* Might be a formal */
164 case DIG
: /* Number in mac. body */
165 case DOT
: /* Maybe a float number */
166 scannumber(c
, save
); /* Scan it off */
169 case QUO
: /* String in mac. body */
173 case BSH
: /* Backslash */
175 if ((c
= get()) == '\n')
180 case SPA
: /* Absorb whitespace */
182 * Note: the "end of comment" marker is passed on
183 * to allow comments to separate tokens.
185 if (workp
[-1] == ' ') /* Absorb multiple */
188 c
= ' '; /* Normalize tabs */
189 /* Fall through to store character */
190 default: /* Other character */
196 inmacro
= FALSE
; /* Stop newline hack */
197 unget(); /* For control check */
198 if (workp
> work
&& workp
[-1] == ' ') /* Drop trailing blank */
200 *workp
= EOS
; /* Terminate work */
201 dp
->repl
= savestring(work
); /* Save the string */
202 dp
->nargs
= nargs
; /* Save arg count */
203 #if OSL_DEBUG_LEVEL > 1
205 dumpadef("macro definition", dp
);
209 if (isredefine
) /* Error if redefined */
211 if ((old
!= NULL
&& dp
->repl
!= NULL
&& !streq(old
, dp
->repl
)) ||
212 (old
== NULL
&& dp
->repl
!= NULL
) ||
213 (old
!= NULL
&& dp
->repl
== NULL
))
216 cerror("Redefining defined variable \"%s\"", dp
->name
);
218 cwarn("Redefining defined variable \"%s\"", dp
->name
);
221 if (old
!= NULL
) /* We don't need the */
222 free(old
); /* old definition now. */
227 cerror("#define syntax error", NULLST
);
228 inmacro
= FALSE
; /* Stop <newline> hack */
232 * Replace this param if it's defined. Note that the macro name is a
233 * possible replacement token. We stuff DEF_MAGIC in front of the token
234 * which is treated as a LETTER by the token scanner and eaten by
235 * the output routine. This prevents the macro expander from
236 * looping if someone writes "#define foo foo".
238 void checkparm(int c
, DEFBUF
* dp
)
243 scanid(c
); /* Get parm to token[] */
244 for (i
= 0; i
< nargs
; i
++) /* For each argument */
246 if (streq(parlist
[i
], token
)) /* If it's known */
251 save(i
+ MAC_PARM
); /* Save a magic cookie */
252 return; /* And exit the search */
255 if (streq(dp
->name
, token
)) /* Macro name in body? */
256 save(DEF_MAGIC
); /* Save magic marker */
257 for (cp
= token
; *cp
!= EOS
;) /* And save */
258 save(*cp
++); /* The token itself */
262 * Normal string parameter scan.
264 void stparmscan(int delim
)
269 wp
= workp
; /* Here's where it starts */
270 if (!scanstring(delim
, save
))
271 return; /* Exit on scanstring error */
272 workp
[-1] = EOS
; /* Erase trailing quote */
273 wp
++; /* -> first string content byte */
274 for (i
= 0; i
< nargs
; i
++)
276 if (streq(parlist
[i
], wp
))
280 *wp
++ = MAC_PARM
+ PAR_MAC
; /* Stuff a magic marker */
281 *wp
++ = (char)(i
+ MAC_PARM
); /* Make a formal marker */
282 *wp
= wp
[-4]; /* Add on closing quote */
283 workp
= wp
+ 1; /* Reset string end */
285 *wp
++ = MAC_PARM
+ PAR_MAC
; /* Stuff a magic marker */
286 *wp
++ = (i
+ MAC_PARM
); /* Make a formal marker */
287 *wp
= wp
[-3]; /* Add on closing quote */
288 workp
= wp
+ 1; /* Reset string end */
293 workp
[-1] = wp
[-1]; /* Nope, reset end quote. */
297 * Remove the symbol from the defined list.
298 * Called from the #control processor.
304 if (type
[(c
= skipws())] != LET
)
305 cerror("Illegal #undef argument", NULLST
);
308 scanid(c
); /* Get name to token[] */
309 if (defendel(token
, TRUE
) == NULL
)
312 cwarn("Symbol \"%s\" not defined in #undef", token
);
319 * Put the string in the parm[] buffer.
321 void textput(char* text
)
325 size
= strlen(text
) + 1;
326 if ((parmp
+ size
) >= &parm
[NPARMWORK
])
327 cfatal("Macro work area overflow", NULLST
);
336 * Put the byte in the parm[] buffer.
340 if (parmp
>= &parm
[NPARMWORK
])
341 cfatal("Macro work area overflow", NULLST
);
349 * M a c r o E x p a n s i o n
352 static DEFBUF
* macro
; /* Catches start of infinite macro */
355 * Expand a macro. Called from the cpp mainline routine (via subroutine
356 * macroid()) when a token is found in the symbol table. It calls
357 * expcollect() to parse actual parameters, checking for the correct number.
358 * It then creates a "file" containing a single line containing the
359 * macro with actual parameters inserted appropriately. This is
360 * "pushed back" onto the input stream. (When the get() routine runs
361 * off the end of the macro line, it will dismiss the macro itself.)
363 void expand(DEFBUF
* tokenp
)
368 #if OSL_DEBUG_LEVEL > 1
370 dumpadef("expand entry", tokenp
);
373 * If no macro is pending, save the name of this macro
374 * for an eventual error message.
376 if (recursion
++ == 0)
378 else if (recursion
== RECURSION_LIMIT
)
380 cerror("Recursive macro definition of \"%s\"", tokenp
->name
);
381 fprintf(stderr
, "(Defined by \"%s\")\n", macro
->name
);
388 while (infile
!= NULL
&& infile
->fp
== NULL
);
395 * Here's a macro to expand.
397 nargs
= 0; /* Formals counter */
398 parmp
= parm
; /* Setup parm buffer */
399 switch (tokenp
->nargs
)
401 case (-2): /* __LINE__ */
402 sprintf(work
, "%d", line
);
406 case (-3): /* __FILE__ */
407 for (file
= infile
; file
!= NULL
; file
= file
->parent
)
409 if (file
->fp
!= NULL
)
411 sprintf(work
, "\"%s\"", (file
->progname
!= NULL
)
412 ? file
->progname
: file
->filename
);
421 * Nothing funny about this macro.
423 if (tokenp
->nargs
< 0)
424 cfatal("Bug: Illegal __ macro \"%s\"", tokenp
->name
);
425 while ((c
= skipws()) == '\n') /* Look for (, skipping */
426 wrongline
= TRUE
; /* spaces and newlines */
430 * If the programmer writes
434 * just write foo to the output stream.
437 cwarn("Macro \"%s\" needs arguments", tokenp
->name
);
438 fputs(tokenp
->name
, pCppOut
);
441 else if (expcollect()) /* Collect arguments */
443 if (tokenp
->nargs
!= nargs
) /* Should be an error? */
445 cwarn("Wrong number of macro arguments for \"%s\"",
448 #if OSL_DEBUG_LEVEL > 1
452 } /* Collect arguments */
453 case DEF_NOARGS
: /* No parameters just stuffs */
454 expstuff(tokenp
); /* Do actual parameters */
459 * Collect the actual parameters for this macro. TRUE if ok.
461 FILE_LOCAL
int expcollect()
464 int paren
; /* For embedded ()'s */
467 paren
= 0; /* Collect next arg. */
468 while ((c
= skipws()) == '\n') /* Skip over whitespace */
469 wrongline
= TRUE
; /* and newlines. */
470 if (c
== ')') /* At end of all args? */
473 * Note that there is a guard byte in parm[]
474 * so we don't have to check for overflow here.
476 *parmp
= EOS
; /* Make sure terminated */
477 break; /* Exit collection loop */
479 else if (nargs
>= LASTPARM
)
480 cfatal("Too many arguments in macro expansion", NULLST
);
481 parlist
[nargs
++] = parmp
; /* At start of new arg */
482 for (;; c
= cget()) /* Collect arg's bytes */
486 cerror("end of file within macro argument", NULLST
);
487 return FALSE
; /* Sorry. */
489 else if (c
== '\\') /* Quote next character */
491 charput(c
); /* Save the \ for later */
492 charput(cget()); /* Save the next char. */
493 continue; /* And go get another */
495 else if (type
[c
] == QUO
) /* Start of string? */
497 scanstring(c
, charput
); /* Scan it off */
498 continue; /* Go get next char */
500 else if (c
== '(') /* Worry about balance */
501 paren
++; /* To know about commas */
502 else if (c
== ')') /* Other side too */
504 if (paren
== 0) /* At the end? */
506 unget(); /* Look at it later */
507 break; /* Exit arg getter. */
509 paren
--; /* More to come. */
511 else if (c
== ',' && paren
== 0) /* Comma delimits args */
513 else if (c
== '\n') /* Newline inside arg? */
514 wrongline
= TRUE
; /* We'll need a #line */
515 charput(c
); /* Store this one */
516 } /* Collect an argument */
517 charput(EOS
); /* Terminate argument */
518 #if OSL_DEBUG_LEVEL > 1
520 fprintf( pCppOut
, "parm[%d] = \"%s\"\n", nargs
, parlist
[nargs
- 1]);
522 } /* Collect all args. */
523 return TRUE
; /* Normal return */
527 * Stuff the macro body, replacing formal parameters by actual parameters.
529 FILE_LOCAL
void expstuff(DEFBUF
* tokenp
)
531 int c
; /* Current character */
532 char* inp
; /* -> repl string */
533 char* defp
; /* -> macro output buff */
534 size_t size
; /* Actual parm. size */
535 char* defend
; /* -> output buff end */
536 int string_magic
; /* String formal hack */
537 FILEINFO
* file
; /* Funny #include */
539 file
= getfile(NBUFF
, tokenp
->name
);
540 inp
= tokenp
->repl
; /* -> macro replacement */
541 defp
= file
->buffer
; /* -> output buffer */
542 defend
= defp
+ (NBUFF
- 1); /* Note its end */
545 while ((c
= (*inp
++ & 0xFF)) != EOS
)
552 if (c
>= MAC_PARM
&& c
<= (MAC_PARM
+ PAR_MAC
))
555 string_magic
= (c
== (MAC_PARM
+ PAR_MAC
));
559 * Replace formal parameter by actual parameter string.
561 if ((c
-= MAC_PARM
) < nargs
)
563 size
= strlen(parlist
[c
]);
564 if ((defp
+ size
) >= defend
)
567 * Erase the extra set of quotes.
569 if (string_magic
&& defp
[-1] == parlist
[c
][0])
571 strcpy(defp
-1, parlist
[c
]);
576 strcpy(defp
, parlist
[c
]);
581 else if (defp
>= defend
)
584 cfatal("Out of space in macro \"%s\" arg expansion",
594 #if OSL_DEBUG_LEVEL > 1
596 fprintf( pCppOut
, "macroline: \"%s\"\n", file
->buffer
);
600 #if OSL_DEBUG_LEVEL > 1
603 * Dump parameter list.
605 void dumpparm(char* why
)
609 fprintf( pCppOut
, "dump of %d parameters (%" SAL_PRI_SIZET
"u bytes total) %s\n",
610 nargs
, parmp
- parm
, why
);
611 for (i
= 0; i
< nargs
; i
++)
613 fprintf( pCppOut
, "parm[%d] (%d) = \"%s\"\n",
614 i
+ 1, (int)strlen(parlist
[i
]), parlist
[i
]);
619 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */