From eff839b26df8b10b2334e2fbdaf81fbb3236b873 Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Fri, 5 Sep 2014 04:25:34 -0400 Subject: memory: xstrchrnul and xmemscan. --- src/nvim/memory.c | 29 +++++++++++++++++++++++++++++ src/nvim/message.c | 3 +-- src/nvim/ops.c | 7 +++---- src/nvim/os/fs.c | 5 +---- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/nvim/memory.c b/src/nvim/memory.c index f4dffb0bcd..3f9e8f1ada 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -222,6 +222,35 @@ void *xmemdupz(const void *data, size_t len) return memcpy(xmallocz(len), data, len); } +/// A version of strchr() that returns a pointer to the terminating NUL if it +/// doesn't find `c`. +/// +/// @param str The string to search. +/// @param c The char to look for. +/// @returns a pointer to the first instance of `c`, or to the NUL terminator +/// if not found. +char *xstrchrnul(const char *str, char c) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + char *p = strchr(str, c); + return p ? p : (char *)(str + strlen(str)); +} + +/// A version of memchr() that returns a pointer one past the end +/// if it doesn't find `c`. +/// +/// @param addr The address of the memory object. +/// @param c The char to look for. +/// @param size The size of the memory object. +/// @returns a pointer to the first instance of `c`, or one past the end if not +/// found. +void *xmemscan(const void *addr, char c, size_t size) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + char *p = memchr(addr, c, size); + return p ? p : (char *)addr + size; +} + /// The xstpcpy() function shall copy the string pointed to by src (including /// the terminating NUL character) into the array pointed to by dst. /// diff --git a/src/nvim/message.c b/src/nvim/message.c index ef0faa35ee..58dbee8cf9 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -3173,8 +3173,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) p = ""; while (*p != NUL) { if (*p != '%') { - char *q = strchr(p + 1, '%'); - size_t n = (q == NULL) ? STRLEN(p) : (size_t)(q - p); + size_t n = xstrchrnul(p + 1, '%') - p; /* Copy up to the next '%' or NUL without any changes. */ if (str_l < str_m) { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 9b98c84be4..2067d863eb 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -4903,10 +4903,9 @@ static void str_to_reg(struct yankreg *y_ptr, * Find the end of each line and save it into the array. */ for (start = 0; start < len + extraline; start += i + 1) { - for (i = start; i < len; ++i) /* find the end of the line */ - if (str[i] == '\n') - break; - i -= start; /* i is now length of line */ + // Let i represent the length of one line. + const char_u *p = str + start; + i = (char_u *)xmemscan(p, '\n', len - start) - p; if (i > maxlen) maxlen = i; if (append) { diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 07accb339a..36c2bb6d9b 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -134,10 +134,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) // Walk through all entries in $PATH to check if "name" exists there and // is an executable file. for (;; ) { - const char *e = strchr(path, ':'); - if (e == NULL) { - e = path + STRLEN(path); - } + const char *e = xstrchrnul(path, ':'); // Glue together the given directory from $PATH with name and save into // buf. -- cgit From 07bfc11448e98fa6a0e3d0218da6ffb2cb997930 Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Fri, 5 Sep 2014 11:49:09 -0400 Subject: memory: memchrsub and strchrsub --- src/nvim/memory.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 3f9e8f1ada..59edefec4a 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -1,5 +1,6 @@ // Various routines dealing with allocation and deallocation of memory. +#include #include #include #include @@ -251,6 +252,37 @@ void *xmemscan(const void *addr, char c, size_t size) return p ? p : (char *)addr + size; } +/// Replaces every instance of `c` with `x`. +/// +/// @warning Will read past `str + strlen(str)` if `c == NUL`. +/// +/// @param str A NUL-terminated string. +/// @param c The unwanted byte. +/// @param x The replacement. +void strchrsub(char *str, char c, char x) + FUNC_ATTR_NONNULL_ALL +{ + assert(c != '\0'); + while ((str = strchr(str, c))) { + *str++ = x; + } +} + +/// Replaces every instance of `c` with `x`. +/// +/// @param data An object in memory. May contain NULs. +/// @param c The unwanted byte. +/// @param x The replacement. +/// @param len The length of data. +void memchrsub(void *data, char c, char x, size_t len) + FUNC_ATTR_NONNULL_ALL +{ + char *p = data, *end = (char *)data + len; + while ((p = memchr(p, c, (size_t)(end - p)))) { + *p++ = x; + } +} + /// The xstpcpy() function shall copy the string pointed to by src (including /// the terminating NUL character) into the array pointed to by dst. /// -- cgit From d3cd3d2b8f84f87d9f6bc32d085fb5741771857b Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Thu, 4 Sep 2014 16:30:00 -0400 Subject: vim-patch:7.4.247 Problem: When passing input to system() there is no way to keep NUL and NL characters separate. Solution: Optionally use a list for the system() input. (ZyX) https://code.google.com/p/vim/source/detail?r=v7-4-247 Implement write_list_to_string() as well since write_list() only works on files. --- src/nvim/eval.c | 130 ++++++++++++++++++++++++++++++++++------------------- src/nvim/version.c | 2 +- 2 files changed, 86 insertions(+), 46 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0ae96365b2..80ae356ef7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14427,9 +14427,17 @@ static void f_system(typval_T *argvars, typval_T *rettv) // get input to the shell command (if any), and its length char_u buf[NUMBUFLEN]; - const char *input = (argvars[1].v_type != VAR_UNKNOWN) - ? (char *) get_tv_string_buf_chk(&argvars[1], buf): NULL; - size_t input_len = input ? strlen(input) : 0; + const char *input = NULL; + size_t input_len = 0; + if (argvars[1].v_type == VAR_LIST) { + input = write_list_to_string(argvars[1].vval.v_list, &input_len); + } else if (argvars[1].v_type != VAR_UNKNOWN) { + if ((input = (char *) get_tv_string_buf_chk(&argvars[1], buf))) { + input_len = strlen(input); + } else { + return; // Type error handled in get_tv_string_buf_chk(). + } + } // get shell command to execute const char *cmd = (char *) get_tv_string(&argvars[0]); @@ -14439,6 +14447,10 @@ static void f_system(typval_T *argvars, typval_T *rettv) char *res = NULL; int status = os_system(cmd, input, input_len, &res, &nread); + if (argvars[1].v_type == VAR_LIST) { + free(input); + } + set_vim_var_nr(VV_SHELL_ERROR, (long) status); #ifdef USE_CRNL @@ -15058,6 +15070,59 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv) dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL); } +/// Writes list of strings to file +static bool write_list(FILE *fd, list_T *list, bool binary) +{ + int ret = true; + + for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { + for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; ++s) { + if (putc(*s == '\n' ? NUL : *s, fd) == EOF) { + ret = false; + break; + } + } + if (!binary || li->li_next != NULL) { + if (putc('\n', fd) == EOF) { + ret = false; + break; + } + } + if (ret == false) { + EMSG(_(e_write)); + break; + } + } + return ret; +} + +/// Like write_list, but to a string with an out-parameter for the length and +/// always assumes binary. +static char *write_list_to_string(list_T *list, size_t *len) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +{ + // Calculate the resulting length. + *len = 0; + for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { + *len += STRLEN(get_tv_string(&li->li_tv)) + 1; + } + + char *ret = xmallocz(*len); + char *end = ret; + for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { + for (char *s = (char *) get_tv_string(&li->li_tv); *s != NUL; s++) { + *end++ = (*s == '\n') ? NUL : *s; + } + + if (li->li_next != NULL) { + *end++ = '\n'; + } + } + *end = NUL; + *len = end - ret; + return ret; +} + /* * "winwidth(nr)" function */ @@ -15072,68 +15137,43 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = wp->w_width; } -/* - * "writefile()" function - */ +/// "writefile()" function static void f_writefile(typval_T *argvars, typval_T *rettv) { - int binary = FALSE; - char_u *fname; - FILE *fd; - listitem_T *li; - char_u *s; - int ret = 0; - int c; + rettv->vval.v_number = 0; // Assuming success. - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "writefile()"); return; } - if (argvars[0].vval.v_list == NULL) + if (argvars[0].vval.v_list == NULL) { return; + } + bool binary = false; if (argvars[2].v_type != VAR_UNKNOWN - && STRCMP(get_tv_string(&argvars[2]), "b") == 0) - binary = TRUE; + && STRCMP(get_tv_string(&argvars[2]), "b") == 0) { + binary = true; + } - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[1]); + // Always open the file in binary mode, library functions have a mind of + // their own about CR-LF conversion. + char_u *fname = get_tv_string(&argvars[1]); + FILE *fd; if (*fname == NUL || (fd = mch_fopen((char *)fname, WRITEBIN)) == NULL) { EMSG2(_(e_notcreate), *fname == NUL ? (char_u *)_("") : fname); - ret = -1; + rettv->vval.v_number = -1; } else { - for (li = argvars[0].vval.v_list->lv_first; li != NULL; - li = li->li_next) { - for (s = get_tv_string(&li->li_tv); *s != NUL; ++s) { - if (*s == '\n') - c = putc(NUL, fd); - else - c = putc(*s, fd); - if (c == EOF) { - ret = -1; - break; - } - } - if (!binary || li->li_next != NULL) - if (putc('\n', fd) == EOF) { - ret = -1; - break; - } - if (ret < 0) { - EMSG(_(e_write)); - break; - } + if (write_list(fd, argvars[0].vval.v_list, binary) == false) { + rettv->vval.v_number = -1; } fclose(fd); } - - rettv->vval.v_number = ret; } - /* * "xor(expr, expr)" function */ diff --git a/src/nvim/version.c b/src/nvim/version.c index 1482ec4a5a..ab609bf492 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -394,7 +394,7 @@ static int included_patches[] = { //250 NA //249, //248, - //247, + 247, //246, 245, //244, -- cgit From 566ce93135c5762f5a2708dccf8a8b573f42438c Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Thu, 4 Sep 2014 23:44:24 -0400 Subject: vim-patch:7.4.248 Problem: Cannot distinguish between NL and NUL in output of system(). Solution: Add systemlist(). (ZyX) https://code.google.com/p/vim/source/detail?r=v7-4-248 --- src/nvim/eval.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++------- src/nvim/ex_cmds2.c | 4 ++-- src/nvim/misc1.c | 9 +++++++-- src/nvim/path.c | 2 +- src/nvim/version.c | 2 +- 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 80ae356ef7..8af7965dfc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6543,6 +6543,7 @@ static struct fst { {"synconcealed", 2, 2, f_synconcealed}, {"synstack", 2, 2, f_synstack}, {"system", 1, 2, f_system}, + {"systemlist", 1, 2, f_systemlist}, {"tabpagebuflist", 0, 1, f_tabpagebuflist}, {"tabpagenr", 0, 1, f_tabpagenr}, {"tabpagewinnr", 1, 2, f_tabpagewinnr}, @@ -9873,7 +9874,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) "spell", "syntax", #if !defined(UNIX) - "system", + "system", // TODO(SplinterOfChaos): This IS defined for UNIX! #endif "tag_binary", "tag_old_static", @@ -14415,8 +14416,8 @@ static void f_synstack(typval_T *argvars, typval_T *rettv) } } -/// f_system - the VimL system() function -static void f_system(typval_T *argvars, typval_T *rettv) +static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, + bool retlist) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -14453,9 +14454,41 @@ static void f_system(typval_T *argvars, typval_T *rettv) set_vim_var_nr(VV_SHELL_ERROR, (long) status); + if (res == NULL) { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + return; + } + + if (retlist) { + list_T *list = list_alloc(); + + // Copy each line to a list element using NL as the delimiter. + for (size_t i = 0; i < nread; i++) { + char_u *start = (char_u *) res + i; + size_t len = (char_u *) xmemscan(start, NL, nread - i) - start; + i += len; + + char_u *s = vim_strnsave(start, len); + for (size_t j = 0; j < len; j++) { + if (s[j] == NUL) { + s[j] = NL; + } + } + + listitem_T *li = listitem_alloc(); + li->li_tv.v_type = VAR_STRING; + li->li_tv.vval.v_string = s; + list_append(list, li); + } + + free(res); + + rettv->v_type = VAR_LIST; + rettv->vval.v_list = list; + } else { #ifdef USE_CRNL - // translate into - if (res != NULL) { + // translate into char *d = res; for (char *s = res; *s; ++s) { if (s[0] == CAR && s[1] == NL) { @@ -14466,12 +14499,23 @@ static void f_system(typval_T *argvars, typval_T *rettv) } *d = NUL; - } #endif + rettv->vval.v_string = (char_u *) res; + } +} - rettv->vval.v_string = (char_u *) res; +/// f_system - the VimL system() function +static void f_system(typval_T *argvars, typval_T *rettv) +{ + get_system_output_as_rettv(argvars, rettv, false); +} + +static void f_systemlist(typval_T *argvars, typval_T *rettv) +{ + get_system_output_as_rettv(argvars, rettv, true); } + /* * "tabpagebuflist()" function */ diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 0703c76b8a..056a101d47 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3181,8 +3181,8 @@ static char_u **find_locales(void) /* Find all available locales by running command "locale -a". If this * doesn't work we won't have completion. */ - char_u *locale_a = get_cmd_output((char_u *)"locale -a", - NULL, kShellOptSilent); + char_u *locale_a = get_cmd_output((char_u *)"locale -a", NULL, + kShellOptSilent, NULL); if (locale_a == NULL) return NULL; ga_init(&locales_ga, sizeof(char_u *), 20); diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 52780b9a57..f832fa25f2 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -3401,13 +3401,16 @@ void fast_breakcheck(void) /* * Get the stdout of an external command. + * If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not + * NULL store the length there. * Returns an allocated string, or NULL for error. */ char_u * get_cmd_output ( char_u *cmd, char_u *infile, /* optional input file name */ - int flags /* can be SHELL_SILENT */ + int flags, // can be kShellOptSilent + size_t *ret_len ) { char_u *tempname; @@ -3463,13 +3466,15 @@ get_cmd_output ( EMSG2(_(e_notread), tempname); free(buffer); buffer = NULL; - } else { + } else if (ret_len == NULL) { /* Change NUL into SOH, otherwise the string is truncated. */ for (i = 0; i < len; ++i) if (buffer[i] == NUL) buffer[i] = 1; buffer[len] = NUL; /* make sure the buffer is terminated */ + } else { + *ret_len = len; } done: diff --git a/src/nvim/path.c b/src/nvim/path.c index 6990a1817c..ff97b7774a 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1172,7 +1172,7 @@ expand_backtick ( buffer = eval_to_string(cmd + 1, &p, TRUE); else buffer = get_cmd_output(cmd, NULL, - (flags & EW_SILENT) ? kShellOptSilent : 0); + (flags & EW_SILENT) ? kShellOptSilent : 0, NULL); free(cmd); if (buffer == NULL) return 0; diff --git a/src/nvim/version.c b/src/nvim/version.c index ab609bf492..a453c0ee10 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -393,7 +393,7 @@ static int included_patches[] = { 251, //250 NA //249, - //248, + 248, 247, //246, 245, -- cgit From 78979427d14470e64eecb1e786bf21520305cf3d Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Fri, 5 Sep 2014 16:38:36 -0400 Subject: Return list from rettv_list_alloc. --- src/nvim/eval.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8af7965dfc..5968edb074 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4686,12 +4686,13 @@ list_T *list_alloc(void) FUNC_ATTR_NONNULL_RET /* * Allocate an empty list for a return value. */ -static void rettv_list_alloc(typval_T *rettv) +static list_T *rettv_list_alloc(typval_T *rettv) { list_T *l = list_alloc(); rettv->vval.v_list = l; rettv->v_type = VAR_LIST; ++l->lv_refcount; + return l; } /* @@ -9408,16 +9409,15 @@ static void f_getpid(typval_T *argvars, typval_T *rettv) static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) { pos_T *fp; - list_T *l; int fnum = -1; - rettv_list_alloc(rettv); - l = rettv->vval.v_list; if (getcurpos) { fp = &curwin->w_cursor; } else { fp = var2fpos(&argvars[0], true, &fnum); } + + list_T *l = rettv_list_alloc(rettv); list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0); list_append_number(l, @@ -12010,8 +12010,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv) EMSG(_(e_invrange)); else { vim_list_remove(l, item, item2); - rettv_list_alloc(rettv); - l = rettv->vval.v_list; + l = rettv_list_alloc(rettv); l->lv_first = item; l->lv_last = item2; item->li_prev = NULL; @@ -14656,8 +14655,7 @@ static void f_taglist(typval_T *argvars, typval_T *rettv) if (*tag_pattern == NUL) return; - rettv_list_alloc(rettv); - (void)get_tags(rettv->vval.v_list, tag_pattern); + (void)get_tags(rettv_list_alloc(rettv), tag_pattern); } /* -- cgit From b0bda2ee87a4fb0e57ef82d805a79b144ca510c5 Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Fri, 5 Sep 2014 16:41:07 -0400 Subject: vim-patch:7.4.256 Problem: Using systemlist() may cause a crash and does not handle NUL characters properly. Solution: Increase the reference count, allocate memory by length. (Yasuhiro Matsumoto) https://code.google.com/p/vim/source/detail?r=v7-4-256 --- src/nvim/eval.c | 14 ++++---------- src/nvim/version.c | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5968edb074..bfe39ced0c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14460,7 +14460,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } if (retlist) { - list_T *list = list_alloc(); + list_T *list = rettv_list_alloc(rettv); // Copy each line to a list element using NL as the delimiter. for (size_t i = 0; i < nread; i++) { @@ -14468,12 +14468,9 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, size_t len = (char_u *) xmemscan(start, NL, nread - i) - start; i += len; - char_u *s = vim_strnsave(start, len); - for (size_t j = 0; j < len; j++) { - if (s[j] == NUL) { - s[j] = NL; - } - } + // Don't use a str function to copy res as it may contains NULs. + char_u *s = xmemdupz(start, len); + memchrsub(s, NUL, NL, len); // Replace NUL with NL to avoid truncation. listitem_T *li = listitem_alloc(); li->li_tv.v_type = VAR_STRING; @@ -14482,9 +14479,6 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } free(res); - - rettv->v_type = VAR_LIST; - rettv->vval.v_list = list; } else { #ifdef USE_CRNL // translate into diff --git a/src/nvim/version.c b/src/nvim/version.c index a453c0ee10..47f4a2f805 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -385,7 +385,7 @@ static int included_patches[] = { //259 NA //258 NA //257 NA - //256, + 256, //255, //254, 253, -- cgit From 44b187dd012a3f5f01d58adbcbad4004e948e35a Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Fri, 12 Sep 2014 12:54:50 -0400 Subject: Old-style test for system(list). --- src/nvim/testdir/Makefile | 3 ++- src/nvim/testdir/test_system.in | Bin 0 -> 137 bytes src/nvim/testdir/test_system.ok | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 src/nvim/testdir/test_system.in create mode 100644 src/nvim/testdir/test_system.ok diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 9f04f880b5..26bf35aa94 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -35,7 +35,8 @@ SCRIPTS := test_autoformat_join.out \ test_listlbr.out test_listlbr_utf8.out \ test_changelist.out \ test_breakindent.out \ - test_insertcount.out + test_insertcount.out \ + test_systen.in SCRIPTS_GUI := test16.out diff --git a/src/nvim/testdir/test_system.in b/src/nvim/testdir/test_system.in new file mode 100644 index 0000000000..420465ce26 Binary files /dev/null and b/src/nvim/testdir/test_system.in differ diff --git a/src/nvim/testdir/test_system.ok b/src/nvim/testdir/test_system.ok new file mode 100644 index 0000000000..aa60536c3b --- /dev/null +++ b/src/nvim/testdir/test_system.ok @@ -0,0 +1,3 @@ + +abcd +['abcd'] -- cgit From 3928acb032b64941afa9b3e75fa82cf004fb243b Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Sun, 21 Sep 2014 22:39:04 -0400 Subject: Fix system() output truncation bug. Replace NULs with SOH to restore the old behaviour of get_cmd_output(). --- src/nvim/eval.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bfe39ced0c..235f7526c2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14480,6 +14480,9 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, free(res); } else { + // res may contain several NULs before the final terminating one. + // Replace them with SOH (1) like in get_cmd_output() to avoid truncation. + memchrsub(res, NUL, 1, nread); #ifdef USE_CRNL // translate into char *d = res; -- cgit From 01fc01e5d6d170715c5f00dab767f857ecec7c21 Mon Sep 17 00:00:00 2001 From: Scott Prager Date: Mon, 22 Sep 2014 05:17:03 -0400 Subject: Implement save_tv_as_string to assist in #1176. --- src/nvim/eval.c | 71 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 235f7526c2..114a6f8a59 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14426,17 +14426,10 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } // get input to the shell command (if any), and its length - char_u buf[NUMBUFLEN]; - const char *input = NULL; - size_t input_len = 0; - if (argvars[1].v_type == VAR_LIST) { - input = write_list_to_string(argvars[1].vval.v_list, &input_len); - } else if (argvars[1].v_type != VAR_UNKNOWN) { - if ((input = (char *) get_tv_string_buf_chk(&argvars[1], buf))) { - input_len = strlen(input); - } else { - return; // Type error handled in get_tv_string_buf_chk(). - } + ssize_t input_len; + char *input = (char *) save_tv_as_string(&argvars[1], &input_len); + if (input_len == -1) { + return; } // get shell command to execute @@ -14447,15 +14440,11 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, char *res = NULL; int status = os_system(cmd, input, input_len, &res, &nread); - if (argvars[1].v_type == VAR_LIST) { - free(input); - } + free(input); set_vim_var_nr(VV_SHELL_ERROR, (long) status); if (res == NULL) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; return; } @@ -15135,24 +15124,54 @@ static bool write_list(FILE *fd, list_T *list, bool binary) return ret; } -/// Like write_list, but to a string with an out-parameter for the length and -/// always assumes binary. -static char *write_list_to_string(list_T *list, size_t *len) - FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET -{ - // Calculate the resulting length. +/// Saves a typval_T as a string. +/// +/// For lists, replaces NLs with NUL and separates items with NLs. +/// +/// @param[in] tv A value to store as a string. +/// @param[out] len The length of the resulting string or -1 on error. +/// @returns an allocated string if `tv` represents a VimL string, list, or +/// number; NULL otherwise. +static char_u *save_tv_as_string(typval_T *tv, ssize_t *len) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL +{ + if (tv->v_type == VAR_UNKNOWN) { + *len = 0; + return NULL; + } + + // For types other than list, let get_tv_string_buf_chk() get the value or + // print an error. + if (tv->v_type != VAR_LIST) { + char_u *ret = get_tv_string_chk(tv); + if (ret && (*len = STRLEN(ret))) { + ret = vim_strsave(ret); + } else { + ret = NULL; + } + if (tv->v_type != VAR_STRING) { + *len = -1; + } + return ret; + } + + // Pre-calculate the resulting length. *len = 0; + list_T *list = tv->vval.v_list; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { *len += STRLEN(get_tv_string(&li->li_tv)) + 1; } - char *ret = xmallocz(*len); - char *end = ret; + if (*len == 0) { + return NULL; + } + + char_u *ret = xmalloc(*len); + char_u *end = ret; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - for (char *s = (char *) get_tv_string(&li->li_tv); *s != NUL; s++) { + for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } - if (li->li_next != NULL) { *end++ = '\n'; } -- cgit