Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <sal/types.h>
21 : #include <stdio.h>
22 : #include <ctype.h>
23 : #include "cppdef.h"
24 : #include "cpp.h"
25 : /*
26 : * parm[], parmp, and parlist[] are used to store #define() argument
27 : * lists. nargs contains the actual number of parameters stored.
28 : */
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 */
33 :
34 361 : void InitCpp4()
35 : {
36 : int i;
37 92416361 : for( i = 0; i < NPARMWORK; i++ )
38 92416000 : parm[ i ] = 0;
39 91694 : for( i = 0; i < LASTPARM; i++ )
40 91333 : parlist[ i ] = NULL;
41 :
42 361 : nargs = 0;
43 361 : }
44 :
45 :
46 : /*
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).
53 : *
54 : * There is some special case code to distinguish
55 : * #define foo bar
56 : * from #define foo() bar
57 : *
58 : * Also, we make sure that
59 : * #define foo foo
60 : * expands to "foo" but doesn't put cpp into an infinite loop.
61 : *
62 : * A warning message is printed if you redefine a symbol to a
63 : * different text. I.e,
64 : * #define foo 123
65 : * #define foo 123
66 : * is ok, but
67 : * #define foo 123
68 : * #define foo +123
69 : * is not.
70 : *
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().
81 : */
82 486845 : void dodefine()
83 : {
84 : int c;
85 : DEFBUF* dp; /* -> new definition */
86 : int isredefine; /* TRUE if redefined */
87 486845 : char* old = 0; /* Remember redefined */
88 :
89 486845 : if (type[(c = skipws())] != LET)
90 0 : goto bad_define;
91 486845 : isredefine = FALSE; /* Set if redefining */
92 486845 : if ((dp = lookid(c)) == NULL) /* If not known now */
93 479576 : dp = defendel(token, FALSE); /* Save the name */
94 : else /* It's known: */
95 : {
96 7269 : isredefine = TRUE; /* Remember this fact */
97 7269 : old = dp->repl; /* Remember replacement */
98 7269 : dp->repl = NULL; /* No replacement now */
99 : }
100 486845 : parlist[0] = parmp = parm; /* Setup parm buffer */
101 486845 : if ((c = get()) == '(') /* With arguments? */
102 : {
103 45 : nargs = 0; /* Init formals counter */
104 : do /* Collect formal parms */
105 : {
106 50 : if (nargs >= LASTPARM)
107 0 : cfatal("Too many arguments for macro", NULLST);
108 50 : else if ((c = skipws()) == ')')
109 2 : break; /* Got them all */
110 48 : else if (type[c] != LET) /* Bad formal syntax */
111 0 : goto bad_define;
112 48 : scanid(c); /* Get the formal param */
113 48 : parlist[nargs++] = parmp; /* Save its start */
114 48 : textput(token); /* Save text in parm[] */
115 : }
116 48 : while ((c = skipws()) == ','); /* Get another argument */
117 45 : if (c != ')') /* Must end at ) */
118 0 : goto bad_define;
119 45 : c = ' '; /* Will skip to body */
120 : }
121 : else
122 : {
123 : /*
124 : * DEF_NOARGS is needed to distinguish between
125 : * "#define foo" and "#define foo()".
126 : */
127 486800 : nargs = DEF_NOARGS; /* No () parameters */
128 : }
129 486845 : if (type[c] == SPA) /* At whitespace? */
130 484204 : c = skipws(); /* Not any more. */
131 486845 : workp = work; /* Replacement put here */
132 486845 : inmacro = TRUE; /* Keep \<newline> now */
133 4196837 : while (c != EOF_CHAR && c != '\n') /* Compile macro body */
134 : {
135 3223147 : if (c == '#') /* Token concatenation? */
136 : {
137 0 : while (workp > work && type[(int)workp[-1]] == SPA)
138 0 : --workp; /* Erase leading spaces */
139 0 : save(TOK_SEP); /* Stuff a delimiter */
140 0 : c = skipws(); /* Eat whitespace */
141 0 : if (type[c] == LET) /* Another token here? */
142 : ; /* Stuff it normally */
143 0 : else if (type[c] == DIG) /* Digit string after? */
144 : {
145 0 : while (type[c] == DIG) /* Stuff the digits */
146 : {
147 0 : save(c);
148 0 : c = get();
149 : }
150 0 : save(TOK_SEP); /* Delimit 2nd token */
151 : }
152 : else
153 : {
154 0 : ciwarn("Strange character after # (%d.)", c);
155 : }
156 0 : continue;
157 : }
158 3223147 : switch (type[c])
159 : {
160 : case LET:
161 417761 : checkparm(c, dp); /* Might be a formal */
162 417761 : break;
163 :
164 : case DIG: /* Number in mac. body */
165 : case DOT: /* Maybe a float number */
166 425767 : scannumber(c, save); /* Scan it off */
167 425767 : break;
168 :
169 : case QUO: /* String in mac. body */
170 50427 : stparmscan(c);
171 50427 : break;
172 :
173 : case BSH: /* Backslash */
174 12462 : save('\\');
175 12462 : if ((c = get()) == '\n')
176 12462 : wrongline = TRUE;
177 12462 : save(c);
178 12462 : break;
179 :
180 : case SPA: /* Absorb whitespace */
181 : /*
182 : * Note: the "end of comment" marker is passed on
183 : * to allow comments to separate tokens.
184 : */
185 1131139 : if (workp[-1] == ' ') /* Absorb multiple */
186 174768 : break; /* spaces */
187 956371 : else if (c == '\t')
188 0 : c = ' '; /* Normalize tabs */
189 : /* Fall through to store character */
190 : default: /* Other character */
191 2141962 : save(c);
192 2141962 : break;
193 : }
194 3223147 : c = get();
195 : }
196 486845 : inmacro = FALSE; /* Stop newline hack */
197 486845 : unget(); /* For control check */
198 486845 : if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
199 15438 : workp--;
200 486845 : *workp = EOS; /* Terminate work */
201 486845 : dp->repl = savestring(work); /* Save the string */
202 486845 : dp->nargs = nargs; /* Save arg count */
203 : #if OSL_DEBUG_LEVEL > 1
204 : if (debug)
205 : dumpadef("macro definition", dp);
206 : else if (bDumpDefs)
207 : dumpadef(NULL, dp);
208 : #endif
209 486845 : if (isredefine) /* Error if redefined */
210 : {
211 7269 : if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl)) ||
212 0 : (old == NULL && dp->repl != NULL) ||
213 7269 : (old != NULL && dp->repl == NULL))
214 : {
215 : #ifdef STRICT_UNDEF
216 : cerror("Redefining defined variable \"%s\"", dp->name);
217 : #else
218 0 : cwarn("Redefining defined variable \"%s\"", dp->name);
219 : #endif
220 : }
221 7269 : if (old != NULL) /* We don't need the */
222 7269 : free(old); /* old definition now. */
223 : }
224 973690 : return;
225 :
226 : bad_define:
227 0 : cerror("#define syntax error", NULLST);
228 0 : inmacro = FALSE; /* Stop <newline> hack */
229 : }
230 :
231 : /*
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".
237 : */
238 417761 : void checkparm(int c, DEFBUF* dp)
239 : {
240 : int i;
241 : char* cp;
242 :
243 417761 : scanid(c); /* Get parm to token[] */
244 417827 : for (i = 0; i < nargs; i++) /* For each argument */
245 : {
246 131 : if (streq(parlist[i], token)) /* If it's known */
247 : {
248 : #ifdef SOLAR
249 65 : save(DEL);
250 : #endif
251 65 : save(i + MAC_PARM); /* Save a magic cookie */
252 417826 : return; /* And exit the search */
253 : }
254 : }
255 417696 : if (streq(dp->name, token)) /* Macro name in body? */
256 0 : save(DEF_MAGIC); /* Save magic marker */
257 6576587 : for (cp = token; *cp != EOS;) /* And save */
258 5741195 : save(*cp++); /* The token itself */
259 : }
260 :
261 : /*
262 : * Normal string parameter scan.
263 : */
264 50427 : void stparmscan(int delim)
265 : {
266 : char* wp;
267 : int i;
268 :
269 50427 : wp = workp; /* Here's where it starts */
270 50427 : if (!scanstring(delim, save))
271 0 : return; /* Exit on scanstring error */
272 50427 : workp[-1] = EOS; /* Erase trailing quote */
273 50427 : wp++; /* -> first string content byte */
274 50427 : for (i = 0; i < nargs; i++)
275 : {
276 0 : if (streq(parlist[i], wp))
277 : {
278 : #ifdef SOLAR
279 0 : *wp++ = DEL;
280 0 : *wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
281 0 : *wp++ = (char)(i + MAC_PARM); /* Make a formal marker */
282 0 : *wp = wp[-4]; /* Add on closing quote */
283 0 : workp = wp + 1; /* Reset string end */
284 : #else
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 */
289 : #endif
290 0 : return;
291 : }
292 : }
293 50427 : workp[-1] = wp[-1]; /* Nope, reset end quote. */
294 : }
295 :
296 : /*
297 : * Remove the symbol from the defined list.
298 : * Called from the #control processor.
299 : */
300 29 : void doundef()
301 : {
302 : int c;
303 :
304 29 : if (type[(c = skipws())] != LET)
305 0 : cerror("Illegal #undef argument", NULLST);
306 : else
307 : {
308 29 : scanid(c); /* Get name to token[] */
309 29 : if (defendel(token, TRUE) == NULL)
310 : {
311 : #ifdef STRICT_UNDEF
312 : cwarn("Symbol \"%s\" not defined in #undef", token);
313 : #endif
314 : }
315 : }
316 29 : }
317 :
318 : /*
319 : * Put the string in the parm[] buffer.
320 : */
321 48 : void textput(char* text)
322 : {
323 : size_t size;
324 :
325 48 : size = strlen(text) + 1;
326 48 : if ((parmp + size) >= &parm[NPARMWORK])
327 0 : cfatal("Macro work area overflow", NULLST);
328 : else
329 : {
330 48 : strcpy(parmp, text);
331 48 : parmp += size;
332 : }
333 48 : }
334 :
335 : /*
336 : * Put the byte in the parm[] buffer.
337 : */
338 8671 : void charput(int c)
339 : {
340 8671 : if (parmp >= &parm[NPARMWORK])
341 0 : cfatal("Macro work area overflow", NULLST);
342 : else
343 : {
344 8671 : *parmp++ = (char)c;
345 : }
346 8671 : }
347 :
348 : /*
349 : * M a c r o E x p a n s i o n
350 : */
351 :
352 : static DEFBUF* macro; /* Catches start of infinite macro */
353 :
354 : /*
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.)
362 : */
363 78570 : void expand(DEFBUF* tokenp)
364 : {
365 : int c;
366 : FILEINFO* file;
367 :
368 : #if OSL_DEBUG_LEVEL > 1
369 : if (debug)
370 : dumpadef("expand entry", tokenp);
371 : #endif
372 : /*
373 : * If no macro is pending, save the name of this macro
374 : * for an eventual error message.
375 : */
376 78570 : if (recursion++ == 0)
377 20103 : macro = tokenp;
378 58467 : else if (recursion == RECURSION_LIMIT)
379 : {
380 0 : cerror("Recursive macro definition of \"%s\"", tokenp->name);
381 0 : fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
382 0 : if (rec_recover)
383 : {
384 : do
385 : {
386 0 : c = get();
387 : }
388 0 : while (infile != NULL && infile->fp == NULL);
389 0 : unget();
390 0 : recursion = 0;
391 0 : return;
392 : }
393 : }
394 : /*
395 : * Here's a macro to expand.
396 : */
397 78570 : nargs = 0; /* Formals counter */
398 78570 : parmp = parm; /* Setup parm buffer */
399 78570 : switch (tokenp->nargs)
400 : {
401 : case (-2): /* __LINE__ */
402 0 : sprintf(work, "%d", line);
403 0 : ungetstring(work);
404 0 : break;
405 :
406 : case (-3): /* __FILE__ */
407 0 : for (file = infile; file != NULL; file = file->parent)
408 : {
409 0 : if (file->fp != NULL)
410 : {
411 0 : sprintf(work, "\"%s\"", (file->progname != NULL)
412 : ? file->progname : file->filename);
413 0 : ungetstring(work);
414 0 : break;
415 : }
416 : }
417 0 : break;
418 :
419 : default:
420 : /*
421 : * Nothing funny about this macro.
422 : */
423 516 : if (tokenp->nargs < 0)
424 0 : cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
425 1032 : while ((c = skipws()) == '\n') /* Look for (, skipping */
426 0 : wrongline = TRUE; /* spaces and newlines */
427 516 : if (c != '(')
428 : {
429 : /*
430 : * If the programmer writes
431 : * #define foo() ...
432 : * ...
433 : * foo [no ()]
434 : * just write foo to the output stream.
435 : */
436 0 : unget();
437 0 : cwarn("Macro \"%s\" needs arguments", tokenp->name);
438 0 : fputs(tokenp->name, pCppOut );
439 0 : return;
440 : }
441 516 : else if (expcollect()) /* Collect arguments */
442 : {
443 516 : if (tokenp->nargs != nargs) /* Should be an error? */
444 : {
445 0 : cwarn("Wrong number of macro arguments for \"%s\"",
446 0 : tokenp->name);
447 : }
448 : #if OSL_DEBUG_LEVEL > 1
449 : if (debug)
450 : dumpparm("expand");
451 : #endif
452 : } /* Collect arguments */
453 : case DEF_NOARGS: /* No parameters just stuffs */
454 78570 : expstuff(tokenp); /* Do actual parameters */
455 : } /* nargs switch */
456 : }
457 :
458 : /*
459 : * Collect the actual parameters for this macro. TRUE if ok.
460 : */
461 1034 : FILE_LOCAL int expcollect()
462 : {
463 : int c;
464 : int paren; /* For embedded ()'s */
465 : for (;;)
466 : {
467 1034 : paren = 0; /* Collect next arg. */
468 2068 : while ((c = skipws()) == '\n') /* Skip over whitespace */
469 0 : wrongline = TRUE; /* and newlines. */
470 1034 : if (c == ')') /* At end of all args? */
471 : {
472 : /*
473 : * Note that there is a guard byte in parm[]
474 : * so we don't have to check for overflow here.
475 : */
476 516 : *parmp = EOS; /* Make sure terminated */
477 516 : break; /* Exit collection loop */
478 : }
479 518 : else if (nargs >= LASTPARM)
480 0 : cfatal("Too many arguments in macro expansion", NULLST);
481 518 : parlist[nargs++] = parmp; /* At start of new arg */
482 8153 : for (;; c = cget()) /* Collect arg's bytes */
483 : {
484 8671 : if (c == EOF_CHAR)
485 : {
486 0 : cerror("end of file within macro argument", NULLST);
487 0 : return FALSE; /* Sorry. */
488 : }
489 8671 : else if (c == '\\') /* Quote next character */
490 : {
491 0 : charput(c); /* Save the \ for later */
492 0 : charput(cget()); /* Save the next char. */
493 0 : continue; /* And go get another */
494 : }
495 8671 : else if (type[c] == QUO) /* Start of string? */
496 : {
497 0 : scanstring(c, charput); /* Scan it off */
498 0 : continue; /* Go get next char */
499 : }
500 8671 : else if (c == '(') /* Worry about balance */
501 0 : paren++; /* To know about commas */
502 8671 : else if (c == ')') /* Other side too */
503 : {
504 482 : if (paren == 0) /* At the end? */
505 : {
506 482 : unget(); /* Look at it later */
507 482 : break; /* Exit arg getter. */
508 : }
509 0 : paren--; /* More to come. */
510 : }
511 8189 : else if (c == ',' && paren == 0) /* Comma delimits args */
512 : break;
513 8153 : else if (c == '\n') /* Newline inside arg? */
514 0 : wrongline = TRUE; /* We'll need a #line */
515 8153 : charput(c); /* Store this one */
516 8153 : } /* Collect an argument */
517 518 : charput(EOS); /* Terminate argument */
518 : #if OSL_DEBUG_LEVEL > 1
519 : if (debug)
520 : fprintf( pCppOut, "parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
521 : #endif
522 518 : } /* Collect all args. */
523 516 : return TRUE; /* Normal return */
524 : }
525 :
526 : /*
527 : * Stuff the macro body, replacing formal parameters by actual parameters.
528 : */
529 78570 : FILE_LOCAL void expstuff(DEFBUF* tokenp)
530 : {
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 */
538 :
539 78570 : file = getfile(NBUFF, tokenp->name);
540 78570 : inp = tokenp->repl; /* -> macro replacement */
541 78570 : defp = file->buffer; /* -> output buffer */
542 78570 : defend = defp + (NBUFF - 1); /* Note its end */
543 78570 : if (inp != NULL)
544 : {
545 1684580 : while ((c = (*inp++ & 0xFF)) != EOS)
546 : {
547 : #ifdef SOLAR
548 1527440 : if (c == DEL)
549 : {
550 518 : c = (*inp++ & 0xFF);
551 : #else
552 : if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC))
553 : {
554 : #endif
555 518 : string_magic = (c == (MAC_PARM + PAR_MAC));
556 518 : if (string_magic)
557 0 : c = (*inp++ & 0xFF);
558 : /*
559 : * Replace formal parameter by actual parameter string.
560 : */
561 518 : if ((c -= MAC_PARM) < nargs)
562 : {
563 518 : size = strlen(parlist[c]);
564 518 : if ((defp + size) >= defend)
565 0 : goto nospace;
566 : /*
567 : * Erase the extra set of quotes.
568 : */
569 518 : if (string_magic && defp[-1] == parlist[c][0])
570 : {
571 0 : strcpy(defp-1, parlist[c]);
572 0 : defp += (size - 2);
573 : }
574 : else
575 : {
576 518 : strcpy(defp, parlist[c]);
577 518 : defp += size;
578 : }
579 : }
580 : }
581 1526922 : else if (defp >= defend)
582 : {
583 : nospace:
584 0 : cfatal("Out of space in macro \"%s\" arg expansion",
585 0 : tokenp->name);
586 : }
587 : else
588 : {
589 1526922 : *defp++ = (char)c;
590 : }
591 : }
592 : }
593 78570 : *defp = EOS;
594 : #if OSL_DEBUG_LEVEL > 1
595 : if (debug > 1)
596 : fprintf( pCppOut, "macroline: \"%s\"\n", file->buffer);
597 : #endif
598 78570 : }
599 :
600 : #if OSL_DEBUG_LEVEL > 1
601 :
602 : /*
603 : * Dump parameter list.
604 : */
605 : void dumpparm(char* why)
606 : {
607 : int i;
608 :
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++)
612 : {
613 : fprintf( pCppOut, "parm[%d] (%d) = \"%s\"\n",
614 : i + 1, (int)strlen(parlist[i]), parlist[i]);
615 : }
616 : }
617 : #endif
618 :
619 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|