aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2016-01-10 21:26:11 -0500
committerJustin M. Keyes <justinkz@gmail.com>2016-01-10 21:26:11 -0500
commit223aafb1a75c9ece9a2283a887b7539a3b771550 (patch)
tree039e4a402f1281eea651b197701d4c700ae7ea95 /src
parent095320a67da7b96824cd7495357b69e2a4fdb047 (diff)
parent3b7c4093e24fd413696e3f0aba9a7a1a470a8b4f (diff)
downloadrneovim-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.c25
-rw-r--r--src/nvim/strings.c48
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