From abcace7ee14a28e7f79329a41f5ae49533cbcd0d Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Wed, 29 Nov 2017 20:52:12 +0530 Subject: [PATCH 1/2] dependencies: Fix parsing of shebangs with spaces While finding an external program, we should only split the shebang once since that is what Linux and BSD also do. This is also why everyone uses #!/usr/bin/env in their shebangs since that allows you to run an interpreter in a path with spaces in it. See `man execve` for more details, specifically the sections for interpreter scripts. --- mesonbuild/dependencies/base.py | 6 +- .../input.txt | 1 + .../main.c | 79 +++++++++++++++++++ .../meson.build | 21 +++++ .../script.int.in | 2 + 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 test cases/common/167 external program shebang parsing/input.txt create mode 100644 test cases/common/167 external program shebang parsing/main.c create mode 100644 test cases/common/167 external program shebang parsing/meson.build create mode 100644 test cases/common/167 external program shebang parsing/script.int.in diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 682182c69..a72023275 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -560,7 +560,11 @@ class ExternalProgram: with open(script) as f: first_line = f.readline().strip() if first_line.startswith('#!'): - commands = first_line[2:].split('#')[0].strip().split() + # In a shebang, everything before the first space is assumed to + # be the command to run and everything after the first space is + # the single argument to pass to that command. So we must split + # exactly once. + commands = first_line[2:].split('#')[0].strip().split(maxsplit=1) if mesonlib.is_windows(): # Windows does not have UNIX paths so remove them, # but don't remove Windows paths diff --git a/test cases/common/167 external program shebang parsing/input.txt b/test cases/common/167 external program shebang parsing/input.txt new file mode 100644 index 000000000..40e30d4b2 --- /dev/null +++ b/test cases/common/167 external program shebang parsing/input.txt @@ -0,0 +1 @@ +some stuff here diff --git a/test cases/common/167 external program shebang parsing/main.c b/test cases/common/167 external program shebang parsing/main.c new file mode 100644 index 000000000..2ebe169ac --- /dev/null +++ b/test cases/common/167 external program shebang parsing/main.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#else + #include +#endif + +static int +intrp_copyfile (char * src, char * dest) +{ +#ifdef _WIN32 + if (!CopyFile (src, dest, FALSE)) + return 1; + return 0; +#else + return execlp ("cp", "copyfile", src, dest, NULL); +#endif +} + +static char* +parser_get_line (FILE * f) +{ + ssize_t size; + size_t n = 0; + char *line = NULL; + + size = getline (&line, &n, f); + if (size < 0) { + fprintf (stderr, "%s\n", strerror (errno)); + free (line); + return NULL; + } + return line; +} + +int +main (int argc, char * argv[]) +{ + FILE *f; + char *line = NULL; + + if (argc != 4) { + fprintf (stderr, "Invalid number of arguments: %i\n", argc); + goto err; + } + + if ((f = fopen (argv[1], "r")) == NULL) { + fprintf (stderr, "%s\n", strerror (errno)); + goto err; + } + + line = parser_get_line (f); + + if (!line || line[0] != '#' || line[1] != '!') { + fprintf (stderr, "Invalid script\n"); + goto err; + } + + free (line); + line = parser_get_line (f); + + if (!line || strncmp (line, "copy", 4) != 0) { + fprintf (stderr, "Syntax error\n"); + goto err; + } + + return intrp_copyfile (argv[2], argv[3]); + +err: + free (line); + return 1; +} diff --git a/test cases/common/167 external program shebang parsing/meson.build b/test cases/common/167 external program shebang parsing/meson.build new file mode 100644 index 000000000..c1cc5af9d --- /dev/null +++ b/test cases/common/167 external program shebang parsing/meson.build @@ -0,0 +1,21 @@ +project('shebang parsing', 'c') + +interpreter = executable('aninterp', 'main.c', native : true) + +cdata = configuration_data() +cdata.set('INTRP', interpreter.full_path()) + +f = configure_file(input : 'script.int.in', + output : 'script.int', + configuration : cdata) + +# Test that parsing a shebang with spaces works properly. See `man execve`, +# specifically the section on "Interpreter scripts" and the one under "NOTES". +script = find_program(f) + +custom_target('interpthis', + input : 'input.txt', + output : 'output.txt', + depends : interpreter, + command : [script, '@INPUT@', '@OUTPUT@'], + build_by_default : true) diff --git a/test cases/common/167 external program shebang parsing/script.int.in b/test cases/common/167 external program shebang parsing/script.int.in new file mode 100644 index 000000000..77ff90907 --- /dev/null +++ b/test cases/common/167 external program shebang parsing/script.int.in @@ -0,0 +1,2 @@ +#!/usr/bin/env @INTRP@ +copy From 12238725135f5494e9dc53830f4151d85d25f1eb Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Thu, 30 Nov 2017 12:20:22 +0530 Subject: [PATCH 2/2] tests/common/167: Port test interpreter to Windows Which means using fgets, unfortunately. --- .../main.c | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/test cases/common/167 external program shebang parsing/main.c b/test cases/common/167 external program shebang parsing/main.c index 2ebe169ac..a90206bc7 100644 --- a/test cases/common/167 external program shebang parsing/main.c +++ b/test cases/common/167 external program shebang parsing/main.c @@ -12,6 +12,9 @@ #include #endif +/* Who cares about stack sizes in test programs anyway */ +#define LINE_LENGTH 4096 + static int intrp_copyfile (char * src, char * dest) { @@ -24,27 +27,18 @@ intrp_copyfile (char * src, char * dest) #endif } -static char* -parser_get_line (FILE * f) +static void +parser_get_line (FILE * f, char line[LINE_LENGTH]) { - ssize_t size; - size_t n = 0; - char *line = NULL; - - size = getline (&line, &n, f); - if (size < 0) { + if (!fgets (line, LINE_LENGTH, f)) fprintf (stderr, "%s\n", strerror (errno)); - free (line); - return NULL; - } - return line; } int main (int argc, char * argv[]) { - FILE *f; - char *line = NULL; + FILE *f = NULL; + char line[LINE_LENGTH]; if (argc != 4) { fprintf (stderr, "Invalid number of arguments: %i\n", argc); @@ -56,24 +50,23 @@ main (int argc, char * argv[]) goto err; } - line = parser_get_line (f); + parser_get_line (f, line); if (!line || line[0] != '#' || line[1] != '!') { fprintf (stderr, "Invalid script\n"); goto err; } - free (line); - line = parser_get_line (f); + parser_get_line (f, line); if (!line || strncmp (line, "copy", 4) != 0) { - fprintf (stderr, "Syntax error\n"); + fprintf (stderr, "Syntax error: %s\n", line); goto err; } return intrp_copyfile (argv[2], argv[3]); err: - free (line); + fclose (f); return 1; }