update credits
[LibreOffice.git] / rsc / source / rscpp / cpp4.c
blobf82faacc9b56e2b41df9153236b5dbc927872b0f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
21 #include <stdio.h>
22 #include <ctype.h>
23 #include "cppdef.h"
24 #include "cpp.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 */
34 void InitCpp4()
36 int i;
37 for( i = 0; i < NPARMWORK; i++ )
38 parm[ i ] = 0;
39 for( i = 0; i < LASTPARM; i++ )
40 parlist[ i ] = NULL;
42 nargs = 0;
46 void dodefine()
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.
56 * There is some special case code to distinguish
57 * #define foo bar
58 * from #define foo() bar
60 * Also, we make sure that
61 * #define foo foo
62 * expands to "foo" but doesn't put cpp into an infinite loop.
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.
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().
85 register int c;
86 register DEFBUF *dp; /* -> new definition */
87 int isredefine; /* TRUE if redefined */
88 char *old = 0; /* Remember redefined */
90 if (type[(c = skipws())] != LET)
91 goto bad_define;
92 isredefine = FALSE; /* Set if redefining */
93 if ((dp = lookid(c)) == NULL) /* If not known now */
94 dp = defendel(token, FALSE); /* Save the name */
95 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? */
102 nargs = 0; /* Init formals counter */
103 do { /* Collect formal parms */
104 if (nargs >= LASTPARM)
105 cfatal("Too many arguments for macro", NULLST);
106 else if ((c = skipws()) == ')')
107 break; /* Got them all */
108 else if (type[c] != LET) /* Bad formal syntax */
109 goto bad_define;
110 scanid(c); /* Get the formal param */
111 parlist[nargs++] = parmp; /* Save its start */
112 textput(token); /* Save text in parm[] */
113 } while ((c = skipws()) == ','); /* Get another argument */
114 if (c != ')') /* Must end at ) */
115 goto bad_define;
116 c = ' '; /* Will skip to body */
118 else {
120 * DEF_NOARGS is needed to distinguish between
121 * "#define foo" and "#define foo()".
123 nargs = DEF_NOARGS; /* No () parameters */
125 if (type[c] == SPA) /* At whitespace? */
126 c = skipws(); /* Not any more. */
127 workp = work; /* Replacement put here */
128 inmacro = TRUE; /* Keep \<newline> now */
129 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 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 #endif
142 if (type[c] == LET) /* Another token here? */
143 ; /* Stuff it normally */
144 else if (type[c] == DIG) { /* Digit string after? */
145 while (type[c] == DIG) { /* Stuff the digits */
146 save(c);
147 c = get();
149 save(TOK_SEP); /* Delimit 2nd token */
151 else {
152 #if ! COMMENT_INVISIBLE
153 ciwarn("Strange character after # (%d.)", c);
154 #endif
156 continue;
158 #endif
159 switch (type[c]) {
160 case LET:
161 checkparm(c, dp); /* Might be a formal */
162 break;
164 case DIG: /* Number in mac. body */
165 case DOT: /* Maybe a float number */
166 scannumber(c, save); /* Scan it off */
167 break;
169 case QUO: /* String in mac. body */
170 #if STRING_FORMAL
171 stparmscan(c, dp); /* Do string magic */
172 #else
173 stparmscan(c);
174 #endif
175 break;
177 case BSH: /* Backslash */
178 save('\\');
179 if ((c = get()) == '\n')
180 wrongline = TRUE;
181 save(c);
182 break;
184 case SPA: /* Absorb whitespace */
186 * Note: the "end of comment" marker is passed on
187 * to allow comments to separate tokens.
189 if (workp[-1] == ' ') /* Absorb multiple */
190 break; /* spaces */
191 else if (c == '\t')
192 c = ' '; /* Normalize tabs */
193 /* Fall through to store character */
194 default: /* Other character */
195 save(c);
196 break;
198 c = get();
200 inmacro = FALSE; /* Stop newline hack */
201 unget(); /* For control check */
202 if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
203 workp--;
204 *workp = EOS; /* Terminate work */
205 dp->repl = savestring(work); /* Save the string */
206 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 if (isredefine) { /* Error if redefined */
214 if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
215 || (old == NULL && dp->repl != NULL)
216 || (old != NULL && dp->repl == NULL)) {
217 #ifdef STRICT_UNDEF
218 cerror("Redefining defined variable \"%s\"", dp->name);
219 #else
220 cwarn("Redefining defined variable \"%s\"", dp->name);
221 #endif
223 if (old != NULL) /* We don't need the */
224 free(old); /* old definition now. */
226 return;
228 bad_define:
229 cerror("#define syntax error", NULLST);
230 inmacro = FALSE; /* Stop <newline> hack */
233 void checkparm(int c, DEFBUF* dp)
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".
242 register int i;
243 register char *cp;
245 scanid(c); /* Get parm to token[] */
246 for (i = 0; i < nargs; i++) { /* For each argument */
247 if (streq(parlist[i], token)) { /* If it's known */
248 #ifdef SOLAR
249 save(DEL);
250 #endif
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 */
261 #if STRING_FORMAL
262 void stparmscan(delim, dp)
263 int delim;
264 register DEFBUF *dp;
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.
272 register int c;
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.
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());
293 instring = FALSE;
294 if (c != delim)
295 cerror("Unterminated string in macro body", NULLST);
296 save(c);
298 #else
299 void stparmscan(int delim)
301 * Normal string parameter scan.
304 register char *wp;
305 register int i;
307 wp = workp; /* Here's where it starts */
308 if (!scanstring(delim, save))
309 return; /* Exit on scanstring error */
310 workp[-1] = EOS; /* Erase trailing quote */
311 wp++; /* -> first string content byte */
312 for (i = 0; i < nargs; i++) {
313 if (streq(parlist[i], wp)) {
314 #ifdef SOLAR
315 *wp++ = DEL;
316 *wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
317 *wp++ = (char)(i + MAC_PARM); /* Make a formal marker */
318 *wp = wp[-4]; /* Add on closing quote */
319 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 return;
329 workp[-1] = wp[-1]; /* Nope, reset end quote. */
331 #endif
333 void doundef()
335 * Remove the symbol from the defined list.
336 * Called from the #control processor.
339 register int c;
341 if (type[(c = skipws())] != LET)
342 cerror("Illegal #undef argument", NULLST);
343 else {
344 scanid(c); /* Get name to token[] */
345 if (defendel(token, TRUE) == NULL) {
346 #ifdef STRICT_UNDEF
347 cwarn("Symbol \"%s\" not defined in #undef", token);
348 #endif
353 void textput(char* text)
355 * Put the string in the parm[] buffer.
358 register int size;
360 size = strlen(text) + 1;
361 if ((parmp + size) >= &parm[NPARMWORK])
362 cfatal("Macro work area overflow", NULLST);
363 else {
364 strcpy(parmp, text);
365 parmp += size;
369 void charput(int c)
371 * Put the byte in the parm[] buffer.
374 if (parmp >= &parm[NPARMWORK])
375 cfatal("Macro work area overflow", NULLST);
376 else {
377 *parmp++ = (char)c;
382 * M a c r o E x p a n s i o n
385 static DEFBUF *macro; /* Catches start of infinite macro */
387 void expand(DEFBUF* tokenp)
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.)
398 register int c;
399 register FILEINFO *file;
400 extern FILEINFO *getfile();
402 #if OSL_DEBUG_LEVEL > 1
403 if (debug)
404 dumpadef("expand entry", tokenp);
405 #endif
407 * If no macro is pending, save the name of this macro
408 * for an eventual error message.
410 if (recursion++ == 0)
411 macro = tokenp;
412 else if (recursion == RECURSION_LIMIT) {
413 cerror("Recursive macro definition of \"%s\"", tokenp->name);
414 fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
415 if (rec_recover) {
416 do {
417 c = get();
418 } while (infile != NULL && infile->fp == NULL);
419 unget();
420 recursion = 0;
421 return;
425 * Here's a macro to expand.
427 nargs = 0; /* Formals counter */
428 parmp = parm; /* Setup parm buffer */
429 switch (tokenp->nargs) {
430 case (-2): /* __LINE__ */
431 sprintf(work, "%d", line);
432 ungetstring(work);
433 break;
435 case (-3): /* __FILE__ */
436 for (file = infile; file != NULL; file = file->parent) {
437 if (file->fp != NULL) {
438 sprintf(work, "\"%s\"", (file->progname != NULL)
439 ? file->progname : file->filename);
440 ungetstring(work);
441 break;
444 break;
446 default:
448 * Nothing funny about this macro.
450 if (tokenp->nargs < 0)
451 cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
452 while ((c = skipws()) == '\n') /* Look for (, skipping */
453 wrongline = TRUE; /* spaces and newlines */
454 if (c != '(') {
456 * If the programmer writes
457 * #define foo() ...
458 * ...
459 * foo [no ()]
460 * just write foo to the output stream.
462 unget();
463 cwarn("Macro \"%s\" needs arguments", tokenp->name);
464 fputs(tokenp->name, pCppOut );
465 return;
467 else if (expcollect()) { /* Collect arguments */
468 if (tokenp->nargs != nargs) { /* Should be an error? */
469 cwarn("Wrong number of macro arguments for \"%s\"",
470 tokenp->name);
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 expstuff(tokenp); /* Do actual parameters */
479 } /* nargs switch */
482 FILE_LOCAL int
483 expcollect()
485 * Collect the actual parameters for this macro. TRUE if ok.
488 register int c;
489 register int paren; /* For embedded ()'s */
490 for (;;) {
491 paren = 0; /* Collect next arg. */
492 while ((c = skipws()) == '\n') /* Skip over whitespace */
493 wrongline = TRUE; /* and newlines. */
494 if (c == ')') { /* At end of all args? */
496 * Note that there is a guard byte in parm[]
497 * so we don't have to check for overflow here.
499 *parmp = EOS; /* Make sure terminated */
500 break; /* Exit collection loop */
502 else if (nargs >= LASTPARM)
503 cfatal("Too many arguments in macro expansion", NULLST);
504 parlist[nargs++] = parmp; /* At start of new arg */
505 for (;; c = cget()) { /* Collect arg's bytes */
506 if (c == EOF_CHAR) {
507 cerror("end of file within macro argument", NULLST);
508 return (FALSE); /* Sorry. */
510 else if (c == '\\') { /* Quote next character */
511 charput(c); /* Save the \ for later */
512 charput(cget()); /* Save the next char. */
513 continue; /* And go get another */
515 else if (type[c] == QUO) { /* Start of string? */
516 scanstring(c, charput); /* Scan it off */
517 continue; /* Go get next char */
519 else if (c == '(') /* Worry about balance */
520 paren++; /* To know about commas */
521 else if (c == ')') { /* Other side too */
522 if (paren == 0) { /* At the end? */
523 unget(); /* Look at it later */
524 break; /* Exit arg getter. */
526 paren--; /* More to come. */
528 else if (c == ',' && paren == 0) /* Comma delimits args */
529 break;
530 else if (c == '\n') /* Newline inside arg? */
531 wrongline = TRUE; /* We'll need a #line */
532 charput(c); /* Store this one */
533 } /* Collect an argument */
534 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 } /* Collect all args. */
540 return (TRUE); /* Normal return */
543 FILE_LOCAL
544 void expstuff(DEFBUF* tokenp)
546 * Stuff the macro body, replacing formal parameters by actual parameters.
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();
558 file = getfile(NBUFF, tokenp->name);
559 inp = tokenp->repl; /* -> macro replacement */
560 defp = file->buffer; /* -> output buffer */
561 defend = defp + (NBUFF - 1); /* Note its end */
562 if (inp != NULL) {
563 while ((c = (*inp++ & 0xFF)) != EOS) {
564 #ifdef SOLAR
565 if (c == DEL) {
566 c = (*inp++ & 0xFF);
567 #else
568 if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
569 #endif
570 string_magic = (c == (MAC_PARM + PAR_MAC));
571 if (string_magic)
572 c = (*inp++ & 0xFF);
574 * Replace formal parameter by actual parameter string.
576 if ((c -= MAC_PARM) < nargs) {
577 size = strlen(parlist[c]);
578 if ((defp + size) >= defend)
579 goto nospace;
581 * Erase the extra set of quotes.
583 if (string_magic && defp[-1] == parlist[c][0]) {
584 strcpy(defp-1, parlist[c]);
585 defp += (size - 2);
587 else {
588 strcpy(defp, parlist[c]);
589 defp += size;
593 else if (defp >= defend) {
594 nospace: cfatal("Out of space in macro \"%s\" arg expansion",
595 tokenp->name);
597 else {
598 *defp++ = (char)c;
602 *defp = EOS;
603 #if OSL_DEBUG_LEVEL > 1
604 if (debug > 1)
605 fprintf( pCppOut, "macroline: \"%s\"\n", file->buffer);
606 #endif
609 #if OSL_DEBUG_LEVEL > 1
610 void dumpparm(char* why)
612 * Dump parameter list.
615 register int i;
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]);
624 #endif
626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */