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