diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2016-01-10 21:26:11 -0500 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2016-01-10 21:26:11 -0500 |
commit | 223aafb1a75c9ece9a2283a887b7539a3b771550 (patch) | |
tree | 039e4a402f1281eea651b197701d4c700ae7ea95 /src | |
parent | 095320a67da7b96824cd7495357b69e2a4fdb047 (diff) | |
parent | 3b7c4093e24fd413696e3f0aba9a7a1a470a8b4f (diff) | |
download | rneovim-223aafb1a75c9ece9a2283a887b7539a3b771550.tar.gz rneovim-223aafb1a75c9ece9a2283a887b7539a3b771550.tar.bz2 rneovim-223aafb1a75c9ece9a2283a887b7539a3b771550.zip |
Merge pull request #3980 from ZyX-I/shell-unquote
shell: Unquote &shell* options before using them
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/os/shell.c | 25 | ||||
-rw-r--r-- | src/nvim/strings.c | 48 |
2 files changed, 61 insertions, 12 deletions
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 3813c45726..f5a1637c94 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -36,7 +36,6 @@ typedef struct { # include "os/shell.c.generated.h" #endif - /// Builds the argument vector for running the user-configured 'shell' (p_sh) /// with an optional command prefixed by 'shellcmdflag' (p_shcf). /// @@ -45,9 +44,10 @@ typedef struct { /// @return A newly allocated argument vector. It must be freed with /// `shell_free_argv` when no longer needed. char **shell_build_argv(const char *cmd, const char *extra_args) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { - size_t argc = tokenize(p_sh, NULL) + tokenize(p_shcf, NULL); - char **rv = xmalloc((unsigned)((argc + 4) * sizeof(char *))); + size_t argc = tokenize(p_sh, NULL) + (cmd ? tokenize(p_shcf, NULL) : 0); + char **rv = xmalloc((argc + 4) * sizeof(*rv)); // Split 'shell' size_t i = tokenize(p_sh, rv); @@ -338,24 +338,22 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, /// @param argv The vector that will be filled with copies of the parsed /// words. It can be NULL if the caller only needs to count words. /// @return The number of words parsed. -static size_t tokenize(const char_u *str, char **argv) +static size_t tokenize(const char_u *const str, char **const argv) + FUNC_ATTR_NONNULL_ARG(1) { - size_t argc = 0, len; - char_u *p = (char_u *) str; + size_t argc = 0; + const char *p = (const char *) str; while (*p != NUL) { - len = word_length(p); + const size_t len = word_length((const char_u *) p); if (argv != NULL) { // Fill the slot - argv[argc] = xmalloc(len + 1); - memcpy(argv[argc], p, len); - argv[argc][len] = NUL; + argv[argc] = vim_strnsave_unquoted(p, len); } argc++; - p += len; - p = skipwhite(p); + p = (const char *) skipwhite((char_u *) (p + len)); } return argc; @@ -377,6 +375,9 @@ static size_t word_length(const char_u *str) if (*p == '"') { // Found a quote character, switch the `inquote` flag inquote = !inquote; + } else if (*p == '\\' && inquote) { + p++; + length++; } p++; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 00dcf3cf46..fe91141375 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -119,6 +119,54 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, return escaped_string; } +/// Save a copy of an unquoted string +/// +/// Turns string like `a\bc"def\"ghi\\\n"jkl` into `a\bcdef"ghi\\njkl`, for use +/// in shell_build_argv: the only purpose of backslash is making next character +/// be treated literally inside the double quotes, if this character is +/// backslash or quote. +/// +/// @param[in] string String to copy. +/// @param[in] length Length of the string to copy. +/// +/// @return [allocated] Copy of the string. +char *vim_strnsave_unquoted(const char *const string, const size_t length) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET +{ +#define ESCAPE_COND(p, inquote, string_end) \ + (*p == '\\' && inquote && p + 1 < string_end && (p[1] == '\\' || p[1] == '"')) + size_t ret_length = 0; + bool inquote = false; + const char *const string_end = string + length; + for (const char *p = string; p < string_end; p++) { + if (*p == '"') { + inquote = !inquote; + } else if (ESCAPE_COND(p, inquote, string_end)) { + ret_length++; + p++; + } else { + ret_length++; + } + } + + char *const ret = xmallocz(ret_length); + char *rp = ret; + inquote = false; + for (const char *p = string; p < string_end; p++) { + if (*p == '"') { + inquote = !inquote; + } else if (ESCAPE_COND(p, inquote, string_end)) { + *rp++ = *(++p); + } else { + *rp++ = *p; + } + } +#undef ESCAPE_COND + + return ret; +} + /* * Escape "string" for use as a shell argument with system(). * This uses single quotes, except when we know we need to use double quotes |