diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2020-02-02 21:46:51 -0800 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2020-02-02 22:14:40 -0800 |
commit | d1d5f5103e2225749f31aa918a17089082029eae (patch) | |
tree | badbc9981b12e6f993e568f594f1db1ddc7c5d6d /src/nvim/os_unix.c | |
parent | 983086f42e5841b84d5b24623a51ffc8c725e78f (diff) | |
download | rneovim-d1d5f5103e2225749f31aa918a17089082029eae.tar.gz rneovim-d1d5f5103e2225749f31aa918a17089082029eae.tar.bz2 rneovim-d1d5f5103e2225749f31aa918a17089082029eae.zip |
refactor: move various things to os/shell.c
- No code changes
- Rename mch_expand_wildcards => os_expand_wildcards
Diffstat (limited to 'src/nvim/os_unix.c')
-rw-r--r-- | src/nvim/os_unix.c | 506 |
1 files changed, 0 insertions, 506 deletions
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 0f44df2188..847e342ff1 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -1,13 +1,6 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -/* - * os_unix.c -- code for all flavors of Unix (BSD, SYSV, SVR4, POSIX, ...) - * - * A lot of this file was originally written by Juergen Weigert and later - * changed beyond recognition. - */ - #include <assert.h> #include <errno.h> #include <inttypes.h> @@ -104,502 +97,3 @@ void mch_exit(int r) exit(r); } - -#define SHELL_SPECIAL (char_u *)"\t \"&'$;<>()\\|" - -/// Does wildcard pattern matching using the shell. -/// -/// @param num_pat is the number of input patterns. -/// @param pat is an array of pointers to input patterns. -/// @param[out] num_file is pointer to number of matched file names. -/// Set to the number of pointers in *file. -/// @param[out] file is pointer to array of pointers to matched file names. -/// Memory pointed to by the initial value of *file will -/// not be freed. -/// Set to NULL if FAIL is returned. Otherwise points to -/// allocated memory. -/// @param flags is a combination of EW_* flags used in -/// expand_wildcards(). -/// If matching fails but EW_NOTFOUND is set in flags or -/// there are no wildcards, the patterns from pat are -/// copied into *file. -/// -/// @returns OK for success or FAIL for error. -int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, - char_u ***file, int flags) FUNC_ATTR_NONNULL_ARG(3) - FUNC_ATTR_NONNULL_ARG(4) -{ - int i; - size_t len; - char_u *p; - bool dir; - char_u *extra_shell_arg = NULL; - ShellOpts shellopts = kShellOptExpand | kShellOptSilent; - int j; - char_u *tempname; - char_u *command; - FILE *fd; - char_u *buffer; -#define STYLE_ECHO 0 /* use "echo", the default */ -#define STYLE_GLOB 1 /* use "glob", for csh */ -#define STYLE_VIMGLOB 2 /* use "vimglob", for Posix sh */ -#define STYLE_PRINT 3 /* use "print -N", for zsh */ -#define STYLE_BT 4 /* `cmd` expansion, execute the pattern - * directly */ - int shell_style = STYLE_ECHO; - int check_spaces; - static bool did_find_nul = false; - bool ampersent = false; - // vimglob() function to define for Posix shell - static char *sh_vimglob_func = - "vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >"; - - bool is_fish_shell = -#if defined(UNIX) - STRNCMP(invocation_path_tail(p_sh, NULL), "fish", 4) == 0; -#else - false; -#endif - - *num_file = 0; // default: no files found - *file = NULL; - - // If there are no wildcards, just copy the names to allocated memory. - // Saves a lot of time, because we don't have to start a new shell. - if (!have_wildcard(num_pat, pat)) { - save_patterns(num_pat, pat, num_file, file); - return OK; - } - - // Don't allow any shell command in the sandbox. - if (sandbox != 0 && check_secure()) { - return FAIL; - } - - // Don't allow the use of backticks in secure and restricted mode. - if (secure || restricted) { - for (i = 0; i < num_pat; i++) { - if (vim_strchr(pat[i], '`') != NULL - && (check_restricted() || check_secure())) { - return FAIL; - } - } - } - - // get a name for the temp file - if ((tempname = vim_tempname()) == NULL) { - EMSG(_(e_notmp)); - return FAIL; - } - - // Let the shell expand the patterns and write the result into the temp - // file. - // STYLE_BT: NL separated - // If expanding `cmd` execute it directly. - // STYLE_GLOB: NUL separated - // If we use *csh, "glob" will work better than "echo". - // STYLE_PRINT: NL or NUL separated - // If we use *zsh, "print -N" will work better than "glob". - // STYLE_VIMGLOB: NL separated - // If we use *sh*, we define "vimglob()". - // STYLE_ECHO: space separated. - // A shell we don't know, stay safe and use "echo". - if (num_pat == 1 && *pat[0] == '`' - && (len = STRLEN(pat[0])) > 2 - && *(pat[0] + len - 1) == '`') { - shell_style = STYLE_BT; - } else if ((len = STRLEN(p_sh)) >= 3) { - if (STRCMP(p_sh + len - 3, "csh") == 0) { - shell_style = STYLE_GLOB; - } else if (STRCMP(p_sh + len - 3, "zsh") == 0) { - shell_style = STYLE_PRINT; - } - } - if (shell_style == STYLE_ECHO && strstr((char *)path_tail(p_sh), - "sh") != NULL) - shell_style = STYLE_VIMGLOB; - - // Compute the length of the command. We need 2 extra bytes: for the - // optional '&' and for the NUL. - // Worst case: "unset nonomatch; print -N >" plus two is 29 - len = STRLEN(tempname) + 29; - if (shell_style == STYLE_VIMGLOB) - len += STRLEN(sh_vimglob_func); - - for (i = 0; i < num_pat; i++) { - // Count the length of the patterns in the same way as they are put in - // "command" below. - len++; // add space - for (j = 0; pat[i][j] != NUL; j++) { - if (vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) { - len++; // may add a backslash - } - len++; - } - } - - if (is_fish_shell) { - len += sizeof("egin;"" end") - 1; - } - - command = xmalloc(len); - - // Build the shell command: - // - Set $nonomatch depending on EW_NOTFOUND (hopefully the shell - // recognizes this). - // - Add the shell command to print the expanded names. - // - Add the temp file name. - // - Add the file name patterns. - if (shell_style == STYLE_BT) { - // change `command; command& ` to (command; command ) - if (is_fish_shell) { - STRCPY(command, "begin; "); - } else { - STRCPY(command, "("); - } - STRCAT(command, pat[0] + 1); // exclude first backtick - p = command + STRLEN(command) - 1; - if (is_fish_shell) { - *p-- = ';'; - STRCAT(command, " end"); - } else { - *p-- = ')'; // remove last backtick - } - while (p > command && ascii_iswhite(*p)) { - p--; - } - if (*p == '&') { // remove trailing '&' - ampersent = true; - *p = ' '; - } - STRCAT(command, ">"); - } else { - if (flags & EW_NOTFOUND) - STRCPY(command, "set nonomatch; "); - else - STRCPY(command, "unset nonomatch; "); - if (shell_style == STYLE_GLOB) - STRCAT(command, "glob >"); - else if (shell_style == STYLE_PRINT) - STRCAT(command, "print -N >"); - else if (shell_style == STYLE_VIMGLOB) - STRCAT(command, sh_vimglob_func); - else - STRCAT(command, "echo >"); - } - - STRCAT(command, tempname); - - if (shell_style != STYLE_BT) { - for (i = 0; i < num_pat; i++) { - // Put a backslash before special - // characters, except inside ``. - bool intick = false; - - p = command + STRLEN(command); - *p++ = ' '; - for (j = 0; pat[i][j] != NUL; j++) { - if (pat[i][j] == '`') { - intick = !intick; - } else if (pat[i][j] == '\\' && pat[i][j + 1] != NUL) { - // Remove a backslash, take char literally. But keep - // backslash inside backticks, before a special character - // and before a backtick. - if (intick - || vim_strchr(SHELL_SPECIAL, pat[i][j + 1]) != NULL - || pat[i][j + 1] == '`') { - *p++ = '\\'; - } - j++; - } else if (!intick - && ((flags & EW_KEEPDOLLAR) == 0 || pat[i][j] != '$') - && vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) { - // Put a backslash before a special character, but not - // when inside ``. And not for $var when EW_KEEPDOLLAR is - // set. - *p++ = '\\'; - } - - // Copy one character. - *p++ = pat[i][j]; - } - *p = NUL; - } - } - - if (flags & EW_SILENT) { - shellopts |= kShellOptHideMess; - } - - if (ampersent) { - STRCAT(command, "&"); // put the '&' after the redirection - } - - // Using zsh -G: If a pattern has no matches, it is just deleted from - // the argument list, otherwise zsh gives an error message and doesn't - // expand any other pattern. - if (shell_style == STYLE_PRINT) { - extra_shell_arg = (char_u *)"-G"; // Use zsh NULL_GLOB option - - // If we use -f then shell variables set in .cshrc won't get expanded. - // vi can do it, so we will too, but it is only necessary if there is a "$" - // in one of the patterns, otherwise we can still use the fast option. - } else if (shell_style == STYLE_GLOB && !have_dollars(num_pat, pat)) { - extra_shell_arg = (char_u *)"-f"; // Use csh fast option - } - - // execute the shell command - i = call_shell( - command, - shellopts, - extra_shell_arg - ); - - // When running in the background, give it some time to create the temp - // file, but don't wait for it to finish. - if (ampersent) { - os_delay(10L, true); - } - - xfree(command); - - if (i) { // os_call_shell() failed - os_remove((char *)tempname); - xfree(tempname); - // With interactive completion, the error message is not printed. - if (!(flags & EW_SILENT)) { - msg_putchar('\n'); // clear bottom line quickly - cmdline_row = Rows - 1; // continue on last line - MSG(_(e_wildexpand)); - msg_start(); // don't overwrite this message - } - - // If a `cmd` expansion failed, don't list `cmd` as a match, even when - // EW_NOTFOUND is given - if (shell_style == STYLE_BT) { - return FAIL; - } - goto notfound; - } - - // read the names from the file into memory - fd = fopen((char *)tempname, READBIN); - if (fd == NULL) { - // Something went wrong, perhaps a file name with a special char. - if (!(flags & EW_SILENT)) { - MSG(_(e_wildexpand)); - msg_start(); // don't overwrite this message - } - xfree(tempname); - goto notfound; - } - int fseek_res = fseek(fd, 0L, SEEK_END); - if (fseek_res < 0) { - xfree(tempname); - fclose(fd); - return FAIL; - } - int64_t templen = ftell(fd); // get size of temp file - if (templen < 0) { - xfree(tempname); - fclose(fd); - return FAIL; - } -#if SIZEOF_LONG_LONG > SIZEOF_SIZE_T - assert(templen <= (long long)SIZE_MAX); -#endif - len = (size_t)templen; - fseek(fd, 0L, SEEK_SET); - buffer = xmalloc(len + 1); - // fread() doesn't terminate buffer with NUL; - // appropriate termination (not always NUL) is done below. - size_t readlen = fread((char *)buffer, 1, len, fd); - fclose(fd); - os_remove((char *)tempname); - if (readlen != len) { - // unexpected read error - EMSG2(_(e_notread), tempname); - xfree(tempname); - xfree(buffer); - return FAIL; - } - xfree(tempname); - - // file names are separated with Space - if (shell_style == STYLE_ECHO) { - buffer[len] = '\n'; // make sure the buffer ends in NL - p = buffer; - for (i = 0; *p != '\n'; i++) { // count number of entries - while (*p != ' ' && *p != '\n') { - p++; - } - p = skipwhite(p); // skip to next entry - } - // file names are separated with NL - } else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) { - buffer[len] = NUL; // make sure the buffer ends in NUL - p = buffer; - for (i = 0; *p != NUL; i++) { // count number of entries - while (*p != '\n' && *p != NUL) { - p++; - } - if (*p != NUL) { - p++; - } - p = skipwhite(p); // skip leading white space - } - // file names are separated with NUL - } else { - // Some versions of zsh use spaces instead of NULs to separate - // results. Only do this when there is no NUL before the end of the - // buffer, otherwise we would never be able to use file names with - // embedded spaces when zsh does use NULs. - // When we found a NUL once, we know zsh is OK, set did_find_nul and - // don't check for spaces again. - check_spaces = false; - if (shell_style == STYLE_PRINT && !did_find_nul) { - // If there is a NUL, set did_find_nul, else set check_spaces - buffer[len] = NUL; - if (len && (int)STRLEN(buffer) < (int)len) - did_find_nul = true; - else - check_spaces = true; - } - - // Make sure the buffer ends with a NUL. For STYLE_PRINT there - // already is one, for STYLE_GLOB it needs to be added. - if (len && buffer[len - 1] == NUL) { - len--; - } else { - buffer[len] = NUL; - } - i = 0; - for (p = buffer; p < buffer + len; p++) { - if (*p == NUL || (*p == ' ' && check_spaces)) { // count entry - i++; - *p = NUL; - } - } - if (len) { - i++; // count last entry - } - } - assert(buffer[len] == NUL || buffer[len] == '\n'); - - if (i == 0) { - // Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I". - // /bin/sh will happily expand it to nothing rather than returning an - // error; and hey, it's good to check anyway -- webb. - xfree(buffer); - goto notfound; - } - *num_file = i; - *file = xmalloc(sizeof(char_u *) * (size_t)i); - - // Isolate the individual file names. - p = buffer; - for (i = 0; i < *num_file; ++i) { - (*file)[i] = p; - // Space or NL separates - if (shell_style == STYLE_ECHO || shell_style == STYLE_BT - || shell_style == STYLE_VIMGLOB) { - while (!(shell_style == STYLE_ECHO && *p == ' ') - && *p != '\n' && *p != NUL) { - p++; - } - if (p == buffer + len) { // last entry - *p = NUL; - } else { - *p++ = NUL; - p = skipwhite(p); // skip to next entry - } - } else { // NUL separates - while (*p && p < buffer + len) { // skip entry - p++; - } - p++; // skip NUL - } - } - - // Move the file names to allocated memory. - for (j = 0, i = 0; i < *num_file; i++) { - // Require the files to exist. Helps when using /bin/sh - if (!(flags & EW_NOTFOUND) && !os_path_exists((*file)[i])) { - continue; - } - - // check if this entry should be included - dir = (os_isdir((*file)[i])); - if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) - continue; - - // Skip files that are not executable if we check for that. - if (!dir && (flags & EW_EXEC) - && !os_can_exe((char *)(*file)[i], NULL, !(flags & EW_SHELLCMD))) { - continue; - } - - p = xmalloc(STRLEN((*file)[i]) + 1 + dir); - STRCPY(p, (*file)[i]); - if (dir) { - add_pathsep((char *)p); // add '/' to a directory name - } - (*file)[j++] = p; - } - xfree(buffer); - *num_file = j; - - if (*num_file == 0) { // rejected all entries - XFREE_CLEAR(*file); - goto notfound; - } - - return OK; - -notfound: - if (flags & EW_NOTFOUND) { - save_patterns(num_pat, pat, num_file, file); - return OK; - } - return FAIL; - -} - - -static void save_patterns(int num_pat, char_u **pat, int *num_file, - char_u ***file) -{ - int i; - char_u *s; - - *file = xmalloc((size_t)num_pat * sizeof(char_u *)); - - for (i = 0; i < num_pat; i++) { - s = vim_strsave(pat[i]); - // Be compatible with expand_filename(): halve the number of - // backslashes. - backslash_halve(s); - (*file)[i] = s; - } - *num_file = num_pat; -} - -static bool have_wildcard(int num, char_u **file) -{ - int i; - - for (i = 0; i < num; i++) - if (path_has_wildcard(file[i])) - return true; - return false; -} - -static bool have_dollars(int num, char_u **file) -{ - int i; - - for (i = 0; i < num; i++) - if (vim_strchr(file[i], '$') != NULL) - return true; - return false; -} |