diff options
author | robot-piglet <[email protected]> | 2025-08-28 14:27:58 +0300 |
---|---|---|
committer | robot-piglet <[email protected]> | 2025-08-28 14:57:06 +0300 |
commit | 81d828c32c8d5477cb2f0ce5da06a1a8d9392ca3 (patch) | |
tree | 3081d566f0d5158d76e9093261344f6406fd09f7 /contrib/tools/swig/Source/Preprocessor/cpp.c | |
parent | 77ea11423f959e51795cc3ef36a48d808b4ffb98 (diff) |
Intermediate changes
commit_hash:d5b1af16dbe9030537a04c27eb410c88c2f496cd
Diffstat (limited to 'contrib/tools/swig/Source/Preprocessor/cpp.c')
-rw-r--r-- | contrib/tools/swig/Source/Preprocessor/cpp.c | 2198 |
1 files changed, 2198 insertions, 0 deletions
diff --git a/contrib/tools/swig/Source/Preprocessor/cpp.c b/contrib/tools/swig/Source/Preprocessor/cpp.c new file mode 100644 index 00000000000..407ccd51546 --- /dev/null +++ b/contrib/tools/swig/Source/Preprocessor/cpp.c @@ -0,0 +1,2198 @@ +/* ----------------------------------------------------------------------------- + * 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 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 *, 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; +} + +/* Skip to a specified character or the end of the line */ +static int skip_tochar_or_eol(String *s, int ch, String *out) { + int c; + while ((c = Getc(s)) != EOF) { + if (c == '\n') { + Ungetc(c, s); + break; + } + 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_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_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_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. + * ----------------------------------------------------------------------------- */ + + +static String *Macro_vararg_name(const_String_or_char_ptr str, const_String_or_char_ptr line) { + String *varargname; + char *s, *dots; + + s = Char(str); + dots = strchr(s, '.'); + if (!dots) { + return NULL; + } + + if (strcmp(dots, "...") != 0) { + Swig_error(Getfile(line), Getline(line), "Illegal macro argument name '%s'\n", str); + return NULL; + } + if (dots == s) { + varargname = NewString("__VA_ARGS__"); + } else { + varargname = NewStringWithSize(s, (int)(dots - s)); + } + 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 (Replace(macrovalue, "\001 ", "\001", DOH_REPLACE_ANY) > 0) { } + while (Replace(macrovalue, " \001", "\001", DOH_REPLACE_ANY) > 0) { } + /* 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); + if (varargs) { + /* Replace '__VA_OPT__' with a special token */ + Replace(macrovalue, "__VA_OPT__", "\006", DOH_REPLACE_ID|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); +} + +/* ----------------------------------------------------------------------------- + * Preprocessor_defined() + * + * Check if a macro is defined. + * ----------------------------------------------------------------------------- */ +int Preprocessor_defined(const_String_or_char_ptr str) { + Hash *symbols; + assert(cpp); + symbols = Getattr(cpp, kpp_symbols); + return Getattr(symbols, str) != NULL; +} + +/* ----------------------------------------------------------------------------- + * 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 == '*') { +another_star: + c = Getc(s); + if (c == '/' || c == EOF) + break; + if (c == '*') goto another_star; + } + } + 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, NULL); + 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 - global context, 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, NULL); + aname = Getitem(margs, i); /* Get macro argument name */ + if (strchr(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 (isvarargs && i == l - 1) { + char *s = Char(ns); + char *a = s; + while ((a = strchr(a, '\006')) != NULL) { + *a = ' '; + while (isspace((unsigned char)*++a)) { } + if (*a == '(') { + char *e = a; + int depth = 1; + while (*++e) { + if (*e == ')') { + if (--depth == 0) break; + } else if (*e == '(') { + ++depth; + } + } + if (*e) { + if (Len(arg) == 0) { + // Empty varargs so replace ( and ) and everything between with + // spaces. + memset(a, ' ', e - a + 1); + } else { + // Non-empty varargs so replace ( and ) with spaces. + *a = ' '; + *e = ' '; + } + } + a = e + 1; + } + } + } + + if (strchr(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); + if (isvarargs && i == l - 1 && Len(arg) == 0) { + // ## followed by a zero length varargs macro argument - if preceded + // by a comma (possibly with whitespace in between) we nuke the + // comma. + char *s = Char(ns); + char *a = s + 1; + while ((a = strstr(a, "\002\003")) != NULL) { + char *t = a; + while (--t >= s) { + if (!isspace((unsigned char)*t)) { + if (*t == ',') *t = ' '; + break; + } + } + // Advance 3 since we're only interested in \002\003 when it could + // be preceded by a comma. + a += 3; + } + } + + 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 (strchr(Char(ns), '\004')) { + String *marg = Swig_name_mangle_string(arg); + Clear(temp); + Printf(temp, "\004%s", aname); + Replace(ns, temp, marg, DOH_REPLACE_ID_END); + Delete(marg); + } + if (strchr(Char(ns), '\005')) { + String *marg = Swig_name_mangle_string(arg); + Clear(temp); + Clear(tempa); + Printf(temp, "\005%s", aname); + Printf(tempa, "\"%s\"", marg); + Replace(ns, temp, tempa, DOH_REPLACE_ID_END); + Delete(marg); + } + + /* 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, line_file); + + 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, DOH *line_file) + * + * 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 *line_file) { + 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("#ifdef", id) || Equal("#ifndef", id)) { + /* Evaluate the defined-ness check and substitute `#if 0` or `#if 1`. */ + int allow = 0; + skip_whitespace(s, 0); + c = Getc(s); + if (isidchar(c)) { + DOH *arg = NewStringEmpty(); + Putc(c, arg); + while (((c = Getc(s)) != EOF)) { + if (!isidchar(c)) { + Ungetc(c, s); + break; + } + Putc(c, arg); + } + if (Equal("#ifdef", id)) { + if (Getattr(symbols, arg)) allow = 1; + } else { + if (!Getattr(symbols, arg)) allow = 1; + } + Delete(arg); + } else { + Ungetc(c, s); + Swig_error(Getfile(s), Getline(s), "Missing identifier for %s.\n", id); + } + if (allow) { + Append(ns, "#if 1"); + } else { + Append(ns, "#if 0"); + } + } 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 ((m = Getattr(symbols, id))) { + /* Yes. There is a macro here */ + /* If it expects arguments, they must come from `line_file` */ + DOH *args = 0; + DOH *e; + int macro_additional_lines = 0; + /* See if the macro expects arguments */ + if (Getattr(m, kpp_args) && line_file) { + /* Yep. We need to go find the arguments and do a substitution */ + int line = Getline(line_file); + args = find_args(line_file, 1, id); + macro_additional_lines = Getline(line_file) - 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); + } + } + 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 { + int len = Len(s2); + for (int i = 0; i < len; ++i) { + if (Char(s2)[i] == '\n') + Putc('\n', s1); + } + } +} + +static void add_chunk(DOH *ns, DOH *chunk, int allow) { + DOH *echunk; + Seek(chunk, 0, SEEK_SET); + if (allow) { + echunk = Preprocessor_replace(chunk, NULL); + 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 */ +state1: + 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); + if (c == '/') + state = 31; + else if (c == '*') + state = 32; + else { + state = 1; + goto state1; /* Process this character the same as if it wasn't preceded by a `/`. */ + } + Putc(c, chunk); + 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_or_eol(s, '\"', value); + } else if (c == '\'') { + Putc(c, value); + skip_tochar_or_eol(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, NULL); + 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, NULL); + 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, NULL); + 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, NULL); + 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)) { + /* 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; + + 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; +} |