mirror of https://github.com/swig/swig
2112 lines
53 KiB
C
2112 lines
53 KiB
C
/* -----------------------------------------------------------------------------
|
|
* This file is part of SWIG, which is licensed as a whole under version 3
|
|
* (or any later version) of the GNU General Public License. Some additional
|
|
* terms also apply to certain portions of SWIG. The full details of the SWIG
|
|
* license and copyrights can be found in the LICENSE and COPYRIGHT files
|
|
* included with the SWIG source code as distributed by the SWIG developers
|
|
* and at https://www.swig.org/legal.html.
|
|
*
|
|
* cpp.c
|
|
*
|
|
* An implementation of a C preprocessor plus some support for additional
|
|
* SWIG directives.
|
|
*
|
|
* - SWIG directives such as %include, %extern, and %import are handled
|
|
* - A new macro %define ... %enddef can be used for multiline macros
|
|
* - No preprocessing is performed in %{ ... %} blocks
|
|
* - Lines beginning with %# are stripped down to #... and passed through.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
#include "swig.h"
|
|
#include "preprocessor.h"
|
|
#include <ctype.h>
|
|
|
|
static Hash *cpp = 0; /* C preprocessor data */
|
|
static int include_all = 0; /* Follow all includes */
|
|
static int ignore_missing = 0;
|
|
static int import_all = 0; /* Follow all includes, but as %import statements */
|
|
static int imported_depth = 0; /* Depth of %imported files */
|
|
static int single_include = 1; /* Only include each file once */
|
|
static Hash *included_files = 0;
|
|
static List *dependencies = 0;
|
|
static Scanner *id_scan = 0;
|
|
static int error_as_warning = 0; /* Understand the cpp #error directive as a special #warning */
|
|
static int expand_defined_operator = 0;
|
|
static int macro_level = 0;
|
|
static int macro_start_line = 0;
|
|
static const String * macro_start_file = 0;
|
|
|
|
/* Test a character to see if it starts an identifier */
|
|
#define isidentifier(c) ((isalpha(c)) || (c == '_') || (c == '$'))
|
|
|
|
/* Test a character to see if it valid in an identifier (after the first letter) */
|
|
#define isidchar(c) ((isalnum(c)) || (c == '_') || (c == '$'))
|
|
|
|
static DOH *Preprocessor_replace(DOH *);
|
|
|
|
/* Skip whitespace */
|
|
static void skip_whitespace(String *s, String *out) {
|
|
int c;
|
|
while ((c = Getc(s)) != EOF) {
|
|
if (!isspace(c)) {
|
|
Ungetc(c, s);
|
|
break;
|
|
} else if (out)
|
|
Putc(c, out);
|
|
}
|
|
}
|
|
|
|
/* Skip to a specified character taking line breaks into account */
|
|
static int skip_tochar(String *s, int ch, String *out) {
|
|
int c;
|
|
while ((c = Getc(s)) != EOF) {
|
|
if (out)
|
|
Putc(c, out);
|
|
if (c == ch)
|
|
break;
|
|
if (c == '\\') {
|
|
c = Getc(s);
|
|
if ((c != EOF) && (out))
|
|
Putc(c, out);
|
|
}
|
|
}
|
|
if (c == EOF)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static void copy_location(const DOH *s1, DOH *s2) {
|
|
Setfile(s2, Getfile((DOH *) s1));
|
|
Setline(s2, Getline((DOH *) s1));
|
|
}
|
|
|
|
static String *cpp_include(const_String_or_char_ptr fn, int sysfile) {
|
|
String *s = sysfile ? Swig_include_sys(fn) : Swig_include(fn);
|
|
if (s && single_include) {
|
|
String *file = Getfile(s);
|
|
if (Getattr(included_files, file)) {
|
|
Delete(s);
|
|
return 0;
|
|
}
|
|
Setattr(included_files, file, file);
|
|
}
|
|
if (!s) {
|
|
if (ignore_missing) {
|
|
Swig_warning(WARN_PP_MISSING_FILE, Getfile(fn), Getline(fn), "Unable to find '%s'\n", fn);
|
|
} else {
|
|
Swig_error(Getfile(fn), Getline(fn), "Unable to find '%s'\n", fn);
|
|
}
|
|
} else {
|
|
String *lf;
|
|
Seek(s, 0, SEEK_SET);
|
|
if (!dependencies) {
|
|
dependencies = NewList();
|
|
}
|
|
lf = Copy(Swig_last_file());
|
|
Append(dependencies, lf);
|
|
Delete(lf);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static int is_digits(const String *str) {
|
|
const char *s = Char(str);
|
|
int isdigits = (*s != 0);
|
|
while (*s) {
|
|
if (!isdigit((int)*s)) {
|
|
isdigits = 0;
|
|
break;
|
|
}
|
|
s++;
|
|
}
|
|
return isdigits;
|
|
}
|
|
|
|
List *Preprocessor_depend(void) {
|
|
return dependencies;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* void Preprocessor_cpp_init() - Initialize the preprocessor
|
|
* ----------------------------------------------------------------------------- */
|
|
static String *kpp_args = 0;
|
|
static String *kpp_define = 0;
|
|
static String *kpp_defined = 0;
|
|
static String *kpp_elif = 0;
|
|
static String *kpp_else = 0;
|
|
static String *kpp_endif = 0;
|
|
static String *kpp_expanded = 0;
|
|
static String *kpp_if = 0;
|
|
static String *kpp_ifdef = 0;
|
|
static String *kpp_ifndef = 0;
|
|
static String *kpp_name = 0;
|
|
static String *kpp_swigmacro = 0;
|
|
static String *kpp_symbols = 0;
|
|
static String *kpp_undef = 0;
|
|
static String *kpp_value = 0;
|
|
static String *kpp_varargs = 0;
|
|
static String *kpp_error = 0;
|
|
static String *kpp_warning = 0;
|
|
static String *kpp_line = 0;
|
|
static String *kpp_include = 0;
|
|
static String *kpp_pragma = 0;
|
|
static String *kpp_level = 0;
|
|
|
|
static String *kpp_dline = 0;
|
|
static String *kpp_ddefine = 0;
|
|
static String *kpp_dinclude = 0;
|
|
static String *kpp_dimport = 0;
|
|
static String *kpp_dbeginfile = 0;
|
|
static String *kpp_dextern = 0;
|
|
|
|
static String *kpp_LINE = 0;
|
|
static String *kpp_FILE = 0;
|
|
|
|
static String *kpp_hash_if = 0;
|
|
static String *kpp_hash_elif = 0;
|
|
|
|
void Preprocessor_init(void) {
|
|
Hash *s;
|
|
|
|
kpp_args = NewString("args");
|
|
kpp_define = NewString("define");
|
|
kpp_defined = NewString("defined");
|
|
kpp_else = NewString("else");
|
|
kpp_elif = NewString("elif");
|
|
kpp_endif = NewString("endif");
|
|
kpp_expanded = NewString("*expanded*");
|
|
kpp_if = NewString("if");
|
|
kpp_ifdef = NewString("ifdef");
|
|
kpp_ifndef = NewString("ifndef");
|
|
kpp_name = NewString("name");
|
|
kpp_swigmacro = NewString("swigmacro");
|
|
kpp_symbols = NewString("symbols");
|
|
kpp_undef = NewString("undef");
|
|
kpp_value = NewString("value");
|
|
kpp_error = NewString("error");
|
|
kpp_warning = NewString("warning");
|
|
kpp_pragma = NewString("pragma");
|
|
kpp_level = NewString("level");
|
|
kpp_line = NewString("line");
|
|
kpp_include = NewString("include");
|
|
kpp_varargs = NewString("varargs");
|
|
|
|
kpp_dinclude = NewString("%include");
|
|
kpp_dimport = NewString("%import");
|
|
kpp_dbeginfile = NewString("%beginfile");
|
|
kpp_dextern = NewString("%extern");
|
|
kpp_ddefine = NewString("%define");
|
|
kpp_dline = NewString("%line");
|
|
|
|
|
|
kpp_LINE = NewString("__LINE__");
|
|
kpp_FILE = NewString("__FILE__");
|
|
|
|
kpp_hash_if = NewString("#if");
|
|
kpp_hash_elif = NewString("#elif");
|
|
|
|
cpp = NewHash();
|
|
s = NewHash();
|
|
Setattr(cpp, kpp_symbols, s);
|
|
Delete(s);
|
|
Preprocessor_expr_init(); /* Initialize the expression evaluator */
|
|
included_files = NewHash();
|
|
|
|
id_scan = NewScanner();
|
|
|
|
}
|
|
|
|
void Preprocessor_delete(void) {
|
|
Delete(kpp_args);
|
|
Delete(kpp_define);
|
|
Delete(kpp_defined);
|
|
Delete(kpp_else);
|
|
Delete(kpp_elif);
|
|
Delete(kpp_endif);
|
|
Delete(kpp_expanded);
|
|
Delete(kpp_if);
|
|
Delete(kpp_ifdef);
|
|
Delete(kpp_ifndef);
|
|
Delete(kpp_name);
|
|
Delete(kpp_swigmacro);
|
|
Delete(kpp_symbols);
|
|
Delete(kpp_undef);
|
|
Delete(kpp_value);
|
|
Delete(kpp_error);
|
|
Delete(kpp_warning);
|
|
Delete(kpp_pragma);
|
|
Delete(kpp_level);
|
|
Delete(kpp_line);
|
|
Delete(kpp_include);
|
|
Delete(kpp_varargs);
|
|
|
|
Delete(kpp_dinclude);
|
|
Delete(kpp_dimport);
|
|
Delete(kpp_dbeginfile);
|
|
Delete(kpp_dextern);
|
|
Delete(kpp_ddefine);
|
|
Delete(kpp_dline);
|
|
|
|
Delete(kpp_LINE);
|
|
Delete(kpp_FILE);
|
|
|
|
Delete(kpp_hash_if);
|
|
Delete(kpp_hash_elif);
|
|
|
|
Delete(cpp);
|
|
Delete(included_files);
|
|
Preprocessor_expr_delete();
|
|
DelScanner(id_scan);
|
|
|
|
Delete(dependencies);
|
|
|
|
Delete(Swig_add_directory(0));
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* void Preprocessor_include_all() - Instruct preprocessor to include all files
|
|
* ----------------------------------------------------------------------------- */
|
|
void Preprocessor_include_all(int a) {
|
|
include_all = a;
|
|
}
|
|
|
|
void Preprocessor_import_all(int a) {
|
|
import_all = a;
|
|
}
|
|
|
|
void Preprocessor_ignore_missing(int a) {
|
|
ignore_missing = a;
|
|
}
|
|
|
|
void Preprocessor_error_as_warning(int a) {
|
|
error_as_warning = a;
|
|
}
|
|
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Preprocessor_define()
|
|
*
|
|
* Defines a new C preprocessor symbol. swigmacro specifies whether or not the macro has
|
|
* SWIG macro semantics.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
|
|
String *Macro_vararg_name(const_String_or_char_ptr str, const_String_or_char_ptr line) {
|
|
String *argname;
|
|
String *varargname;
|
|
char *s, *dots;
|
|
|
|
argname = Copy(str);
|
|
s = Char(argname);
|
|
dots = strchr(s, '.');
|
|
if (!dots) {
|
|
Delete(argname);
|
|
return NULL;
|
|
}
|
|
|
|
if (strcmp(dots, "...") != 0) {
|
|
Swig_error(Getfile(line), Getline(line), "Illegal macro argument name '%s'\n", str);
|
|
Delete(argname);
|
|
return NULL;
|
|
}
|
|
if (dots == s) {
|
|
varargname = NewString("__VA_ARGS__");
|
|
} else {
|
|
*dots = '\0';
|
|
varargname = NewString(s);
|
|
}
|
|
Delete(argname);
|
|
return varargname;
|
|
}
|
|
|
|
Hash *Preprocessor_define(const_String_or_char_ptr _str, int swigmacro) {
|
|
String *macroname = 0, *argstr = 0, *macrovalue = 0, *file = 0, *s = 0;
|
|
Hash *macro = 0, *symbols = 0, *m1;
|
|
List *arglist = 0;
|
|
int c, line;
|
|
int varargs = 0;
|
|
String *str;
|
|
|
|
assert(cpp);
|
|
assert(_str);
|
|
|
|
/* First make sure that string is actually a string */
|
|
if (DohCheck(_str)) {
|
|
s = Copy(_str);
|
|
copy_location(_str, s);
|
|
str = s;
|
|
} else {
|
|
str = NewString((char *) _str);
|
|
}
|
|
Seek(str, 0, SEEK_SET);
|
|
line = Getline(str);
|
|
file = Getfile(str);
|
|
|
|
/* Skip over any leading whitespace */
|
|
skip_whitespace(str, 0);
|
|
|
|
/* Now look for a macro name */
|
|
macroname = NewStringEmpty();
|
|
copy_location(str, macroname);
|
|
while ((c = Getc(str)) != EOF) {
|
|
if (c == '(') {
|
|
argstr = NewStringEmpty();
|
|
copy_location(str, argstr);
|
|
/* It is a macro. Go extract its argument string */
|
|
while ((c = Getc(str)) != EOF) {
|
|
if (c == ')')
|
|
break;
|
|
else
|
|
Putc(c, argstr);
|
|
}
|
|
if (c != ')') {
|
|
Swig_error(Getfile(argstr), Getline(argstr), "Missing \')\' in macro parameters\n");
|
|
goto macro_error;
|
|
}
|
|
break;
|
|
} else if (isidchar(c) || (c == '%')) {
|
|
Putc(c, macroname);
|
|
} else if (isspace(c)) {
|
|
break;
|
|
} else if (c == '\\') {
|
|
c = Getc(str);
|
|
if (c != '\n') {
|
|
Ungetc(c, str);
|
|
Ungetc('\\', str);
|
|
break;
|
|
}
|
|
} else {
|
|
Ungetc(c, str);
|
|
break;
|
|
}
|
|
}
|
|
if (!swigmacro)
|
|
skip_whitespace(str, 0);
|
|
macrovalue = NewStringEmpty();
|
|
copy_location(str, macrovalue);
|
|
while ((c = Getc(str)) != EOF) {
|
|
Putc(c, macrovalue);
|
|
}
|
|
|
|
/* If there are any macro arguments, convert into a list */
|
|
if (argstr) {
|
|
String *argname, *varargname;
|
|
arglist = NewList();
|
|
Seek(argstr, 0, SEEK_SET);
|
|
argname = NewStringEmpty();
|
|
while ((c = Getc(argstr)) != EOF) {
|
|
if (c == ',') {
|
|
varargname = Macro_vararg_name(argname, argstr);
|
|
if (varargname) {
|
|
Delete(varargname);
|
|
Swig_error(Getfile(argstr), Getline(argstr), "Variable length macro argument must be last parameter\n");
|
|
} else {
|
|
Append(arglist, argname);
|
|
}
|
|
Delete(argname);
|
|
argname = NewStringEmpty();
|
|
} else if (isidchar(c) || (c == '.')) {
|
|
Putc(c, argname);
|
|
} else if (!(isspace(c) || (c == '\\'))) {
|
|
Delete(argname);
|
|
Swig_error(Getfile(argstr), Getline(argstr), "Illegal character in macro argument name\n");
|
|
goto macro_error;
|
|
}
|
|
}
|
|
if (Len(argname)) {
|
|
/* Check for varargs */
|
|
varargname = Macro_vararg_name(argname, argstr);
|
|
if (varargname) {
|
|
Append(arglist, varargname);
|
|
Delete(varargname);
|
|
varargs = 1;
|
|
} else {
|
|
Append(arglist, argname);
|
|
}
|
|
}
|
|
Delete(argname);
|
|
}
|
|
|
|
if (!swigmacro) {
|
|
Replace(macrovalue, "\\\n", " ", DOH_REPLACE_NOQUOTE);
|
|
}
|
|
|
|
/* Look for special # substitutions. We only consider # that appears
|
|
outside of quotes and comments */
|
|
|
|
{
|
|
int state = 0;
|
|
char *cc = Char(macrovalue);
|
|
while (*cc) {
|
|
switch (state) {
|
|
case 0:
|
|
if (*cc == '#')
|
|
*cc = '\001';
|
|
else if (*cc == '/')
|
|
state = 10;
|
|
else if (*cc == '\'')
|
|
state = 20;
|
|
else if (*cc == '\"')
|
|
state = 30;
|
|
break;
|
|
case 10:
|
|
if (*cc == '*')
|
|
state = 11;
|
|
else if (*cc == '/')
|
|
state = 15;
|
|
else {
|
|
state = 0;
|
|
cc--;
|
|
}
|
|
break;
|
|
case 11:
|
|
if (*cc == '*')
|
|
state = 12;
|
|
break;
|
|
case 12:
|
|
if (*cc == '/')
|
|
state = 0;
|
|
else if (*cc != '*')
|
|
state = 11;
|
|
break;
|
|
case 15:
|
|
if (*cc == '\n')
|
|
state = 0;
|
|
break;
|
|
case 20:
|
|
if (*cc == '\'')
|
|
state = 0;
|
|
if (*cc == '\\')
|
|
state = 21;
|
|
break;
|
|
case 21:
|
|
state = 20;
|
|
break;
|
|
case 30:
|
|
if (*cc == '\"')
|
|
state = 0;
|
|
if (*cc == '\\')
|
|
state = 31;
|
|
break;
|
|
case 31:
|
|
state = 30;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
cc++;
|
|
}
|
|
}
|
|
|
|
/* Get rid of whitespace surrounding # */
|
|
/* Replace(macrovalue,"#","\001",DOH_REPLACE_NOQUOTE); */
|
|
while (strstr(Char(macrovalue), "\001 ")) {
|
|
Replace(macrovalue, "\001 ", "\001", DOH_REPLACE_ANY);
|
|
}
|
|
while (strstr(Char(macrovalue), " \001")) {
|
|
Replace(macrovalue, " \001", "\001", DOH_REPLACE_ANY);
|
|
}
|
|
/* Replace '##' with a special token */
|
|
Replace(macrovalue, "\001\001", "\002", DOH_REPLACE_ANY);
|
|
/* Replace '#@' with a special token */
|
|
Replace(macrovalue, "\001@", "\004", DOH_REPLACE_ANY);
|
|
/* Replace '##@' with a special token */
|
|
Replace(macrovalue, "\002@", "\005", DOH_REPLACE_ANY);
|
|
|
|
/* Go create the macro */
|
|
macro = NewHash();
|
|
Setattr(macro, kpp_name, macroname);
|
|
|
|
if (arglist) {
|
|
Setattr(macro, kpp_args, arglist);
|
|
Delete(arglist);
|
|
if (varargs) {
|
|
Setattr(macro, kpp_varargs, "1");
|
|
}
|
|
}
|
|
Setattr(macro, kpp_value, macrovalue);
|
|
Setline(macro, line);
|
|
Setfile(macro, file);
|
|
if (swigmacro) {
|
|
Setattr(macro, kpp_swigmacro, "1");
|
|
}
|
|
symbols = Getattr(cpp, kpp_symbols);
|
|
if ((m1 = Getattr(symbols, macroname))) {
|
|
if (!Checkattr(m1, kpp_value, macrovalue)) {
|
|
Swig_error(Getfile(macroname), Getline(macroname), "Macro '%s' redefined,\n", macroname);
|
|
Swig_error(Getfile(m1), Getline(m1), "previous definition of '%s'.\n", macroname);
|
|
goto macro_error;
|
|
}
|
|
} else {
|
|
Setattr(symbols, macroname, macro);
|
|
Delete(macro);
|
|
}
|
|
|
|
Delete(macroname);
|
|
Delete(macrovalue);
|
|
|
|
Delete(str);
|
|
Delete(argstr);
|
|
return macro;
|
|
|
|
macro_error:
|
|
Delete(str);
|
|
Delete(argstr);
|
|
Delete(arglist);
|
|
Delete(macroname);
|
|
Delete(macrovalue);
|
|
return 0;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Preprocessor_undef()
|
|
*
|
|
* Undefines a macro.
|
|
* ----------------------------------------------------------------------------- */
|
|
void Preprocessor_undef(const_String_or_char_ptr str) {
|
|
Hash *symbols;
|
|
assert(cpp);
|
|
symbols = Getattr(cpp, kpp_symbols);
|
|
Delattr(symbols, str);
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* find_args()
|
|
*
|
|
* Isolates macro arguments and returns them in a list. For each argument,
|
|
* leading and trailing whitespace is stripped (ala K&R, pg. 230).
|
|
* ----------------------------------------------------------------------------- */
|
|
static List *find_args(String *s, int ismacro, String *macro_name) {
|
|
List *args;
|
|
String *str;
|
|
int c, level;
|
|
long pos;
|
|
|
|
/* Create a new list */
|
|
args = NewList();
|
|
copy_location(s, args);
|
|
|
|
/* First look for a '(' */
|
|
pos = Tell(s);
|
|
skip_whitespace(s, 0);
|
|
|
|
/* Now see if the next character is a '(' */
|
|
c = Getc(s);
|
|
if (c != '(') {
|
|
/* Not a macro, bail out now! */
|
|
assert(pos != -1);
|
|
(void)Seek(s, pos, SEEK_SET);
|
|
Delete(args);
|
|
return 0;
|
|
}
|
|
c = Getc(s);
|
|
/* Okay. This appears to be a macro so we will start isolating arguments */
|
|
while (c != EOF) {
|
|
if (isspace(c)) {
|
|
skip_whitespace(s, 0); /* Skip leading whitespace */
|
|
c = Getc(s);
|
|
}
|
|
str = NewStringEmpty();
|
|
copy_location(s, str);
|
|
level = 0;
|
|
while (c != EOF) {
|
|
if (c == '\"') {
|
|
Putc(c, str);
|
|
skip_tochar(s, '\"', str);
|
|
c = Getc(s);
|
|
continue;
|
|
} else if (c == '\'') {
|
|
Putc(c, str);
|
|
skip_tochar(s, '\'', str);
|
|
c = Getc(s);
|
|
continue;
|
|
} else if (c == '/') {
|
|
/* Ensure comments are ignored by eating up the characters */
|
|
c = Getc(s);
|
|
/* Handle / * ... * / type comments (multi-line) */
|
|
if (c == '*') {
|
|
while ((c = Getc(s)) != EOF) {
|
|
if (c == '*') {
|
|
c = Getc(s);
|
|
if (c == '/' || c == EOF)
|
|
break;
|
|
}
|
|
}
|
|
c = Getc(s);
|
|
continue;
|
|
}
|
|
/* Handle // ... type comments (single-line) */
|
|
if (c == '/') {
|
|
while ((c = Getc(s)) != EOF) {
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
c = Getc(s);
|
|
continue;
|
|
}
|
|
/* ensure char is available in the stream as this was not a comment*/
|
|
Ungetc(c, s);
|
|
c = '/';
|
|
}
|
|
if ((c == ',') && (level == 0))
|
|
break;
|
|
if ((c == ')') && (level == 0))
|
|
break;
|
|
Putc(c, str);
|
|
if (c == '(')
|
|
level++;
|
|
if (c == ')')
|
|
level--;
|
|
c = Getc(s);
|
|
}
|
|
if (level > 0) {
|
|
goto unterm;
|
|
}
|
|
Chop(str);
|
|
Append(args, str);
|
|
Delete(str);
|
|
if (c == ')')
|
|
return args;
|
|
c = Getc(s);
|
|
}
|
|
unterm:
|
|
if (ismacro)
|
|
Swig_error(Getfile(args), Getline(args), "Unterminated call invoking macro '%s'\n", macro_name);
|
|
else
|
|
Swig_error(Getfile(args), Getline(args), "Unterminated call to '%s'\n", macro_name);
|
|
return args;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* DOH *get_filename()
|
|
*
|
|
* Read a filename from str. A filename can be enclosed in quotes, angle brackets,
|
|
* or bare.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
static String *get_filename(String *str, int *sysfile) {
|
|
String *fn;
|
|
int c;
|
|
|
|
fn = NewStringEmpty();
|
|
copy_location(str, fn);
|
|
c = Getc(str);
|
|
*sysfile = 0;
|
|
if (c == '\"') {
|
|
while (((c = Getc(str)) != EOF) && (c != '\"'))
|
|
Putc(c, fn);
|
|
} else if (c == '<') {
|
|
*sysfile = 1;
|
|
while (((c = Getc(str)) != EOF) && (c != '>'))
|
|
Putc(c, fn);
|
|
} else {
|
|
String *preprocessed_str;
|
|
Putc(c, fn);
|
|
while (((c = Getc(str)) != EOF) && (!isspace(c)))
|
|
Putc(c, fn);
|
|
if (isspace(c))
|
|
Ungetc(c, str);
|
|
preprocessed_str = Preprocessor_replace(fn);
|
|
Seek(preprocessed_str, 0, SEEK_SET);
|
|
Delete(fn);
|
|
|
|
fn = NewStringEmpty();
|
|
copy_location(preprocessed_str, fn);
|
|
c = Getc(preprocessed_str);
|
|
if (c == '\"') {
|
|
while (((c = Getc(preprocessed_str)) != EOF) && (c != '\"'))
|
|
Putc(c, fn);
|
|
} else if (c == '<') {
|
|
*sysfile = 1;
|
|
while (((c = Getc(preprocessed_str)) != EOF) && (c != '>'))
|
|
Putc(c, fn);
|
|
} else {
|
|
fn = Copy(preprocessed_str);
|
|
}
|
|
Delete(preprocessed_str);
|
|
}
|
|
Swig_filename_unescape(fn);
|
|
Swig_filename_correct(fn);
|
|
Seek(fn, 0, SEEK_SET);
|
|
return fn;
|
|
}
|
|
|
|
static String *get_options(String *str) {
|
|
int c;
|
|
c = Getc(str);
|
|
if (c == '(') {
|
|
String *opt;
|
|
int level = 1;
|
|
opt = NewString("(");
|
|
while (((c = Getc(str)) != EOF)) {
|
|
Putc(c, opt);
|
|
switch (c) {
|
|
case ')':
|
|
level--;
|
|
if (!level)
|
|
return opt;
|
|
break;
|
|
case '(':
|
|
level++;
|
|
break;
|
|
case '"':
|
|
/* Skip over quoted strings */
|
|
while (1) {
|
|
c = Getc(str);
|
|
if (c == EOF)
|
|
goto bad;
|
|
Putc(c, opt);
|
|
if (c == '"')
|
|
break;
|
|
if (c == '\\') {
|
|
c = Getc(str);
|
|
if (c == EOF)
|
|
goto bad;
|
|
Putc(c, opt);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
bad:
|
|
Delete(opt);
|
|
return 0;
|
|
} else {
|
|
Ungetc(c, str);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* expand_macro()
|
|
*
|
|
* Perform macro expansion and return a new string. Returns NULL if some sort
|
|
* of error occurred.
|
|
* name - name of the macro
|
|
* args - arguments passed to the macro
|
|
* line_file - only used for line/file name when reporting errors
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
static String *expand_macro(String *name, List *args, String *line_file) {
|
|
String *ns;
|
|
DOH *symbols, *macro, *margs, *mvalue, *temp, *tempa, *e;
|
|
int i, l;
|
|
int isvarargs = 0;
|
|
|
|
symbols = Getattr(cpp, kpp_symbols);
|
|
if (!symbols)
|
|
return 0;
|
|
|
|
/* See if the name is actually defined */
|
|
macro = Getattr(symbols, name);
|
|
if (!macro)
|
|
return 0;
|
|
|
|
if (macro_level == 0) {
|
|
/* Store the start of the macro should the macro contain __LINE__ and __FILE__ for expansion */
|
|
macro_start_line = Getline(args ? args : line_file);
|
|
macro_start_file = Getfile(args ? args : line_file);
|
|
}
|
|
macro_level++;
|
|
|
|
if (Getattr(macro, kpp_expanded)) {
|
|
ns = NewStringEmpty();
|
|
Append(ns, name);
|
|
if (args) {
|
|
int lenargs = Len(args);
|
|
if (lenargs)
|
|
Putc('(', ns);
|
|
for (i = 0; i < lenargs; i++) {
|
|
Append(ns, Getitem(args, i));
|
|
if (i < (lenargs - 1))
|
|
Putc(',', ns);
|
|
}
|
|
if (i)
|
|
Putc(')', ns);
|
|
}
|
|
macro_level--;
|
|
return ns;
|
|
}
|
|
|
|
/* Get macro arguments and value */
|
|
mvalue = Getattr(macro, kpp_value);
|
|
assert(mvalue);
|
|
margs = Getattr(macro, kpp_args);
|
|
|
|
if (args && Getattr(macro, kpp_varargs)) {
|
|
isvarargs = 1;
|
|
/* Variable length argument macro. We need to collect all of the extra arguments into a single argument */
|
|
if (Len(args) >= (Len(margs) - 1)) {
|
|
int i;
|
|
int vi, na;
|
|
String *vararg = NewStringEmpty();
|
|
vi = Len(margs) - 1;
|
|
na = Len(args);
|
|
for (i = vi; i < na; i++) {
|
|
Append(vararg, Getitem(args, i));
|
|
if ((i + 1) < na) {
|
|
Append(vararg, ",");
|
|
}
|
|
}
|
|
/* Remove arguments */
|
|
for (i = vi; i < na; i++) {
|
|
Delitem(args, vi);
|
|
}
|
|
Append(args, vararg);
|
|
Delete(vararg);
|
|
}
|
|
}
|
|
|
|
if (args && margs && Len(margs) == 0 && Len(args) == 1 && Len(Getitem(args, 0)) == 0) {
|
|
/* FOO() can invoke a macro defined as FOO(X) as well as one defined FOO().
|
|
*
|
|
* Handle this by removing the only argument if it's empty and the macro
|
|
* expects no arguments.
|
|
*
|
|
* We don't need to worry about varargs here - a varargs macro will always have
|
|
* Len(margs) >= 1, since the varargs are put in the final macro argument.
|
|
*/
|
|
Delitem(args, 0);
|
|
}
|
|
|
|
/* If there are arguments, see if they match what we were given */
|
|
if (args && (!margs || Len(margs) != Len(args))) {
|
|
if (margs && Len(margs) > (1 + isvarargs))
|
|
Swig_error(macro_start_file, macro_start_line, "Macro '%s' expects %d arguments\n", name, Len(margs) - isvarargs);
|
|
else if (margs && Len(margs) == (1 + isvarargs))
|
|
Swig_error(macro_start_file, macro_start_line, "Macro '%s' expects 1 argument\n", name);
|
|
else
|
|
Swig_error(macro_start_file, macro_start_line, "Macro '%s' expects no arguments\n", name);
|
|
macro_level--;
|
|
return 0;
|
|
}
|
|
|
|
/* If the macro expects arguments, but none were supplied, we leave it in place */
|
|
if (!args && margs) {
|
|
macro_level--;
|
|
return NewString(name);
|
|
}
|
|
|
|
/* Copy the macro value */
|
|
ns = Copy(mvalue);
|
|
copy_location(mvalue, ns);
|
|
|
|
/* Tag the macro as being expanded. This is to avoid recursion in
|
|
macro expansion */
|
|
|
|
temp = NewStringEmpty();
|
|
tempa = NewStringEmpty();
|
|
if (args && margs) {
|
|
l = Len(margs);
|
|
for (i = 0; i < l; i++) {
|
|
DOH *arg, *aname;
|
|
String *reparg;
|
|
arg = Getitem(args, i); /* Get an argument value */
|
|
reparg = Preprocessor_replace(arg);
|
|
aname = Getitem(margs, i); /* Get macro argument name */
|
|
if (strstr(Char(ns), "\001")) {
|
|
/* Try to replace a quoted version of the argument */
|
|
Clear(temp);
|
|
Clear(tempa);
|
|
Printf(temp, "\001%s", aname);
|
|
Printf(tempa, "\"%s\"", arg);
|
|
Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
|
|
}
|
|
if (strstr(Char(ns), "\002")) {
|
|
/* Look for concatenation tokens */
|
|
Clear(temp);
|
|
Clear(tempa);
|
|
Printf(temp, "\002%s", aname);
|
|
Append(tempa, "\002\003");
|
|
Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
|
|
Clear(temp);
|
|
Clear(tempa);
|
|
Printf(temp, "%s\002", aname);
|
|
Append(tempa, "\003\002");
|
|
Replace(ns, temp, tempa, DOH_REPLACE_ID_BEGIN);
|
|
}
|
|
|
|
/* Non-standard macro expansion. The value `x` is replaced by a quoted
|
|
version of the argument except that if the argument is already quoted
|
|
nothing happens */
|
|
|
|
if (strchr(Char(ns), '`')) {
|
|
String *rep;
|
|
char *c;
|
|
Clear(temp);
|
|
Printf(temp, "`%s`", aname);
|
|
c = Char(arg);
|
|
if (*c == '\"') {
|
|
rep = arg;
|
|
} else {
|
|
Clear(tempa);
|
|
Printf(tempa, "\"%s\"", arg);
|
|
rep = tempa;
|
|
}
|
|
Replace(ns, temp, rep, DOH_REPLACE_ANY);
|
|
}
|
|
|
|
/* Non-standard mangle expansions.
|
|
The #@Name is replaced by mangle_arg(Name). */
|
|
if (strstr(Char(ns), "\004")) {
|
|
String *marg = Swig_string_mangle(arg);
|
|
Clear(temp);
|
|
Printf(temp, "\004%s", aname);
|
|
Replace(ns, temp, marg, DOH_REPLACE_ID_END);
|
|
Delete(marg);
|
|
}
|
|
if (strstr(Char(ns), "\005")) {
|
|
String *marg = Swig_string_mangle(arg);
|
|
Clear(temp);
|
|
Clear(tempa);
|
|
Printf(temp, "\005%s", aname);
|
|
Printf(tempa, "\"%s\"", marg);
|
|
Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
|
|
Delete(marg);
|
|
}
|
|
|
|
if (isvarargs && i == l - 1 && Len(arg) == 0) {
|
|
/* Zero length varargs macro argument. We search for commas that might appear before and nuke them */
|
|
char *a, *s, *t, *name;
|
|
int namelen;
|
|
s = Char(ns);
|
|
name = Char(aname);
|
|
namelen = Len(aname);
|
|
a = strstr(s, name);
|
|
while (a) {
|
|
char ca = a[namelen];
|
|
if (!isidchar((int) ca)) {
|
|
/* Matched the entire vararg name, not just a prefix */
|
|
if (a > s) {
|
|
t = a - 1;
|
|
if (*t == '\002') {
|
|
t--;
|
|
while (t >= s) {
|
|
if (isspace((int) *t))
|
|
t--;
|
|
else if (*t == ',') {
|
|
*t = ' ';
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
a = strstr(a + namelen, name);
|
|
}
|
|
}
|
|
/* Replace(ns, aname, arg, DOH_REPLACE_ID); */
|
|
Replace(ns, aname, reparg, DOH_REPLACE_ID); /* Replace expanded args */
|
|
Replace(ns, "\003", arg, DOH_REPLACE_ANY); /* Replace unexpanded arg */
|
|
Delete(reparg);
|
|
}
|
|
}
|
|
Replace(ns, "\002", "", DOH_REPLACE_ANY); /* Get rid of concatenation tokens */
|
|
Replace(ns, "\001", "#", DOH_REPLACE_ANY); /* Put # back (non-standard C) */
|
|
Replace(ns, "\004", "#@", DOH_REPLACE_ANY); /* Put # back (non-standard C) */
|
|
|
|
/* Expand this macro even further */
|
|
Setattr(macro, kpp_expanded, "1");
|
|
|
|
e = Preprocessor_replace(ns);
|
|
|
|
Delattr(macro, kpp_expanded);
|
|
Delete(ns);
|
|
|
|
if (Getattr(macro, kpp_swigmacro)) {
|
|
String *g;
|
|
String *f = NewStringEmpty();
|
|
Seek(e, 0, SEEK_SET);
|
|
copy_location(macro, e);
|
|
g = Preprocessor_parse(e);
|
|
|
|
#if 0
|
|
/* Drop the macro in place, but with a marker around it */
|
|
Printf(f, "/*@%s,%d,%s@*/%s/*@@*/", Getfile(macro), Getline(macro), name, g);
|
|
#else
|
|
/* Use simplified around markers to properly count lines in cscanner.c */
|
|
if (strchr(Char(g), '\n')) {
|
|
Printf(f, "/*@SWIG:%s,%d,%s@*/%s/*@SWIG@*/", Getfile(macro), Getline(macro), name, g);
|
|
#if 0
|
|
Printf(f, "/*@SWIG:%s@*/%s/*@SWIG@*/", name, g);
|
|
#endif
|
|
} else {
|
|
Append(f, g);
|
|
}
|
|
#endif
|
|
|
|
Delete(g);
|
|
Delete(e);
|
|
e = f;
|
|
}
|
|
macro_level--;
|
|
Delete(temp);
|
|
Delete(tempa);
|
|
return e;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* DOH *Preprocessor_replace(DOH *s)
|
|
*
|
|
* Performs a macro substitution on a string s. Returns a new string with
|
|
* substitutions applied. This function works by walking down s and looking
|
|
* for identifiers. When found, a check is made to see if they are macros
|
|
* which are then expanded.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
/* #define SWIG_PUT_BUFF */
|
|
|
|
static DOH *Preprocessor_replace(DOH *s) {
|
|
DOH *ns, *symbols, *m;
|
|
int c, i, state = 0;
|
|
String *id = NewStringEmpty();
|
|
|
|
assert(cpp);
|
|
symbols = Getattr(cpp, kpp_symbols);
|
|
|
|
ns = NewStringEmpty();
|
|
copy_location(s, ns);
|
|
Seek(s, 0, SEEK_SET);
|
|
|
|
/* Try to locate identifiers in s and replace them with macro replacements */
|
|
while ((c = Getc(s)) != EOF) {
|
|
switch (state) {
|
|
case 0:
|
|
if (isidentifier(c)) {
|
|
Clear(id);
|
|
Putc(c, id);
|
|
state = 4;
|
|
} else if (c == '%') {
|
|
Clear(id);
|
|
Putc(c, id);
|
|
state = 2;
|
|
} else if (c == '#') {
|
|
Clear(id);
|
|
Putc(c, id);
|
|
state = 4;
|
|
} else if (c == '\"') {
|
|
Putc(c, ns);
|
|
skip_tochar(s, '\"', ns);
|
|
} else if (c == '\'') {
|
|
Putc(c, ns);
|
|
skip_tochar(s, '\'', ns);
|
|
} else if (c == '/') {
|
|
Putc(c, ns);
|
|
state = 10;
|
|
} else if (c == '\\') {
|
|
Putc(c, ns);
|
|
c = Getc(s);
|
|
if (c == '\n') {
|
|
Putc(c, ns);
|
|
} else {
|
|
Ungetc(c, s);
|
|
}
|
|
} else if (c == '\n') {
|
|
Putc(c, ns);
|
|
expand_defined_operator = 0;
|
|
} else {
|
|
Putc(c, ns);
|
|
}
|
|
break;
|
|
case 2:
|
|
/* Found '%#' */
|
|
if (c == '#') {
|
|
Putc(c, id);
|
|
state = 4;
|
|
} else {
|
|
Ungetc(c, s);
|
|
state = 4;
|
|
}
|
|
break;
|
|
case 4: /* An identifier */
|
|
if (isidchar(c)) {
|
|
Putc(c, id);
|
|
state = 4;
|
|
} else {
|
|
/* We found the end of a valid identifier */
|
|
Ungetc(c, s);
|
|
/* See if this is the special "defined" operator */
|
|
if (Equal(kpp_defined, id)) {
|
|
if (expand_defined_operator) {
|
|
int lenargs = 0;
|
|
DOH *args = 0;
|
|
/* See whether or not a parenthesis has been used */
|
|
skip_whitespace(s, 0);
|
|
c = Getc(s);
|
|
if (c == '(') {
|
|
Ungetc(c, s);
|
|
args = find_args(s, 0, kpp_defined);
|
|
} else if (isidchar(c)) {
|
|
DOH *arg = NewStringEmpty();
|
|
args = NewList();
|
|
Putc(c, arg);
|
|
while (((c = Getc(s)) != EOF)) {
|
|
if (!isidchar(c)) {
|
|
Ungetc(c, s);
|
|
break;
|
|
}
|
|
Putc(c, arg);
|
|
}
|
|
if (Len(arg))
|
|
Append(args, arg);
|
|
Delete(arg);
|
|
} else {
|
|
Seek(s, -1, SEEK_CUR);
|
|
}
|
|
lenargs = Len(args);
|
|
if ((!args) || (!lenargs)) {
|
|
/* This is not a defined() operator. */
|
|
Append(ns, id);
|
|
state = 0;
|
|
break;
|
|
}
|
|
for (i = 0; i < lenargs; i++) {
|
|
DOH *o = Getitem(args, i);
|
|
if (!Getattr(symbols, o)) {
|
|
break;
|
|
}
|
|
}
|
|
if (i < lenargs)
|
|
Putc('0', ns);
|
|
else
|
|
Putc('1', ns);
|
|
Delete(args);
|
|
} else {
|
|
Append(ns, id);
|
|
}
|
|
state = 0;
|
|
break;
|
|
} else if (Equal(kpp_LINE, id)) {
|
|
Printf(ns, "%d", macro_level > 0 ? macro_start_line : Getline(s));
|
|
state = 0;
|
|
break;
|
|
} else if (Equal(kpp_FILE, id)) {
|
|
String *fn = Copy(macro_level > 0 ? macro_start_file : Getfile(s));
|
|
Replaceall(fn, "\\", "\\\\");
|
|
Printf(ns, "\"%s\"", fn);
|
|
Delete(fn);
|
|
state = 0;
|
|
break;
|
|
} else if (Equal(kpp_hash_if, id) || Equal(kpp_hash_elif, id)) {
|
|
expand_defined_operator = 1;
|
|
Append(ns, id);
|
|
/*
|
|
} else if (Equal("%#if", id) || Equal("%#ifdef", id)) {
|
|
Swig_warning(998, Getfile(s), Getline(s), "Found: %s preprocessor directive.\n", id);
|
|
Append(ns, id);
|
|
} else if (Equal("#ifdef", id) || Equal("#ifndef", id)) {
|
|
Swig_warning(998, Getfile(s), Getline(s), "The %s preprocessor directive does not work in macros, try #if instead.\n", id);
|
|
Append(ns, id);
|
|
*/
|
|
} else if ((m = Getattr(symbols, id))) {
|
|
/* See if the macro is defined in the preprocessor symbol table */
|
|
DOH *args = 0;
|
|
DOH *e;
|
|
int macro_additional_lines = 0;
|
|
/* See if the macro expects arguments */
|
|
if (Getattr(m, kpp_args)) {
|
|
/* Yep. We need to go find the arguments and do a substitution */
|
|
int line = Getline(s);
|
|
args = find_args(s, 1, id);
|
|
macro_additional_lines = Getline(s) - line;
|
|
assert(macro_additional_lines >= 0);
|
|
} else {
|
|
args = 0;
|
|
}
|
|
e = expand_macro(id, args, s);
|
|
if (e) {
|
|
Append(ns, e);
|
|
}
|
|
while (macro_additional_lines--) {
|
|
Putc('\n', ns);
|
|
}
|
|
Delete(e);
|
|
Delete(args);
|
|
} else {
|
|
Append(ns, id);
|
|
}
|
|
state = 0;
|
|
}
|
|
break;
|
|
case 10:
|
|
if (c == '/')
|
|
state = 11;
|
|
else if (c == '*')
|
|
state = 12;
|
|
else {
|
|
Ungetc(c, s);
|
|
state = 0;
|
|
break;
|
|
}
|
|
Putc(c, ns);
|
|
break;
|
|
case 11:
|
|
/* in C++ comment */
|
|
Putc(c, ns);
|
|
if (c == '\n') {
|
|
expand_defined_operator = 0;
|
|
state = 0;
|
|
}
|
|
break;
|
|
case 12:
|
|
/* in C comment */
|
|
Putc(c, ns);
|
|
if (c == '*')
|
|
state = 13;
|
|
break;
|
|
case 13:
|
|
Putc(c, ns);
|
|
if (c == '/')
|
|
state = 0;
|
|
else if (c != '*')
|
|
state = 12;
|
|
break;
|
|
default:
|
|
state = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Identifier at the end */
|
|
if (state == 2 || state == 4) {
|
|
/* See if this is the special "defined" operator */
|
|
if (Equal(kpp_defined, id)) {
|
|
Swig_error(Getfile(s), Getline(s), "No arguments given to defined()\n");
|
|
} else if (Equal(kpp_LINE, id)) {
|
|
Printf(ns, "%d", macro_level > 0 ? macro_start_line : Getline(s));
|
|
} else if (Equal(kpp_FILE, id)) {
|
|
String *fn = Copy(macro_level > 0 ? macro_start_file : Getfile(s));
|
|
Replaceall(fn, "\\", "\\\\");
|
|
Printf(ns, "\"%s\"", fn);
|
|
Delete(fn);
|
|
} else if (Getattr(symbols, id)) {
|
|
DOH *e;
|
|
/* Yes. There is a macro here */
|
|
/* See if the macro expects arguments */
|
|
e = expand_macro(id, 0, s);
|
|
if (e)
|
|
Append(ns, e);
|
|
Delete(e);
|
|
} else {
|
|
Append(ns, id);
|
|
}
|
|
}
|
|
Delete(id);
|
|
return ns;
|
|
}
|
|
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* int checkpp_id(DOH *s)
|
|
*
|
|
* Checks the string s to see if it contains any unresolved identifiers. This
|
|
* function contains the heuristic that determines whether or not a macro
|
|
* definition passes through the preprocessor as a constant declaration.
|
|
* ----------------------------------------------------------------------------- */
|
|
static int checkpp_id(DOH *s) {
|
|
int c;
|
|
int hastok = 0;
|
|
Scanner *scan = id_scan;
|
|
|
|
Seek(s, 0, SEEK_SET);
|
|
|
|
Scanner_clear(scan);
|
|
s = Copy(s);
|
|
Seek(s, SEEK_SET, 0);
|
|
Scanner_push(scan, s);
|
|
while ((c = Scanner_token(scan))) {
|
|
hastok = 1;
|
|
if ((c == SWIG_TOKEN_ID) || (c == SWIG_TOKEN_LBRACE) || (c == SWIG_TOKEN_RBRACE))
|
|
return 1;
|
|
}
|
|
if (!hastok)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* addline(). Utility function for adding lines to a chunk */
|
|
static void addline(DOH *s1, DOH *s2, int allow) {
|
|
if (allow) {
|
|
Append(s1, s2);
|
|
} else {
|
|
char *c = Char(s2);
|
|
while (*c) {
|
|
if (*c == '\n')
|
|
Putc('\n', s1);
|
|
c++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void add_chunk(DOH *ns, DOH *chunk, int allow) {
|
|
DOH *echunk;
|
|
Seek(chunk, 0, SEEK_SET);
|
|
if (allow) {
|
|
echunk = Preprocessor_replace(chunk);
|
|
addline(ns, echunk, allow);
|
|
Delete(echunk);
|
|
} else {
|
|
addline(ns, chunk, 0);
|
|
}
|
|
Clear(chunk);
|
|
}
|
|
|
|
/*
|
|
push/pop_imported(): helper functions for defining and undefining
|
|
SWIGIMPORTED (when %importing a file).
|
|
*/
|
|
static void push_imported(void) {
|
|
if (imported_depth == 0) {
|
|
Preprocessor_define("SWIGIMPORTED 1", 0);
|
|
}
|
|
++imported_depth;
|
|
}
|
|
|
|
static void pop_imported(void) {
|
|
--imported_depth;
|
|
if (imported_depth == 0) {
|
|
Preprocessor_undef("SWIGIMPORTED");
|
|
}
|
|
}
|
|
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Preprocessor_parse()
|
|
*
|
|
* Parses the string s. Returns a new string containing the preprocessed version.
|
|
*
|
|
* Parsing rules :
|
|
* 1. Lines starting with # are C preprocessor directives
|
|
* 2. Macro expansion inside strings is not allowed
|
|
* 3. All code inside false conditionals is changed to blank lines
|
|
* 4. Code in %{, %} is not parsed because it may need to be
|
|
* included inline (with all preprocessor directives included).
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
String *Preprocessor_parse(String *s) {
|
|
String *ns; /* New string containing the preprocessed text */
|
|
String *chunk, *decl;
|
|
Hash *symbols;
|
|
String *id = 0, *value = 0, *comment = 0;
|
|
int i, state, e, c;
|
|
int start_line = 0;
|
|
int allow = 1;
|
|
int level = 0;
|
|
int dlevel = 0;
|
|
int filelevel = 0;
|
|
int mask = 0;
|
|
int start_level = 0;
|
|
int cpp_lines = 0;
|
|
int cond_lines[256];
|
|
|
|
/* Blow away all carriage returns */
|
|
Replace(s, "\015", "", DOH_REPLACE_ANY);
|
|
|
|
ns = NewStringEmpty(); /* Return result */
|
|
|
|
decl = NewStringEmpty();
|
|
id = NewStringEmpty();
|
|
value = NewStringEmpty();
|
|
comment = NewStringEmpty();
|
|
chunk = NewStringEmpty();
|
|
copy_location(s, chunk);
|
|
copy_location(s, ns);
|
|
symbols = Getattr(cpp, kpp_symbols);
|
|
|
|
state = 0;
|
|
while ((c = Getc(s)) != EOF) {
|
|
switch (state) {
|
|
case 0: /* Initial state - in first column */
|
|
/* Look for C preprocessor directives. Otherwise, go directly to state 1 */
|
|
if (c == '#') {
|
|
copy_location(s, chunk);
|
|
add_chunk(ns, chunk, allow);
|
|
cpp_lines = 1;
|
|
state = 40;
|
|
} else if (isspace(c)) {
|
|
Putc(c, chunk);
|
|
skip_whitespace(s, chunk);
|
|
} else {
|
|
state = 1;
|
|
Ungetc(c, s);
|
|
}
|
|
break;
|
|
case 1: /* Non-preprocessor directive */
|
|
/* Look for SWIG directives */
|
|
if (c == '%') {
|
|
state = 100;
|
|
break;
|
|
}
|
|
Putc(c, chunk);
|
|
if (c == '\n')
|
|
state = 0;
|
|
else if (c == '\"') {
|
|
start_line = Getline(s);
|
|
if (skip_tochar(s, '\"', chunk) < 0) {
|
|
Swig_error(Getfile(s), start_line, "Unterminated string constant\n");
|
|
}
|
|
} else if (c == '\'') {
|
|
start_line = Getline(s);
|
|
if (skip_tochar(s, '\'', chunk) < 0) {
|
|
Swig_error(Getfile(s), start_line, "Unterminated character constant\n");
|
|
}
|
|
} else if (c == '/')
|
|
state = 30; /* Comment */
|
|
break;
|
|
|
|
case 30: /* Possibly a comment string of some sort */
|
|
start_line = Getline(s);
|
|
Putc(c, chunk);
|
|
if (c == '/')
|
|
state = 31;
|
|
else if (c == '*')
|
|
state = 32;
|
|
else
|
|
state = 1;
|
|
break;
|
|
case 31:
|
|
Putc(c, chunk);
|
|
if (c == '\n')
|
|
state = 0;
|
|
break;
|
|
case 32:
|
|
Putc(c, chunk);
|
|
if (c == '*')
|
|
state = 33;
|
|
break;
|
|
case 33:
|
|
Putc(c, chunk);
|
|
if (c == '/')
|
|
state = 1;
|
|
else if (c != '*')
|
|
state = 32;
|
|
break;
|
|
|
|
case 40: /* Start of a C preprocessor directive */
|
|
if (c == '\n') {
|
|
Putc('\n', chunk);
|
|
state = 0;
|
|
} else if (isspace(c)) {
|
|
state = 40;
|
|
} else {
|
|
/* Got the start of a preprocessor directive */
|
|
Ungetc(c, s);
|
|
Clear(id);
|
|
copy_location(s, id);
|
|
state = 41;
|
|
}
|
|
break;
|
|
|
|
case 41: /* Build up the name of the preprocessor directive */
|
|
if ((isspace(c) || (!isidchar(c)))) {
|
|
Clear(value);
|
|
Clear(comment);
|
|
if (c == '\n') {
|
|
Ungetc(c, s);
|
|
state = 50;
|
|
} else {
|
|
state = 42;
|
|
if (!isspace(c)) {
|
|
Ungetc(c, s);
|
|
}
|
|
}
|
|
|
|
copy_location(s, value);
|
|
break;
|
|
}
|
|
Putc(c, id);
|
|
break;
|
|
|
|
case 42: /* Strip any leading space after the preprocessor directive (before preprocessor value) */
|
|
if (isspace(c)) {
|
|
if (c == '\n') {
|
|
Ungetc(c, s);
|
|
state = 50;
|
|
}
|
|
break;
|
|
}
|
|
state = 43;
|
|
/* FALL THRU */
|
|
|
|
case 43:
|
|
/* Get preprocessor value */
|
|
if (c == '\n') {
|
|
Ungetc(c, s);
|
|
state = 50;
|
|
} else if (c == '/') {
|
|
state = 45;
|
|
} else if (c == '\"') {
|
|
Putc(c, value);
|
|
skip_tochar(s, '\"', value);
|
|
} else if (c == '\'') {
|
|
Putc(c, value);
|
|
skip_tochar(s, '\'', value);
|
|
} else {
|
|
Putc(c, value);
|
|
if (c == '\\')
|
|
state = 44;
|
|
}
|
|
break;
|
|
|
|
case 44:
|
|
if (c == '\n') {
|
|
Putc(c, value);
|
|
cpp_lines++;
|
|
} else {
|
|
Ungetc(c, s);
|
|
}
|
|
state = 43;
|
|
break;
|
|
|
|
/* States 45-48 are used to remove, but retain comments from macro values. The comments
|
|
will be placed in the output in an alternative form */
|
|
|
|
case 45:
|
|
if (c == '/')
|
|
state = 46;
|
|
else if (c == '*')
|
|
state = 47;
|
|
else if (c == '\n') {
|
|
Putc('/', value);
|
|
Ungetc(c, s);
|
|
state = 50;
|
|
} else {
|
|
Putc('/', value);
|
|
Putc(c, value);
|
|
state = 43;
|
|
}
|
|
break;
|
|
case 46: /* in C++ comment */
|
|
if (c == '\n') {
|
|
Ungetc(c, s);
|
|
state = 50;
|
|
} else
|
|
Putc(c, comment);
|
|
break;
|
|
case 47: /* in C comment */
|
|
if (c == '*')
|
|
state = 48;
|
|
else
|
|
Putc(c, comment);
|
|
break;
|
|
case 48:
|
|
if (c == '/')
|
|
state = 43;
|
|
else if (c == '*')
|
|
Putc(c, comment);
|
|
else {
|
|
Putc('*', comment);
|
|
Putc(c, comment);
|
|
state = 47;
|
|
}
|
|
break;
|
|
case 50:
|
|
/* Check for various preprocessor directives */
|
|
Chop(value);
|
|
if (Equal(id, kpp_define)) {
|
|
if (allow) {
|
|
DOH *m, *v, *v1;
|
|
Seek(value, 0, SEEK_SET);
|
|
m = Preprocessor_define(value, 0);
|
|
if ((m) && !(Getattr(m, kpp_args))) {
|
|
v = Copy(Getattr(m, kpp_value));
|
|
copy_location(m, v);
|
|
if (Len(v)) {
|
|
Swig_error_silent(1);
|
|
v1 = Preprocessor_replace(v);
|
|
Swig_error_silent(0);
|
|
/* Printf(stdout,"checking '%s'\n", v1); */
|
|
if (!checkpp_id(v1)) {
|
|
if (Len(comment) == 0)
|
|
Printf(ns, "%%constant %s = %s;\n", Getattr(m, kpp_name), v1);
|
|
else
|
|
Printf(ns, "%%constant %s = %s; /*%s*/\n", Getattr(m, kpp_name), v1, comment);
|
|
cpp_lines--;
|
|
}
|
|
Delete(v1);
|
|
}
|
|
Delete(v);
|
|
}
|
|
}
|
|
} else if (Equal(id, kpp_undef)) {
|
|
if (allow)
|
|
Preprocessor_undef(value);
|
|
} else if (Equal(id, kpp_ifdef)) {
|
|
cond_lines[level] = Getline(id);
|
|
level++;
|
|
if (allow) {
|
|
start_level = level;
|
|
if (Len(value) > 0) {
|
|
/* See if the identifier is in the hash table */
|
|
if (!Getattr(symbols, value))
|
|
allow = 0;
|
|
} else {
|
|
Swig_error(Getfile(s), Getline(id), "Missing identifier for #ifdef.\n");
|
|
allow = 0;
|
|
}
|
|
mask = 1;
|
|
}
|
|
} else if (Equal(id, kpp_ifndef)) {
|
|
cond_lines[level] = Getline(id);
|
|
level++;
|
|
if (allow) {
|
|
start_level = level;
|
|
if (Len(value) > 0) {
|
|
/* See if the identifier is in the hash table */
|
|
if (Getattr(symbols, value))
|
|
allow = 0;
|
|
} else {
|
|
Swig_error(Getfile(s), Getline(id), "Missing identifier for #ifndef.\n");
|
|
allow = 0;
|
|
}
|
|
mask = 1;
|
|
}
|
|
} else if (Equal(id, kpp_else)) {
|
|
if (level <= 0) {
|
|
Swig_error(Getfile(s), Getline(id), "Misplaced #else.\n");
|
|
} else {
|
|
cond_lines[level - 1] = Getline(id);
|
|
if (Len(value) != 0)
|
|
Swig_warning(WARN_PP_UNEXPECTED_TOKENS, Getfile(s), Getline(id), "Unexpected tokens after #else directive.\n");
|
|
if (allow) {
|
|
allow = 0;
|
|
mask = 0;
|
|
} else if (level == start_level) {
|
|
allow = 1 * mask;
|
|
}
|
|
}
|
|
} else if (Equal(id, kpp_endif)) {
|
|
level--;
|
|
if (level < 0) {
|
|
Swig_error(Getfile(id), Getline(id), "Extraneous #endif.\n");
|
|
level = 0;
|
|
} else {
|
|
if (level < start_level) {
|
|
if (Len(value) != 0)
|
|
Swig_warning(WARN_PP_UNEXPECTED_TOKENS, Getfile(s), Getline(id), "Unexpected tokens after #endif directive.\n");
|
|
allow = 1;
|
|
start_level--;
|
|
}
|
|
}
|
|
} else if (Equal(id, kpp_if)) {
|
|
cond_lines[level] = Getline(id);
|
|
level++;
|
|
if (allow) {
|
|
int val;
|
|
String *sval;
|
|
expand_defined_operator = 1;
|
|
sval = Preprocessor_replace(value);
|
|
start_level = level;
|
|
Seek(sval, 0, SEEK_SET);
|
|
/* Printf(stdout,"Evaluating '%s'\n", sval); */
|
|
if (Len(sval) > 0) {
|
|
val = Preprocessor_expr(sval, &e);
|
|
if (e) {
|
|
const char *msg = Preprocessor_expr_error();
|
|
Seek(value, 0, SEEK_SET);
|
|
Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "Could not evaluate expression '%s'\n", value);
|
|
if (msg)
|
|
Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "%s\n", msg);
|
|
allow = 0;
|
|
} else {
|
|
if (val == 0)
|
|
allow = 0;
|
|
}
|
|
} else {
|
|
Swig_error(Getfile(s), Getline(id), "Missing expression for #if.\n");
|
|
allow = 0;
|
|
}
|
|
expand_defined_operator = 0;
|
|
mask = 1;
|
|
}
|
|
} else if (Equal(id, kpp_elif)) {
|
|
if (level == 0) {
|
|
Swig_error(Getfile(s), Getline(id), "Misplaced #elif.\n");
|
|
} else {
|
|
cond_lines[level - 1] = Getline(id);
|
|
if (allow) {
|
|
allow = 0;
|
|
mask = 0;
|
|
} else if (level == start_level) {
|
|
int val;
|
|
String *sval;
|
|
expand_defined_operator = 1;
|
|
sval = Preprocessor_replace(value);
|
|
Seek(sval, 0, SEEK_SET);
|
|
if (Len(sval) > 0) {
|
|
val = Preprocessor_expr(sval, &e);
|
|
if (e) {
|
|
const char *msg = Preprocessor_expr_error();
|
|
Seek(value, 0, SEEK_SET);
|
|
Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "Could not evaluate expression '%s'\n", value);
|
|
if (msg)
|
|
Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "%s\n", msg);
|
|
allow = 0;
|
|
} else {
|
|
if (val)
|
|
allow = 1 * mask;
|
|
else
|
|
allow = 0;
|
|
}
|
|
} else {
|
|
Swig_error(Getfile(s), Getline(id), "Missing expression for #elif.\n");
|
|
allow = 0;
|
|
}
|
|
expand_defined_operator = 0;
|
|
}
|
|
}
|
|
} else if (Equal(id, kpp_warning)) {
|
|
if (allow) {
|
|
Swig_warning(WARN_PP_CPP_WARNING, Getfile(s), Getline(id), "CPP #warning, \"%s\".\n", value);
|
|
}
|
|
} else if (Equal(id, kpp_error)) {
|
|
if (allow) {
|
|
if (error_as_warning) {
|
|
Swig_warning(WARN_PP_CPP_ERROR, Getfile(s), Getline(id), "CPP #error \"%s\".\n", value);
|
|
} else {
|
|
Swig_error(Getfile(s), Getline(id), "CPP #error \"%s\". Use the -cpperraswarn option to continue swig processing.\n", value);
|
|
}
|
|
}
|
|
} else if (Equal(id, kpp_line)) {
|
|
} else if (Equal(id, kpp_include)) {
|
|
if (((include_all) || (import_all)) && (allow)) {
|
|
String *s1, *s2, *fn;
|
|
String *dirname;
|
|
int sysfile = 0;
|
|
if (include_all && import_all) {
|
|
Swig_warning(WARN_PP_INCLUDEALL_IMPORTALL, Getfile(s), Getline(id), "Both includeall and importall are defined: using includeall.\n");
|
|
import_all = 0;
|
|
}
|
|
Seek(value, 0, SEEK_SET);
|
|
fn = get_filename(value, &sysfile);
|
|
s1 = cpp_include(fn, sysfile);
|
|
if (s1) {
|
|
if (include_all)
|
|
Printf(ns, "%%includefile \"%s\" %%beginfile\n", Swig_filename_escape(Swig_last_file()));
|
|
else if (import_all) {
|
|
Printf(ns, "%%importfile \"%s\" %%beginfile\n", Swig_filename_escape(Swig_last_file()));
|
|
push_imported();
|
|
}
|
|
|
|
/* See if the filename has a directory component */
|
|
dirname = Swig_file_dirname(Swig_last_file());
|
|
if (sysfile || !Len(dirname)) {
|
|
Delete(dirname);
|
|
dirname = 0;
|
|
}
|
|
if (dirname) {
|
|
int len = Len(dirname);
|
|
Delslice(dirname, len - 1, len); /* Kill trailing directory delimiter */
|
|
Swig_push_directory(dirname);
|
|
}
|
|
s2 = Preprocessor_parse(s1);
|
|
addline(ns, s2, allow);
|
|
Append(ns, "%endoffile");
|
|
if (dirname) {
|
|
Swig_pop_directory();
|
|
}
|
|
if (import_all) {
|
|
pop_imported();
|
|
}
|
|
Delete(s2);
|
|
Delete(dirname);
|
|
Delete(s1);
|
|
}
|
|
Delete(fn);
|
|
}
|
|
} else if (Equal(id, kpp_pragma)) {
|
|
if (Strncmp(value, "SWIG ", 5) == 0) {
|
|
char *c = Char(value) + 5;
|
|
while (*c && (isspace((int) *c)))
|
|
c++;
|
|
if (*c) {
|
|
if (strncmp(c, "nowarn=", 7) == 0) {
|
|
String *val = NewString(c + 7);
|
|
String *nowarn = Preprocessor_replace(val);
|
|
Swig_warnfilter(nowarn, 1);
|
|
Delete(nowarn);
|
|
Delete(val);
|
|
} else if (strncmp(c, "cpperraswarn=", 13) == 0) {
|
|
error_as_warning = atoi(c + 13);
|
|
} else {
|
|
Swig_error(Getfile(s), Getline(id), "Unknown SWIG pragma: %s\n", c);
|
|
}
|
|
}
|
|
}
|
|
} else if (Equal(id, kpp_level)) {
|
|
Swig_error(Getfile(s), Getline(id), "cpp debug: level = %d, startlevel = %d\n", level, start_level);
|
|
} else if (Equal(id, "")) {
|
|
/* Null directive */
|
|
} else if (is_digits(id)) {
|
|
/* A gcc linemarker of the form '# linenum filename flags' (resulting from running gcc -E) */
|
|
} else {
|
|
/* Ignore unknown preprocessor directives which are inside an inactive
|
|
* conditional (github issue #394). */
|
|
if (allow)
|
|
Swig_error(Getfile(s), Getline(id), "Unknown SWIG preprocessor directive: %s (if this is a block of target language code, delimit it with %%{ and %%})\n", id);
|
|
}
|
|
for (i = 0; i < cpp_lines; i++)
|
|
Putc('\n', ns);
|
|
state = 0;
|
|
break;
|
|
|
|
/* SWIG directives */
|
|
case 100:
|
|
/* %{,%} block */
|
|
if (c == '{') {
|
|
start_line = Getline(s);
|
|
copy_location(s, chunk);
|
|
add_chunk(ns, chunk, allow);
|
|
Putc('%', chunk);
|
|
Putc(c, chunk);
|
|
state = 105;
|
|
}
|
|
/* %#cpp - an embedded C preprocessor directive (we strip off the %) */
|
|
else if (c == '#') {
|
|
add_chunk(ns, chunk, allow);
|
|
Putc(c, chunk);
|
|
state = 107;
|
|
} else if (isidentifier(c)) {
|
|
Clear(decl);
|
|
Putc('%', decl);
|
|
Putc(c, decl);
|
|
state = 110;
|
|
} else {
|
|
Putc('%', chunk);
|
|
Putc(c, chunk);
|
|
state = 1;
|
|
}
|
|
break;
|
|
|
|
case 105:
|
|
Putc(c, chunk);
|
|
if (c == '%')
|
|
state = 106;
|
|
break;
|
|
|
|
case 106:
|
|
Putc(c, chunk);
|
|
if (c == '}') {
|
|
state = 1;
|
|
addline(ns, chunk, allow);
|
|
Clear(chunk);
|
|
copy_location(s, chunk);
|
|
} else {
|
|
state = 105;
|
|
}
|
|
break;
|
|
|
|
case 107:
|
|
Putc(c, chunk);
|
|
if (c == '\n') {
|
|
addline(ns, chunk, allow);
|
|
Clear(chunk);
|
|
state = 0;
|
|
} else if (c == '\\') {
|
|
state = 108;
|
|
}
|
|
break;
|
|
|
|
case 108:
|
|
Putc(c, chunk);
|
|
state = 107;
|
|
break;
|
|
|
|
case 110:
|
|
if (!isidchar(c)) {
|
|
Ungetc(c, s);
|
|
/* Look for common SWIG directives */
|
|
if (Equal(decl, kpp_dinclude) || Equal(decl, kpp_dimport) || Equal(decl, kpp_dextern)) {
|
|
/* Got some kind of file inclusion directive, eg: %import(option1="value1") "filename" */
|
|
if (allow) {
|
|
DOH *s1, *s2, *fn, *opt;
|
|
String *options_whitespace = NewStringEmpty();
|
|
String *filename_whitespace = NewStringEmpty();
|
|
int sysfile = 0;
|
|
|
|
if (Equal(decl, kpp_dextern)) {
|
|
Swig_warning(WARN_DEPRECATED_EXTERN, Getfile(s), Getline(s), "%%extern is deprecated. Use %%import instead.\n");
|
|
Clear(decl);
|
|
Append(decl, "%%import");
|
|
}
|
|
skip_whitespace(s, options_whitespace);
|
|
opt = get_options(s);
|
|
|
|
skip_whitespace(s, filename_whitespace);
|
|
fn = get_filename(s, &sysfile);
|
|
s1 = cpp_include(fn, sysfile);
|
|
if (s1) {
|
|
String *dirname;
|
|
copy_location(s, chunk);
|
|
add_chunk(ns, chunk, allow);
|
|
Printf(ns, "%sfile%s%s%s\"%s\" %%beginfile\n", decl, options_whitespace, opt, filename_whitespace, Swig_filename_escape(Swig_last_file()));
|
|
if (Equal(decl, kpp_dimport)) {
|
|
push_imported();
|
|
}
|
|
dirname = Swig_file_dirname(Swig_last_file());
|
|
if (sysfile || !Len(dirname)) {
|
|
Delete(dirname);
|
|
dirname = 0;
|
|
}
|
|
if (dirname) {
|
|
int len = Len(dirname);
|
|
Delslice(dirname, len - 1, len); /* Kill trailing directory delimiter */
|
|
Swig_push_directory(dirname);
|
|
}
|
|
s2 = Preprocessor_parse(s1);
|
|
if (dirname) {
|
|
Swig_pop_directory();
|
|
}
|
|
if (Equal(decl, kpp_dimport)) {
|
|
pop_imported();
|
|
}
|
|
addline(ns, s2, allow);
|
|
Append(ns, "%endoffile");
|
|
Delete(s2);
|
|
Delete(dirname);
|
|
Delete(s1);
|
|
}
|
|
Delete(fn);
|
|
Delete(filename_whitespace);
|
|
Delete(options_whitespace);
|
|
}
|
|
state = 1;
|
|
} else if (Equal(decl, kpp_dbeginfile)) {
|
|
/* Got an internal directive marking the beginning of an included file: %beginfile ... %endoffile */
|
|
filelevel++;
|
|
start_line = Getline(s);
|
|
copy_location(s, chunk);
|
|
add_chunk(ns, chunk, allow);
|
|
Append(chunk, decl);
|
|
state = 120;
|
|
} else if (Equal(decl, kpp_dline)) {
|
|
/* Got a line directive */
|
|
state = 1;
|
|
} else if (Equal(decl, kpp_ddefine)) {
|
|
/* Got a define directive */
|
|
dlevel++;
|
|
copy_location(s, chunk);
|
|
add_chunk(ns, chunk, allow);
|
|
Clear(value);
|
|
copy_location(s, value);
|
|
state = 150;
|
|
} else {
|
|
Append(chunk, decl);
|
|
state = 1;
|
|
}
|
|
} else {
|
|
Putc(c, decl);
|
|
}
|
|
break;
|
|
|
|
/* Searching for the end of a %beginfile block */
|
|
case 120:
|
|
Putc(c, chunk);
|
|
if (c == '%') {
|
|
const char *bf = "beginfile";
|
|
const char *ef = "endoffile";
|
|
char statement[10];
|
|
int i = 0;
|
|
for (i = 0; i < 9;) {
|
|
c = Getc(s);
|
|
Putc(c, chunk);
|
|
statement[i++] = (char)c;
|
|
if (strncmp(statement, bf, i) && strncmp(statement, ef, i))
|
|
break;
|
|
}
|
|
c = Getc(s);
|
|
Ungetc(c, s);
|
|
if ((i == 9) && (isspace(c))) {
|
|
if (strncmp(statement, bf, i) == 0) {
|
|
++filelevel;
|
|
} else if (strncmp(statement, ef, i) == 0) {
|
|
--filelevel;
|
|
if (!filelevel) {
|
|
/* Reached end of included file */
|
|
addline(ns, chunk, allow);
|
|
Clear(chunk);
|
|
copy_location(s, chunk);
|
|
state = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* Searching for the end of a %define statement */
|
|
case 150:
|
|
Putc(c, value);
|
|
if (c == '%') {
|
|
const char *ed = "enddef";
|
|
const char *df = "define";
|
|
char statement[7];
|
|
int i = 0;
|
|
for (i = 0; i < 6;) {
|
|
c = Getc(s);
|
|
Putc(c, value);
|
|
statement[i++] = (char)c;
|
|
if (strncmp(statement, ed, i) && strncmp(statement, df, i))
|
|
break;
|
|
}
|
|
c = Getc(s);
|
|
Ungetc(c, s);
|
|
if ((i == 6) && (isspace(c))) {
|
|
if (strncmp(statement, df, i) == 0) {
|
|
++dlevel;
|
|
} else {
|
|
if (strncmp(statement, ed, i) == 0) {
|
|
--dlevel;
|
|
if (!dlevel) {
|
|
/* Got the macro */
|
|
for (i = 0; i < 7; i++) {
|
|
Delitem(value, DOH_END);
|
|
}
|
|
if (allow) {
|
|
Seek(value, 0, SEEK_SET);
|
|
Preprocessor_define(value, 1);
|
|
}
|
|
addline(ns, value, 0);
|
|
state = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Printf(stderr, "cpp: Invalid parser state %d\n", state);
|
|
Exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
while (level > 0) {
|
|
Swig_error(Getfile(s), cond_lines[level - 1], "Missing #endif for conditional starting here\n");
|
|
level--;
|
|
}
|
|
if (state == 120) {
|
|
Swig_error(Getfile(s), start_line, "Missing %%endoffile for file inclusion block starting here\n");
|
|
}
|
|
if (state == 150) {
|
|
Seek(value, 0, SEEK_SET);
|
|
Swig_error(Getfile(s), Getline(value), "Missing %%enddef for macro starting here\n", Getline(value));
|
|
}
|
|
if ((state >= 105) && (state < 107)) {
|
|
Swig_error(Getfile(s), start_line, "Unterminated %%{ ... %%} block\n");
|
|
}
|
|
if ((state >= 30) && (state < 40)) {
|
|
Swig_error(Getfile(s), start_line, "Unterminated comment\n");
|
|
}
|
|
|
|
copy_location(s, chunk);
|
|
add_chunk(ns, chunk, allow);
|
|
|
|
/* DelScope(scp); */
|
|
Delete(decl);
|
|
Delete(id);
|
|
Delete(value);
|
|
Delete(comment);
|
|
Delete(chunk);
|
|
|
|
return ns;
|
|
}
|