forked from amazingfate/loongoffice
627 lines
24 KiB
C
627 lines
24 KiB
C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <sal/types.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include "cppdef.h"
|
|
#include "cpp.h"
|
|
/*
|
|
* parm[], parmp, and parlist[] are used to store #define() argument
|
|
* lists. nargs contains the actual number of parameters stored.
|
|
*/
|
|
static char parm[NPARMWORK + 1]; /* define param work buffer */
|
|
static char *parmp; /* Free space in parm */
|
|
static char *parlist[LASTPARM]; /* -> start of each parameter */
|
|
static int nargs; /* Parameters for this macro */
|
|
|
|
void InitCpp4()
|
|
{
|
|
int i;
|
|
for( i = 0; i < NPARMWORK; i++ )
|
|
parm[ i ] = 0;
|
|
for( i = 0; i < LASTPARM; i++ )
|
|
parlist[ i ] = NULL;
|
|
|
|
nargs = 0;
|
|
}
|
|
|
|
|
|
void dodefine()
|
|
/*
|
|
* Called from control when a #define is scanned. This module
|
|
* parses formal parameters and the replacement string. When
|
|
* the formal parameter name is encountered in the replacement
|
|
* string, it is replaced by a character in the range 128 to
|
|
* 128+NPARAM (this allows up to 32 parameters within the
|
|
* Dec Multinational range). If cpp is ported to an EBCDIC
|
|
* machine, you will have to make other arrangements.
|
|
*
|
|
* There is some special case code to distinguish
|
|
* #define foo bar
|
|
* from #define foo() bar
|
|
*
|
|
* Also, we make sure that
|
|
* #define foo foo
|
|
* expands to "foo" but doesn't put cpp into an infinite loop.
|
|
*
|
|
* A warning message is printed if you redefine a symbol to a
|
|
* different text. I.e,
|
|
* #define foo 123
|
|
* #define foo 123
|
|
* is ok, but
|
|
* #define foo 123
|
|
* #define foo +123
|
|
* is not.
|
|
*
|
|
* The following subroutines are called from define():
|
|
* checkparm called when a token is scanned. It checks through the
|
|
* array of formal parameters. If a match is found, the
|
|
* token is replaced by a control byte which will be used
|
|
* to locate the parameter when the macro is expanded.
|
|
* textput puts a string in the macro work area (parm[]), updating
|
|
* parmp to point to the first free byte in parm[].
|
|
* textput() tests for work buffer overflow.
|
|
* charput puts a single character in the macro work area (parm[])
|
|
* in a manner analogous to textput().
|
|
*/
|
|
{
|
|
register int c;
|
|
register DEFBUF *dp; /* -> new definition */
|
|
int isredefine; /* TRUE if redefined */
|
|
char *old = 0; /* Remember redefined */
|
|
|
|
if (type[(c = skipws())] != LET)
|
|
goto bad_define;
|
|
isredefine = FALSE; /* Set if redefining */
|
|
if ((dp = lookid(c)) == NULL) /* If not known now */
|
|
dp = defendel(token, FALSE); /* Save the name */
|
|
else { /* It's known: */
|
|
isredefine = TRUE; /* Remember this fact */
|
|
old = dp->repl; /* Remember replacement */
|
|
dp->repl = NULL; /* No replacement now */
|
|
}
|
|
parlist[0] = parmp = parm; /* Setup parm buffer */
|
|
if ((c = get()) == '(') { /* With arguments? */
|
|
nargs = 0; /* Init formals counter */
|
|
do { /* Collect formal parms */
|
|
if (nargs >= LASTPARM)
|
|
cfatal("Too many arguments for macro", NULLST);
|
|
else if ((c = skipws()) == ')')
|
|
break; /* Got them all */
|
|
else if (type[c] != LET) /* Bad formal syntax */
|
|
goto bad_define;
|
|
scanid(c); /* Get the formal param */
|
|
parlist[nargs++] = parmp; /* Save its start */
|
|
textput(token); /* Save text in parm[] */
|
|
} while ((c = skipws()) == ','); /* Get another argument */
|
|
if (c != ')') /* Must end at ) */
|
|
goto bad_define;
|
|
c = ' '; /* Will skip to body */
|
|
}
|
|
else {
|
|
/*
|
|
* DEF_NOARGS is needed to distinguish between
|
|
* "#define foo" and "#define foo()".
|
|
*/
|
|
nargs = DEF_NOARGS; /* No () parameters */
|
|
}
|
|
if (type[c] == SPA) /* At whitespace? */
|
|
c = skipws(); /* Not any more. */
|
|
workp = work; /* Replacement put here */
|
|
inmacro = TRUE; /* Keep \<newline> now */
|
|
while (c != EOF_CHAR && c != '\n') { /* Compile macro body */
|
|
#if OK_CONCAT
|
|
#if COMMENT_INVISIBLE
|
|
if (c == COM_SEP) { /* Token concatenation? */
|
|
save(TOK_SEP); /* Stuff a delimiter */
|
|
c = get();
|
|
#else
|
|
if (c == '#') { /* Token concatenation? */
|
|
while (workp > work && type[(int)workp[-1]] == SPA)
|
|
--workp; /* Erase leading spaces */
|
|
save(TOK_SEP); /* Stuff a delimiter */
|
|
c = skipws(); /* Eat whitespace */
|
|
#endif
|
|
if (type[c] == LET) /* Another token here? */
|
|
; /* Stuff it normally */
|
|
else if (type[c] == DIG) { /* Digit string after? */
|
|
while (type[c] == DIG) { /* Stuff the digits */
|
|
save(c);
|
|
c = get();
|
|
}
|
|
save(TOK_SEP); /* Delimit 2nd token */
|
|
}
|
|
else {
|
|
#if ! COMMENT_INVISIBLE
|
|
ciwarn("Strange character after # (%d.)", c);
|
|
#endif
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
switch (type[c]) {
|
|
case LET:
|
|
checkparm(c, dp); /* Might be a formal */
|
|
break;
|
|
|
|
case DIG: /* Number in mac. body */
|
|
case DOT: /* Maybe a float number */
|
|
scannumber(c, save); /* Scan it off */
|
|
break;
|
|
|
|
case QUO: /* String in mac. body */
|
|
#if STRING_FORMAL
|
|
stparmscan(c, dp); /* Do string magic */
|
|
#else
|
|
stparmscan(c);
|
|
#endif
|
|
break;
|
|
|
|
case BSH: /* Backslash */
|
|
save('\\');
|
|
if ((c = get()) == '\n')
|
|
wrongline = TRUE;
|
|
save(c);
|
|
break;
|
|
|
|
case SPA: /* Absorb whitespace */
|
|
/*
|
|
* Note: the "end of comment" marker is passed on
|
|
* to allow comments to separate tokens.
|
|
*/
|
|
if (workp[-1] == ' ') /* Absorb multiple */
|
|
break; /* spaces */
|
|
else if (c == '\t')
|
|
c = ' '; /* Normalize tabs */
|
|
/* Fall through to store character */
|
|
default: /* Other character */
|
|
save(c);
|
|
break;
|
|
}
|
|
c = get();
|
|
}
|
|
inmacro = FALSE; /* Stop newline hack */
|
|
unget(); /* For control check */
|
|
if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
|
|
workp--;
|
|
*workp = EOS; /* Terminate work */
|
|
dp->repl = savestring(work); /* Save the string */
|
|
dp->nargs = nargs; /* Save arg count */
|
|
#if OSL_DEBUG_LEVEL > 1
|
|
if (debug)
|
|
dumpadef("macro definition", dp);
|
|
else if (bDumpDefs)
|
|
dumpadef(NULL, dp);
|
|
#endif
|
|
if (isredefine) { /* Error if redefined */
|
|
if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
|
|
|| (old == NULL && dp->repl != NULL)
|
|
|| (old != NULL && dp->repl == NULL)) {
|
|
#ifdef STRICT_UNDEF
|
|
cerror("Redefining defined variable \"%s\"", dp->name);
|
|
#else
|
|
cwarn("Redefining defined variable \"%s\"", dp->name);
|
|
#endif
|
|
}
|
|
if (old != NULL) /* We don't need the */
|
|
free(old); /* old definition now. */
|
|
}
|
|
return;
|
|
|
|
bad_define:
|
|
cerror("#define syntax error", NULLST);
|
|
inmacro = FALSE; /* Stop <newline> hack */
|
|
}
|
|
|
|
void checkparm(int c, DEFBUF* dp)
|
|
/*
|
|
* Replace this param if it's defined. Note that the macro name is a
|
|
* possible replacement token. We stuff DEF_MAGIC in front of the token
|
|
* which is treated as a LETTER by the token scanner and eaten by
|
|
* the output routine. This prevents the macro expander from
|
|
* looping if someone writes "#define foo foo".
|
|
*/
|
|
{
|
|
register int i;
|
|
register char *cp;
|
|
|
|
scanid(c); /* Get parm to token[] */
|
|
for (i = 0; i < nargs; i++) { /* For each argument */
|
|
if (streq(parlist[i], token)) { /* If it's known */
|
|
#ifdef SOLAR
|
|
save(DEL);
|
|
#endif
|
|
save(i + MAC_PARM); /* Save a magic cookie */
|
|
return; /* And exit the search */
|
|
}
|
|
}
|
|
if (streq(dp->name, token)) /* Macro name in body? */
|
|
save(DEF_MAGIC); /* Save magic marker */
|
|
for (cp = token; *cp != EOS;) /* And save */
|
|
save(*cp++); /* The token itself */
|
|
}
|
|
|
|
#if STRING_FORMAL
|
|
void stparmscan(delim, dp)
|
|
int delim;
|
|
register DEFBUF *dp;
|
|
/*
|
|
* Scan the string (starting with the given delimiter).
|
|
* The token is replaced if it is the only text in this string or
|
|
* character constant. The algorithm follows checkparm() above.
|
|
* Note that scanstring() has approved of the string.
|
|
*/
|
|
{
|
|
register int c;
|
|
|
|
/*
|
|
* Warning -- this code hasn't been tested for a while.
|
|
* It exists only to preserve compatibility with earlier
|
|
* implementations of cpp. It is not part of the Draft
|
|
* ANSI Standard C language.
|
|
*/
|
|
save(delim);
|
|
instring = TRUE;
|
|
while ((c = get()) != delim
|
|
&& c != '\n'
|
|
&& c != EOF_CHAR) {
|
|
if (type[c] == LET) /* Maybe formal parm */
|
|
checkparm(c, dp);
|
|
else {
|
|
save(c);
|
|
if (c == '\\')
|
|
save(get());
|
|
}
|
|
}
|
|
instring = FALSE;
|
|
if (c != delim)
|
|
cerror("Unterminated string in macro body", NULLST);
|
|
save(c);
|
|
}
|
|
#else
|
|
void stparmscan(int delim)
|
|
/*
|
|
* Normal string parameter scan.
|
|
*/
|
|
{
|
|
register char *wp;
|
|
register int i;
|
|
|
|
wp = workp; /* Here's where it starts */
|
|
if (!scanstring(delim, save))
|
|
return; /* Exit on scanstring error */
|
|
workp[-1] = EOS; /* Erase trailing quote */
|
|
wp++; /* -> first string content byte */
|
|
for (i = 0; i < nargs; i++) {
|
|
if (streq(parlist[i], wp)) {
|
|
#ifdef SOLAR
|
|
*wp++ = DEL;
|
|
*wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
|
|
*wp++ = (char)(i + MAC_PARM); /* Make a formal marker */
|
|
*wp = wp[-4]; /* Add on closing quote */
|
|
workp = wp + 1; /* Reset string end */
|
|
#else
|
|
*wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
|
|
*wp++ = (i + MAC_PARM); /* Make a formal marker */
|
|
*wp = wp[-3]; /* Add on closing quote */
|
|
workp = wp + 1; /* Reset string end */
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
workp[-1] = wp[-1]; /* Nope, reset end quote. */
|
|
}
|
|
#endif
|
|
|
|
void doundef()
|
|
/*
|
|
* Remove the symbol from the defined list.
|
|
* Called from the #control processor.
|
|
*/
|
|
{
|
|
register int c;
|
|
|
|
if (type[(c = skipws())] != LET)
|
|
cerror("Illegal #undef argument", NULLST);
|
|
else {
|
|
scanid(c); /* Get name to token[] */
|
|
if (defendel(token, TRUE) == NULL) {
|
|
#ifdef STRICT_UNDEF
|
|
cwarn("Symbol \"%s\" not defined in #undef", token);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void textput(char* text)
|
|
/*
|
|
* Put the string in the parm[] buffer.
|
|
*/
|
|
{
|
|
register int size;
|
|
|
|
size = strlen(text) + 1;
|
|
if ((parmp + size) >= &parm[NPARMWORK])
|
|
cfatal("Macro work area overflow", NULLST);
|
|
else {
|
|
strcpy(parmp, text);
|
|
parmp += size;
|
|
}
|
|
}
|
|
|
|
void charput(int c)
|
|
/*
|
|
* Put the byte in the parm[] buffer.
|
|
*/
|
|
{
|
|
if (parmp >= &parm[NPARMWORK])
|
|
cfatal("Macro work area overflow", NULLST);
|
|
else {
|
|
*parmp++ = (char)c;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* M a c r o E x p a n s i o n
|
|
*/
|
|
|
|
static DEFBUF *macro; /* Catches start of infinite macro */
|
|
|
|
void expand(DEFBUF* tokenp)
|
|
/*
|
|
* Expand a macro. Called from the cpp mainline routine (via subroutine
|
|
* macroid()) when a token is found in the symbol table. It calls
|
|
* expcollect() to parse actual parameters, checking for the correct number.
|
|
* It then creates a "file" containing a single line containing the
|
|
* macro with actual parameters inserted appropriately. This is
|
|
* "pushed back" onto the input stream. (When the get() routine runs
|
|
* off the end of the macro line, it will dismiss the macro itself.)
|
|
*/
|
|
{
|
|
register int c;
|
|
register FILEINFO *file;
|
|
extern FILEINFO *getfile();
|
|
|
|
#if OSL_DEBUG_LEVEL > 1
|
|
if (debug)
|
|
dumpadef("expand entry", tokenp);
|
|
#endif
|
|
/*
|
|
* If no macro is pending, save the name of this macro
|
|
* for an eventual error message.
|
|
*/
|
|
if (recursion++ == 0)
|
|
macro = tokenp;
|
|
else if (recursion == RECURSION_LIMIT) {
|
|
cerror("Recursive macro definition of \"%s\"", tokenp->name);
|
|
fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
|
|
if (rec_recover) {
|
|
do {
|
|
c = get();
|
|
} while (infile != NULL && infile->fp == NULL);
|
|
unget();
|
|
recursion = 0;
|
|
return;
|
|
}
|
|
}
|
|
/*
|
|
* Here's a macro to expand.
|
|
*/
|
|
nargs = 0; /* Formals counter */
|
|
parmp = parm; /* Setup parm buffer */
|
|
switch (tokenp->nargs) {
|
|
case (-2): /* __LINE__ */
|
|
sprintf(work, "%d", line);
|
|
ungetstring(work);
|
|
break;
|
|
|
|
case (-3): /* __FILE__ */
|
|
for (file = infile; file != NULL; file = file->parent) {
|
|
if (file->fp != NULL) {
|
|
sprintf(work, "\"%s\"", (file->progname != NULL)
|
|
? file->progname : file->filename);
|
|
ungetstring(work);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* Nothing funny about this macro.
|
|
*/
|
|
if (tokenp->nargs < 0)
|
|
cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
|
|
while ((c = skipws()) == '\n') /* Look for (, skipping */
|
|
wrongline = TRUE; /* spaces and newlines */
|
|
if (c != '(') {
|
|
/*
|
|
* If the programmer writes
|
|
* #define foo() ...
|
|
* ...
|
|
* foo [no ()]
|
|
* just write foo to the output stream.
|
|
*/
|
|
unget();
|
|
cwarn("Macro \"%s\" needs arguments", tokenp->name);
|
|
fputs(tokenp->name, pCppOut );
|
|
return;
|
|
}
|
|
else if (expcollect()) { /* Collect arguments */
|
|
if (tokenp->nargs != nargs) { /* Should be an error? */
|
|
cwarn("Wrong number of macro arguments for \"%s\"",
|
|
tokenp->name);
|
|
}
|
|
#if OSL_DEBUG_LEVEL > 1
|
|
if (debug)
|
|
dumpparm("expand");
|
|
#endif
|
|
} /* Collect arguments */
|
|
case DEF_NOARGS: /* No parameters just stuffs */
|
|
expstuff(tokenp); /* Do actual parameters */
|
|
} /* nargs switch */
|
|
}
|
|
|
|
FILE_LOCAL int
|
|
expcollect()
|
|
/*
|
|
* Collect the actual parameters for this macro. TRUE if ok.
|
|
*/
|
|
{
|
|
register int c;
|
|
register int paren; /* For embedded ()'s */
|
|
for (;;) {
|
|
paren = 0; /* Collect next arg. */
|
|
while ((c = skipws()) == '\n') /* Skip over whitespace */
|
|
wrongline = TRUE; /* and newlines. */
|
|
if (c == ')') { /* At end of all args? */
|
|
/*
|
|
* Note that there is a guard byte in parm[]
|
|
* so we don't have to check for overflow here.
|
|
*/
|
|
*parmp = EOS; /* Make sure terminated */
|
|
break; /* Exit collection loop */
|
|
}
|
|
else if (nargs >= LASTPARM)
|
|
cfatal("Too many arguments in macro expansion", NULLST);
|
|
parlist[nargs++] = parmp; /* At start of new arg */
|
|
for (;; c = cget()) { /* Collect arg's bytes */
|
|
if (c == EOF_CHAR) {
|
|
cerror("end of file within macro argument", NULLST);
|
|
return (FALSE); /* Sorry. */
|
|
}
|
|
else if (c == '\\') { /* Quote next character */
|
|
charput(c); /* Save the \ for later */
|
|
charput(cget()); /* Save the next char. */
|
|
continue; /* And go get another */
|
|
}
|
|
else if (type[c] == QUO) { /* Start of string? */
|
|
scanstring(c, charput); /* Scan it off */
|
|
continue; /* Go get next char */
|
|
}
|
|
else if (c == '(') /* Worry about balance */
|
|
paren++; /* To know about commas */
|
|
else if (c == ')') { /* Other side too */
|
|
if (paren == 0) { /* At the end? */
|
|
unget(); /* Look at it later */
|
|
break; /* Exit arg getter. */
|
|
}
|
|
paren--; /* More to come. */
|
|
}
|
|
else if (c == ',' && paren == 0) /* Comma delimits args */
|
|
break;
|
|
else if (c == '\n') /* Newline inside arg? */
|
|
wrongline = TRUE; /* We'll need a #line */
|
|
charput(c); /* Store this one */
|
|
} /* Collect an argument */
|
|
charput(EOS); /* Terminate argument */
|
|
#if OSL_DEBUG_LEVEL > 1
|
|
if (debug)
|
|
fprintf( pCppOut, "parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
|
|
#endif
|
|
} /* Collect all args. */
|
|
return (TRUE); /* Normal return */
|
|
}
|
|
|
|
FILE_LOCAL
|
|
void expstuff(DEFBUF* tokenp)
|
|
/*
|
|
* Stuff the macro body, replacing formal parameters by actual parameters.
|
|
*/
|
|
{
|
|
register int c; /* Current character */
|
|
register char *inp; /* -> repl string */
|
|
register char *defp; /* -> macro output buff */
|
|
int size; /* Actual parm. size */
|
|
char *defend; /* -> output buff end */
|
|
int string_magic; /* String formal hack */
|
|
FILEINFO *file; /* Funny #include */
|
|
extern FILEINFO *getfile();
|
|
|
|
file = getfile(NBUFF, tokenp->name);
|
|
inp = tokenp->repl; /* -> macro replacement */
|
|
defp = file->buffer; /* -> output buffer */
|
|
defend = defp + (NBUFF - 1); /* Note its end */
|
|
if (inp != NULL) {
|
|
while ((c = (*inp++ & 0xFF)) != EOS) {
|
|
#ifdef SOLAR
|
|
if (c == DEL) {
|
|
c = (*inp++ & 0xFF);
|
|
#else
|
|
if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
|
|
#endif
|
|
string_magic = (c == (MAC_PARM + PAR_MAC));
|
|
if (string_magic)
|
|
c = (*inp++ & 0xFF);
|
|
/*
|
|
* Replace formal parameter by actual parameter string.
|
|
*/
|
|
if ((c -= MAC_PARM) < nargs) {
|
|
size = strlen(parlist[c]);
|
|
if ((defp + size) >= defend)
|
|
goto nospace;
|
|
/*
|
|
* Erase the extra set of quotes.
|
|
*/
|
|
if (string_magic && defp[-1] == parlist[c][0]) {
|
|
strcpy(defp-1, parlist[c]);
|
|
defp += (size - 2);
|
|
}
|
|
else {
|
|
strcpy(defp, parlist[c]);
|
|
defp += size;
|
|
}
|
|
}
|
|
}
|
|
else if (defp >= defend) {
|
|
nospace: cfatal("Out of space in macro \"%s\" arg expansion",
|
|
tokenp->name);
|
|
}
|
|
else {
|
|
*defp++ = (char)c;
|
|
}
|
|
}
|
|
}
|
|
*defp = EOS;
|
|
#if OSL_DEBUG_LEVEL > 1
|
|
if (debug > 1)
|
|
fprintf( pCppOut, "macroline: \"%s\"\n", file->buffer);
|
|
#endif
|
|
}
|
|
|
|
#if OSL_DEBUG_LEVEL > 1
|
|
void dumpparm(char* why)
|
|
/*
|
|
* Dump parameter list.
|
|
*/
|
|
{
|
|
register int i;
|
|
|
|
fprintf( pCppOut, "dump of %d parameters (%" SAL_PRI_SIZET "u bytes total) %s\n",
|
|
nargs, parmp - parm, why);
|
|
for (i = 0; i < nargs; i++) {
|
|
fprintf( pCppOut, "parm[%d] (%d) = \"%s\"\n",
|
|
i + 1, (int)strlen(parlist[i]), parlist[i]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|