Upgrade libuv to c834d5de9e4747e5138bed9140320b44622ab6de
authorBert Belder <bertbelder@gmail.com>
Fri, 5 Aug 2011 07:15:04 +0000 (09:15 +0200)
committerBert Belder <bertbelder@gmail.com>
Fri, 5 Aug 2011 07:15:04 +0000 (09:15 +0200)
deps/uv/include/uv.h
deps/uv/src/win/process.c
deps/uv/test/test-list.h
deps/uv/test/test-spawn.c

index bb3e37c45d6467d5865d269f472a24fd17fbf966..2ee7c315f2b4af3b1a08bb3a14a20514ee48df0d 100644 (file)
@@ -504,6 +504,7 @@ typedef struct uv_process_options_s {
   char** args;
   char** env;
   char* cwd;
+  int windows_verbatim_arguments;
   /*
    * The user should supply pointers to initialized uv_pipe_t structs for
    * stdio. The user is reponsible for calling uv_close on them.
index 489c2a16c8654799d373e7f54918e3a0c23d55ff..7b4b562e85a963cf974612e06d09f53172df3b76 100644 (file)
@@ -75,48 +75,6 @@ static void uv_process_init(uv_process_t* handle) {
 }
 
 
-/*
- * Quotes command line arguments
- * Returns a pointer to the end (next char to be written) of the buffer
- */
-static wchar_t* quote_cmd_arg(wchar_t *source, wchar_t *target,
-    wchar_t terminator) {
-  int len = wcslen(source),
-      i;
-
-  // Check if the string must be quoted;
-  // if unnecessary, don't do it, it may only confuse older programs.
-  if (len == 0) {
-    goto quote;
-  }
-  for (i = 0; i < len; i++) {
-    if (source[i] == L' ' || source[i] == L'"') {
-      goto quote;
-    }
-  }
-
-  // No quotation needed
-  wcsncpy(target, source, len);
-  target += len;
-  *(target++) = terminator;
-  return target;
-
-quote:
-  // Quote
-  *(target++) = L'"';
-  for (i = 0; i < len; i++) {
-    if (source[i] == L'"' || source[i] == L'\\') {
-      *(target++) = '\\';
-    }
-    *(target++) = source[i];
-  }
-  *(target++) = L'"';
-  *(target++) = terminator;
-
-  return target;
-}
-
-
 /*
  * Path search functions
  */
@@ -403,8 +361,83 @@ static wchar_t* search_path(const wchar_t *file,
   return result;
 }
 
+/*
+ * Quotes command line arguments
+ * Returns a pointer to the end (next char to be written) of the buffer
+ */
+wchar_t* quote_cmd_arg(const wchar_t *source, wchar_t *target) {
+  int len = wcslen(source),
+      i, quote_hit;
+  wchar_t* start;
+
+  /* 
+   * Check if the string must be quoted;
+   * if unnecessary, don't do it, it may only confuse older programs.
+   */
+  if (len == 0) {
+    return target;
+  }
+
+  if (NULL == wcspbrk(source, L" \t\"")) {
+    /* No quotation needed */
+    wcsncpy(target, source, len);
+    target += len;
+    return target;
+  }
+
+  if (NULL == wcspbrk(source, L"\"\\")) {
+    /* 
+     * No embedded double quotes or backlashes, so I can just wrap
+     * quote marks around the whole thing.
+     */
+    *(target++) = L'"';
+    wcsncpy(target, source, len);
+    target += len;
+    *(target++) = L'"';
+    return target;
+  }
+
+  /*
+   * Expected intput/output:
+   *   input : hello"world
+   *   output: "hello\"world"
+   *   input : hello""world
+   *   output: "hello\"\"world"
+   *   input : hello\world
+   *   output: hello\world
+   *   input : hello\\world
+   *   output: hello\\world
+   *   input : hello\"world
+   *   output: "hello\\\"world"
+   *   input : hello\\"world
+   *   output: "hello\\\\\"world"
+   *   input : hello world\
+   *   output: "hello world\"
+   */
+
+  *(target++) = L'"';
+  start = target;
+  quote_hit = 1;
+
+  for (i = len; i > 0; --i) {
+    *(target++) = source[i - 1];
 
-static wchar_t* make_program_args(char** args) {
+    if (quote_hit && source[i - 1] == L'\\') {
+      *(target++) = L'\\';
+    } else if(source[i - 1] == L'"') {
+      quote_hit = 1;
+      *(target++) = L'\\';
+    } else {
+      quote_hit = 0;
+    }
+  }
+  target[0] = L'\0';
+  wcsrev(start);
+  *(target++) = L'"';
+  return target;
+}
+
+wchar_t* make_program_args(char** args, int verbatim_arguments) {
   wchar_t* dst;
   wchar_t* ptr;
   char** arg;
@@ -423,13 +456,9 @@ static wchar_t* make_program_args(char** args) {
     arg_count++;
   }
 
-  /* Adjust for potential quotes. */
-  size *= 2;
-
-  /* Arguments are separated with a space. */
-  if (arg_count > 0) {
-    size += arg_count - 1;
-  }
+  /* Adjust for potential quotes. Also assume the worst-case scenario 
+  /* that every character needs escaping, so we need twice as much space. */
+  size = size * 2 + arg_count * 2;
 
   dst = (wchar_t*)malloc(size);
   if (!dst) {
@@ -447,8 +476,13 @@ static wchar_t* make_program_args(char** args) {
     if (!len) {
       goto error;
     }
-
-    ptr = quote_cmd_arg(buffer, ptr, *(arg + 1) ? L' ' : L'\0');
+    if (verbatim_arguments) {
+      wcscpy(ptr, buffer);
+      ptr += len - 1;
+    } else {
+      ptr = quote_cmd_arg(buffer, ptr);
+    }
+    *ptr++ = *(arg + 1) ? L' ' : L'\0';
   }
 
   free(buffer);
@@ -743,7 +777,7 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) {
 
   process->exit_cb = options.exit_cb;
   UTF8_TO_UTF16(options.file, application);
-  arguments = options.args ? make_program_args(options.args) : NULL;
+  arguments = options.args ? make_program_args(options.args, options.windows_verbatim_arguments) : NULL;
   env = options.env ? make_program_env(options.env) : NULL;
 
   if (options.cwd) {
index ebfdeb655696af388574b40ef7ad0cc54b94035e..08ed92a2bdd39ca9d95ffcbb92831c81d0c83010 100644 (file)
@@ -69,6 +69,7 @@ TEST_DECLARE   (spawn_stdin)
 TEST_DECLARE   (spawn_and_kill)
 #ifdef _WIN32
 TEST_DECLARE   (spawn_detect_pipe_name_collisions_on_windows)
+TEST_DECLARE   (argument_escaping)
 #endif
 HELPER_DECLARE (tcp4_echo_server)
 HELPER_DECLARE (tcp6_echo_server)
@@ -153,6 +154,7 @@ TASK_LIST_START
   TEST_ENTRY  (spawn_and_kill)
 #ifdef _WIN32
   TEST_ENTRY  (spawn_detect_pipe_name_collisions_on_windows)
+  TEST_ENTRY  (argument_escaping)
 #endif
 
 #if 0
index 6378aab9811378094dcb2f0903fb51a34af45277..18a9bb7d97805d65d936a43a77854835bed8cd46 100644 (file)
@@ -269,4 +269,77 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) {
 
   return 0;
 }
+
+
+wchar_t* make_program_args(char** args, int verbatim_arguments);
+wchar_t* quote_cmd_arg(const wchar_t *source, wchar_t *target);
+
+TEST_IMPL(argument_escaping) {
+  const wchar_t* test_str[] = {
+    L"HelloWorld",
+    L"Hello World",
+    L"Hello\"World",
+    L"Hello World\\",
+    L"Hello\\\"World",
+    L"Hello\\World",
+    L"Hello\\\\World",
+    L"Hello World\\",
+    L"c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\""
+  };
+  const int count = sizeof(test_str) / sizeof(*test_str);
+  wchar_t** test_output;
+  wchar_t* command_line;
+  wchar_t** cracked;
+  size_t total_size = 0;
+  int i;
+  int num_args;
+
+  char* verbatim[] = {
+    "cmd.exe",
+    "/c",
+    "c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"",
+    NULL
+  };
+  wchar_t* verbatim_output;
+  wchar_t* non_verbatim_output;
+
+  test_output = calloc(count, sizeof(wchar_t*));
+  for (i = 0; i < count; ++i) {
+    test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(wchar_t));
+    quote_cmd_arg(test_str[i], test_output[i]);
+    wprintf(L"input : %s\n", test_str[i]);
+    wprintf(L"output: %s\n", test_output[i]);
+    total_size += wcslen(test_output[i]) + 1;
+  }
+  command_line = calloc(total_size + 1, sizeof(wchar_t));
+  for (i = 0; i < count; ++i) {
+    wcscat(command_line, test_output[i]);
+    wcscat(command_line, L" ");
+  }
+  command_line[total_size - 1] = L'\0';
+
+  wprintf(L"command_line: %s\n", command_line);
+
+  cracked = CommandLineToArgvW(command_line, &num_args);
+  for (i = 0; i < num_args; ++i) {
+    wprintf(L"%d: %s\t%s\n", i, test_str[i], cracked[i]);
+    ASSERT(wcscmp(test_str[i], cracked[i]) == 0);
+  }
+
+  LocalFree(cracked);
+  for (i = 0; i < count; ++i) {
+    free(test_output[i]);
+  }
+
+  verbatim_output = make_program_args(verbatim, 1);
+  non_verbatim_output = make_program_args(verbatim, 0);
+
+  wprintf(L"    verbatim_output: %s\n", verbatim_output);
+  wprintf(L"non_verbatim_output: %s\n", non_verbatim_output);
+
+  ASSERT(wcscmp(verbatim_output, L"cmd.exe /c c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"") == 0);
+  ASSERT(wcscmp(non_verbatim_output, L"cmd.exe /c \"c:\\path\\to\\node.exe --eval \\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"") == 0);
+
+  return 0;
+}
 #endif
\ No newline at end of file