From 7eff4e7c1d40b4f6666aa8b635f30d8f6771e88f Mon Sep 17 00:00:00 2001 From: Joshua Watt Date: Mon, 4 Dec 2017 09:23:06 -0600 Subject: [PATCH 1/3] Terminate options when passed via env var The C standard requires that argv be terminated with a NULL pointer (that is, argv[argc] == NULL). There are several places in the swig codebase that require this to check for missing arguments. However, SWIG_merge_envopt() was not keeping the NULL terminator, resulting in argument parsing failures (typically program aborts) when arguments were passed via the SWIG_FEATURES environment variable. --- Source/Modules/swigmain.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Modules/swigmain.cxx b/Source/Modules/swigmain.cxx index 397677fc5..b49fe909a 100644 --- a/Source/Modules/swigmain.cxx +++ b/Source/Modules/swigmain.cxx @@ -125,7 +125,7 @@ void SWIG_merge_envopt(const char *env, int oargc, char *oargv[], int *nargc, ch int argc = 1; int arge = oargc + 1024; - char **argv = (char **) malloc(sizeof(char *) * (arge)); + char **argv = (char **) malloc(sizeof(char *) * (arge + 1)); char *buffer = (char *) malloc(2048); char *b = buffer; char *be = b + 1023; @@ -147,6 +147,7 @@ void SWIG_merge_envopt(const char *env, int oargc, char *oargv[], int *nargc, ch for (int i = 1; (i < oargc) && (argc < arge); ++i, ++argc) { argv[argc] = oargv[i]; } + argv[argc] = NULL; *nargc = argc; *nargv = argv; From e9b4d3378b314e903db2dec6d6832245b8d61948 Mon Sep 17 00:00:00 2001 From: Joshua Watt Date: Tue, 28 Nov 2017 21:43:24 -0600 Subject: [PATCH 2/3] Add option file support Arguments may be passed using an option file prefixed with the '@' character. Processing of option files is the same as for the same feature in gcc. --- Source/Modules/main.cxx | 4 ++ Source/Modules/swigmain.cxx | 82 ++++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/Source/Modules/main.cxx b/Source/Modules/main.cxx index 9822b6af7..c7ae02625 100644 --- a/Source/Modules/main.cxx +++ b/Source/Modules/main.cxx @@ -159,6 +159,10 @@ is equivalent to: \n\ \n\ $ swig -Wall -python interface.i \n\ \n\ +Arguments may also be passed in a file, separated by whitespace. For example:\n\ +\n\ + $ echo \"-Wall -python interface.i\" > args.txt\n\ + $ swig @args.txt\n\ \n"; // Local variables diff --git a/Source/Modules/swigmain.cxx b/Source/Modules/swigmain.cxx index b49fe909a..f72525b6f 100644 --- a/Source/Modules/swigmain.cxx +++ b/Source/Modules/swigmain.cxx @@ -119,7 +119,8 @@ static swig_module modules[] = { void SWIG_merge_envopt(const char *env, int oargc, char *oargv[], int *nargc, char ***nargv) { if (!env) { *nargc = oargc; - *nargv = oargv; + *nargv = (char **)malloc(sizeof(char *) * (oargc + 1)); + memcpy(*nargv, oargv, sizeof(char *) * (oargc + 1)); return; } @@ -153,6 +154,84 @@ void SWIG_merge_envopt(const char *env, int oargc, char *oargv[], int *nargc, ch *nargv = argv; } +static void insert_option(int *argc, char ***argv, int index, char const *start, char const *end) { + int new_argc = *argc; + char **new_argv = *argv; + size_t option_len = end - start; + + // Preserve the NULL pointer at argv[argc] + new_argv = (char **)realloc(new_argv, (new_argc + 2) * sizeof(char *)); + memmove(&new_argv[index + 1], &new_argv[index], sizeof(char *) * (new_argc + 1 - index)); + new_argc++; + + new_argv[index] = (char *)malloc(option_len + 1); + memcpy(new_argv[index], start, option_len); + new_argv[index][option_len] = '\0'; + + *argc = new_argc; + *argv = new_argv; +} + +static void merge_options_files(int *argc, char ***argv) { + static const int BUFFER_SIZE = 4096; + char buffer[BUFFER_SIZE]; + int i; + int insert; + char **new_argv = *argv; + int new_argc = *argc; + FILE *f; + + i = 1; + while (i < new_argc) { + if (new_argv[i] && new_argv[i][0] == '@' && (f = fopen(&new_argv[i][1], "r"))) { + char c; + char *b; + char *be = &buffer[BUFFER_SIZE]; + int quote = 0; + bool escape = false; + + new_argc--; + memmove(&new_argv[i], &new_argv[i + 1], sizeof(char *) * (new_argc - i)); + insert = i; + b = buffer; + + while ((c = fgetc(f)) != EOF) { + if (escape) { + if (b != be) { + *b = c; + ++b; + } + escape = false; + } else if (c == '\\') { + escape = true; + } else if (!quote && (c == '\'' || c == '"')) { + quote = c; + } else if (quote && c == quote) { + quote = 0; + } else if (isspace(c) && !quote) { + if (b != buffer) { + insert_option(&new_argc, &new_argv, insert, buffer, b); + insert++; + + b = buffer; + } + } else if (b != be) { + *b = c; + ++b; + } + } + if (b != buffer) + insert_option(&new_argc, &new_argv, insert, buffer, b); + fclose(f); + } else { + ++i; + } + } + + *argv = new_argv; + *argc = new_argc; +} + int main(int margc, char **margv) { int i; Language *dl = 0; @@ -162,6 +241,7 @@ int main(int margc, char **margv) { char **argv; SWIG_merge_envopt(getenv("SWIG_FEATURES"), margc, margv, &argc, &argv); + merge_options_files(&argc, &argv); #ifdef MACSWIG SIOUXSettings.asktosaveonclose = false; From bc89aad59f4090bcd2b6cc1bb5f3acdbd56c5748 Mon Sep 17 00:00:00 2001 From: Joshua Watt Date: Wed, 29 Nov 2017 20:29:21 -0600 Subject: [PATCH 3/3] Add documentation for option files --- Doc/Manual/SWIG.html | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Doc/Manual/SWIG.html b/Doc/Manual/SWIG.html index f6a62d55d..ffc373f24 100644 --- a/Doc/Manual/SWIG.html +++ b/Doc/Manual/SWIG.html @@ -163,6 +163,32 @@ can be obtained by typing swig -help or swig +

+Arguments may also be passed in a command-line options file (also known as a +response file) which is useful if they exceed the system command line length +limit. To do this, put the arguments in a file, then provide the file name +prefixed with @ like so: +

+ +
+swig @file
+
+ +

+The options read from the file are inserted in place of the file option. If the +file does not exist, or cannot be read, then the option will be treated +literally and not removed. +

+ +

+Options in the file are separated by whitespace. A whitespace character may be +included in an option by surrounding the entire option in either single or +double quotes. Any character (including a backslash) may be included by +prefixing the character to be included with a backslash. The file may itself +contain additional @file options; any such options will be processed +recursively. +

+

5.1.1 Input format