| /* ----------------------------------------------------------------------------- | |
| * 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; | |
| } | |
| /* The locator comments that expand_macro() puts round a multiline macro expansion */ | |
| typedef enum { | |
| LOCATOR_NONE, /* Not a locator comment */ | |
| LOCATOR_START, /* The locator comment before the expansion */ | |
| LOCATOR_END /* The locator comment after the expansion */ | |
| } LocatorComment; | |
| /* ----------------------------------------------------------------------------- | |
| * locator_comment() | |
| * | |
| * Returns which locator comment, if any, the 'len' characters at 'comment' are. | |
| * ----------------------------------------------------------------------------- */ | |
| static LocatorComment locator_comment(const char *comment, long len) { | |
| if (len >= 10 && strncmp(comment, "/*@SWIG", 7) == 0 && strncmp(comment + len - 3, "@*/", 3) == 0) | |
| return comment[7] == '@' ? LOCATOR_END : LOCATOR_START; | |
| return LOCATOR_NONE; | |
| } | |
| /* ----------------------------------------------------------------------------- | |
| * 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). | |
| * | |
| * Comments are stripped too, except for the locator comments round a multiline | |
| * macro expanded in an argument, which keep the lines in the expansion numbered | |
| * from the macro definition. They are stripped from an argument holding a | |
| * locator comment without its partner, as when a ',' in an expansion splits the | |
| * arguments. If 'lines' is not NULL, it is set to the number of lines the | |
| * arguments span, less the lines between locator comments, which are lines in | |
| * the macro definition. | |
| * ----------------------------------------------------------------------------- */ | |
| static List *find_args(String *s, int ismacro, String *macro_name, int *lines) { | |
| List *args; | |
| String *str; | |
| String *str_nolocators; /* The argument without any locator comments */ | |
| int c, level; | |
| long pos; | |
| int start_line = Getline(s); | |
| int locator_level = 0; /* Nesting level of the locator comments */ | |
| int locator_line = 0; /* Line of the outermost start locator comment */ | |
| int locator_lines = 0; /* Number of lines within the locator comments */ | |
| /* 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); | |
| if (lines) | |
| *lines = 0; | |
| return 0; | |
| } | |
| c = Getc(s); | |
| /* Okay. This appears to be a macro so we will start isolating arguments */ | |
| while (c != EOF) { | |
| int arg_locator_level = 0; /* Locator comment nesting level in this argument */ | |
| int arg_locator_unbalanced = 0; /* Set if this argument has an unmatched end locator */ | |
| if (isspace(c)) { | |
| skip_whitespace(s, 0); /* Skip leading whitespace */ | |
| c = Getc(s); | |
| } | |
| str = NewStringEmpty(); | |
| copy_location(s, str); | |
| str_nolocators = NewStringEmpty(); | |
| copy_location(s, str_nolocators); | |
| level = 0; | |
| while (c != EOF) { | |
| if (c == '\"' || c == '\'') { | |
| int len = Len(str); | |
| Putc(c, str); | |
| skip_tochar(s, c, str); | |
| Append(str_nolocators, Char(str) + len); | |
| 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 == '*') { | |
| long start = Tell(s) - 2; | |
| LocatorComment locator; | |
| while ((c = Getc(s)) != EOF) { | |
| if (c == '*') { | |
| another_star: | |
| c = Getc(s); | |
| if (c == '/' || c == EOF) | |
| break; | |
| if (c == '*') | |
| goto another_star; | |
| } | |
| } | |
| locator = locator_comment(Char(s) + start, Tell(s) - start); | |
| if (locator != LOCATOR_NONE) { | |
| String *comment = NewStringWithSize(Char(s) + start, (int)(Tell(s) - start)); | |
| Append(str, comment); | |
| Delete(comment); | |
| if (locator == LOCATOR_START) { | |
| if (locator_level++ == 0) | |
| locator_line = Getline(s); | |
| arg_locator_level++; | |
| } else { | |
| if (locator_level > 0 && --locator_level == 0) | |
| locator_lines += Getline(s) - locator_line; | |
| if (arg_locator_level > 0) | |
| arg_locator_level--; | |
| else | |
| arg_locator_unbalanced = 1; | |
| } | |
| } | |
| 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); | |
| Putc(c, str_nolocators); | |
| if (c == '(') | |
| level++; | |
| if (c == ')') | |
| level--; | |
| c = Getc(s); | |
| } | |
| if (level > 0) { | |
| Delete(str); | |
| Delete(str_nolocators); | |
| break; | |
| } | |
| if (arg_locator_level > 0 || arg_locator_unbalanced) { | |
| /* An expansion is split between arguments */ | |
| Delete(str); | |
| str = str_nolocators; | |
| } else { | |
| Delete(str_nolocators); | |
| } | |
| Chop(str); | |
| Append(args, str); | |
| Delete(str); | |
| if (c == ')') | |
| break; | |
| c = Getc(s); | |
| } | |
| if (c != ')') { | |
| 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); | |
| } | |
| if (lines) { | |
| if (locator_level > 0) | |
| locator_lines += Getline(s) - locator_line; | |
| *lines = Getline(s) - start_line - locator_lines; | |
| assert(*lines >= 0); | |
| } | |
| 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, 0); | |
| } 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 */ | |
| args = find_args(s, 1, id, ¯o_additional_lines); | |
| } 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 */ | |
| args = find_args(line_file, 1, id, ¯o_additional_lines); | |
| } 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; | |
| } |