mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge #6224 from justinmk/test-fish-backtick
test: backtick-expansion for `shell=fish`
This commit is contained in:
		| @@ -62,9 +62,7 @@ static int selinux_enabled = -1; | |||||||
|  |  | ||||||
|  |  | ||||||
| #if defined(HAVE_SELINUX) | #if defined(HAVE_SELINUX) | ||||||
| /* | // Copy security info from "from_file" to "to_file". | ||||||
|  * Copy security info from "from_file" to "to_file". |  | ||||||
|  */ |  | ||||||
| void mch_copy_sec(char_u *from_file, char_u *to_file) | void mch_copy_sec(char_u *from_file, char_u *to_file) | ||||||
| { | { | ||||||
|   if (from_file == NULL) |   if (from_file == NULL) | ||||||
| @@ -78,11 +76,12 @@ void mch_copy_sec(char_u *from_file, char_u *to_file) | |||||||
|     security_context_t to_context = NULL; |     security_context_t to_context = NULL; | ||||||
|  |  | ||||||
|     if (getfilecon((char *)from_file, &from_context) < 0) { |     if (getfilecon((char *)from_file, &from_context) < 0) { | ||||||
|       /* If the filesystem doesn't support extended attributes, |       // If the filesystem doesn't support extended attributes, | ||||||
|          the original had no special security context and the |       // the original had no special security context and the | ||||||
|          target cannot have one either.  */ |       // target cannot have one either. | ||||||
|       if (errno == EOPNOTSUPP) |       if (errno == EOPNOTSUPP) { | ||||||
|         return; |         return; | ||||||
|  |       } | ||||||
|  |  | ||||||
|       MSG_PUTS(_("\nCould not get security context for ")); |       MSG_PUTS(_("\nCould not get security context for ")); | ||||||
|       msg_outtrans(from_file); |       msg_outtrans(from_file); | ||||||
| @@ -107,21 +106,17 @@ void mch_copy_sec(char_u *from_file, char_u *to_file) | |||||||
|     freecon(from_context); |     freecon(from_context); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| #endif /* HAVE_SELINUX */ | #endif  // HAVE_SELINUX | ||||||
|  |  | ||||||
| /* | // Return a pointer to the ACL of file "fname" in allocated memory. | ||||||
|  * Return a pointer to the ACL of file "fname" in allocated memory. | // Return NULL if the ACL is not available for whatever reason. | ||||||
|  * Return NULL if the ACL is not available for whatever reason. |  | ||||||
|  */ |  | ||||||
| vim_acl_T mch_get_acl(char_u *fname) | vim_acl_T mch_get_acl(char_u *fname) | ||||||
| { | { | ||||||
|   vim_acl_T ret = NULL; |   vim_acl_T ret = NULL; | ||||||
|   return ret; |   return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | // Set the ACL of file "fname" to "acl" (unless it's NULL). | ||||||
|  * Set the ACL of file "fname" to "acl" (unless it's NULL). |  | ||||||
|  */ |  | ||||||
| void mch_set_acl(char_u *fname, vim_acl_T aclent) | void mch_set_acl(char_u *fname, vim_acl_T aclent) | ||||||
| { | { | ||||||
|   if (aclent == NULL) |   if (aclent == NULL) | ||||||
| @@ -141,7 +136,7 @@ void mch_exit(int r) | |||||||
|  |  | ||||||
|   ui_builtin_stop(); |   ui_builtin_stop(); | ||||||
|   ui_flush(); |   ui_flush(); | ||||||
|   ml_close_all(true);           /* remove all memfiles */ |   ml_close_all(true);           // remove all memfiles | ||||||
|  |  | ||||||
|   event_teardown(); |   event_teardown(); | ||||||
|   stream_set_blocking(input_global_fd(), true);  // normalize stream (#2598) |   stream_set_blocking(input_global_fd(), true);  // normalize stream (#2598) | ||||||
| @@ -198,17 +193,22 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, | |||||||
|   int check_spaces; |   int check_spaces; | ||||||
|   static bool did_find_nul = false; |   static bool did_find_nul = false; | ||||||
|   bool ampersent = false; |   bool ampersent = false; | ||||||
|   /* vimglob() function to define for Posix shell */ |   // vimglob() function to define for Posix shell | ||||||
|   static char *sh_vimglob_func = |   static char *sh_vimglob_func = | ||||||
|     "vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >"; |     "vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >"; | ||||||
|  |  | ||||||
|   *num_file = 0;        /* default: no files found */ |   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; |   *file = NULL; | ||||||
|  |  | ||||||
|   /* |   // If there are no wildcards, just copy the names to allocated memory. | ||||||
|    * 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. | ||||||
|    * Saves a lot of time, because we don't have to start a new shell. |  | ||||||
|    */ |  | ||||||
|   if (!have_wildcard(num_pat, pat)) { |   if (!have_wildcard(num_pat, pat)) { | ||||||
|     save_patterns(num_pat, pat, num_file, file); |     save_patterns(num_pat, pat, num_file, file); | ||||||
|     return OK; |     return OK; | ||||||
| @@ -219,87 +219,99 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, | |||||||
|     return FAIL; |     return FAIL; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* |   // Don't allow the use of backticks in secure and restricted mode. | ||||||
|    * Don't allow the use of backticks in secure and restricted mode. |   if (secure || restricted) { | ||||||
|    */ |     for (i = 0; i < num_pat; i++) { | ||||||
|   if (secure || restricted) |  | ||||||
|     for (i = 0; i < num_pat; ++i) |  | ||||||
|       if (vim_strchr(pat[i], '`') != NULL |       if (vim_strchr(pat[i], '`') != NULL | ||||||
|           && (check_restricted() || check_secure())) |           && (check_restricted() || check_secure())) { | ||||||
|         return FAIL; |         return FAIL; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   // get a name for the temp file | ||||||
|    * get a name for the temp file |  | ||||||
|    */ |  | ||||||
|   if ((tempname = vim_tempname()) == NULL) { |   if ((tempname = vim_tempname()) == NULL) { | ||||||
|     EMSG(_(e_notmp)); |     EMSG(_(e_notmp)); | ||||||
|     return FAIL; |     return FAIL; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* |   // Let the shell expand the patterns and write the result into the temp | ||||||
|    * Let the shell expand the patterns and write the result into the temp |   // file. | ||||||
|    * file. |   // STYLE_BT:         NL separated | ||||||
|    * STYLE_BT:	NL separated |   //       If expanding `cmd` execute it directly. | ||||||
|    *	    If expanding `cmd` execute it directly. |   // STYLE_GLOB:       NUL separated | ||||||
|    * STYLE_GLOB:	NUL separated |   //       If we use *csh, "glob" will work better than "echo". | ||||||
|    *	    If we use *csh, "glob" will work better than "echo". |   // STYLE_PRINT:      NL or NUL separated | ||||||
|    * STYLE_PRINT:	NL or NUL separated |   //       If we use *zsh, "print -N" will work better than "glob". | ||||||
|    *	    If we use *zsh, "print -N" will work better than "glob". |   // STYLE_VIMGLOB:    NL separated | ||||||
|    * STYLE_VIMGLOB:	NL separated |   //       If we use *sh*, we define "vimglob()". | ||||||
|    *	    If we use *sh*, we define "vimglob()". |   // STYLE_ECHO:       space separated. | ||||||
|    * STYLE_ECHO:	space separated. |   //       A shell we don't know, stay safe and use "echo". | ||||||
|    *	    A shell we don't know, stay safe and use "echo". |  | ||||||
|    */ |  | ||||||
|   if (num_pat == 1 && *pat[0] == '`' |   if (num_pat == 1 && *pat[0] == '`' | ||||||
|       && (len = STRLEN(pat[0])) > 2 |       && (len = STRLEN(pat[0])) > 2 | ||||||
|       && *(pat[0] + len - 1) == '`') |       && *(pat[0] + len - 1) == '`') { | ||||||
|     shell_style = STYLE_BT; |     shell_style = STYLE_BT; | ||||||
|   else if ((len = STRLEN(p_sh)) >= 3) { |   } else if ((len = STRLEN(p_sh)) >= 3) { | ||||||
|     if (STRCMP(p_sh + len - 3, "csh") == 0) |     if (STRCMP(p_sh + len - 3, "csh") == 0) { | ||||||
|       shell_style = STYLE_GLOB; |       shell_style = STYLE_GLOB; | ||||||
|     else if (STRCMP(p_sh + len - 3, "zsh") == 0) |     } else if (STRCMP(p_sh + len - 3, "zsh") == 0) { | ||||||
|       shell_style = STYLE_PRINT; |       shell_style = STYLE_PRINT; | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|   if (shell_style == STYLE_ECHO && strstr((char *)path_tail(p_sh), |   if (shell_style == STYLE_ECHO && strstr((char *)path_tail(p_sh), | ||||||
|           "sh") != NULL) |           "sh") != NULL) | ||||||
|     shell_style = STYLE_VIMGLOB; |     shell_style = STYLE_VIMGLOB; | ||||||
|  |  | ||||||
|   /* Compute the length of the command.  We need 2 extra bytes: for the |   // Compute the length of the command.  We need 2 extra bytes: for the | ||||||
|    * optional '&' and for the NUL. |   // optional '&' and for the NUL. | ||||||
|    * Worst case: "unset nonomatch; print -N >" plus two is 29 */ |   // Worst case: "unset nonomatch; print -N >" plus two is 29 | ||||||
|   len = STRLEN(tempname) + 29; |   len = STRLEN(tempname) + 29; | ||||||
|   if (shell_style == STYLE_VIMGLOB) |   if (shell_style == STYLE_VIMGLOB) | ||||||
|     len += STRLEN(sh_vimglob_func); |     len += STRLEN(sh_vimglob_func); | ||||||
|  |  | ||||||
|   for (i = 0; i < num_pat; ++i) { |   for (i = 0; i < num_pat; i++) { | ||||||
|     /* Count the length of the patterns in the same way as they are put in |     // Count the length of the patterns in the same way as they are put in | ||||||
|      * "command" below. */ |     // "command" below. | ||||||
|     ++len;                              /* add space */ |     len++;                              // add space | ||||||
|     for (j = 0; pat[i][j] != NUL; ++j) { |     for (j = 0; pat[i][j] != NUL; j++) { | ||||||
|       if (vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) |       if (vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) { | ||||||
|         ++len;                  /* may add a backslash */ |         len++;                  // may add a backslash | ||||||
|       ++len; |       } | ||||||
|  |       len++; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (is_fish_shell) { | ||||||
|  |     len += sizeof("egin;"" end") - 1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   command = xmalloc(len); |   command = xmalloc(len); | ||||||
|  |  | ||||||
|   /* |   // Build the shell command: | ||||||
|    * Build the shell command: |   // - Set $nonomatch depending on EW_NOTFOUND (hopefully the shell | ||||||
|    * - Set $nonomatch depending on EW_NOTFOUND (hopefully the shell |   //    recognizes this). | ||||||
|    *	 recognizes this). |   // - Add the shell command to print the expanded names. | ||||||
|    * - Add the shell command to print the expanded names. |   // - Add the temp file name. | ||||||
|    * - Add the temp file name. |   // - Add the file name patterns. | ||||||
|    * - Add the file name patterns. |  | ||||||
|    */ |  | ||||||
|   if (shell_style == STYLE_BT) { |   if (shell_style == STYLE_BT) { | ||||||
|     /* change `command; command& ` to (command; command ) */ |     // change `command; command& ` to (command; command ) | ||||||
|  |     if (is_fish_shell) { | ||||||
|  |       STRCPY(command, "begin; "); | ||||||
|  |     } else { | ||||||
|       STRCPY(command, "("); |       STRCPY(command, "("); | ||||||
|     STRCAT(command, pat[0] + 1);                /* exclude first backtick */ |     } | ||||||
|  |     STRCAT(command, pat[0] + 1);                // exclude first backtick | ||||||
|     p = command + STRLEN(command) - 1; |     p = command + STRLEN(command) - 1; | ||||||
|     *p-- = ')';                                 /* remove last backtick */ |     if (is_fish_shell) { | ||||||
|     while (p > command && ascii_iswhite(*p)) |       *p-- = ';'; | ||||||
|       --p; |       STRCAT(command, " end"); | ||||||
|     if (*p == '&') {                            /* remove trailing '&' */ |     } else { | ||||||
|  |       *p-- = ')';                                 // remove last backtick | ||||||
|  |     } | ||||||
|  |     while (p > command && ascii_iswhite(*p)) { | ||||||
|  |       p--; | ||||||
|  |     } | ||||||
|  |     if (*p == '&') {                            // remove trailing '&' | ||||||
|       ampersent = true; |       ampersent = true; | ||||||
|       *p = ' '; |       *p = ' '; | ||||||
|     } |     } | ||||||
| @@ -321,113 +333,109 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, | |||||||
|  |  | ||||||
|   STRCAT(command, tempname); |   STRCAT(command, tempname); | ||||||
|  |  | ||||||
|   if (shell_style != STYLE_BT) |   if (shell_style != STYLE_BT) { | ||||||
|     for (i = 0; i < num_pat; ++i) { |     for (i = 0; i < num_pat; i++) { | ||||||
|       /* Put a backslash before special |       // Put a backslash before special | ||||||
|        * characters, except inside ``. */ |       // characters, except inside ``. | ||||||
|       bool intick = false; |       bool intick = false; | ||||||
|  |  | ||||||
|       p = command + STRLEN(command); |       p = command + STRLEN(command); | ||||||
|       *p++ = ' '; |       *p++ = ' '; | ||||||
|       for (j = 0; pat[i][j] != NUL; ++j) { |       for (j = 0; pat[i][j] != NUL; j++) { | ||||||
|         if (pat[i][j] == '`') |         if (pat[i][j] == '`') { | ||||||
|           intick = !intick; |           intick = !intick; | ||||||
|         else if (pat[i][j] == '\\' && pat[i][j + 1] != NUL) { |         } else if (pat[i][j] == '\\' && pat[i][j + 1] != NUL) { | ||||||
|           /* Remove a backslash, take char literally.  But keep |           // Remove a backslash, take char literally.  But keep | ||||||
|            * backslash inside backticks, before a special character |           // backslash inside backticks, before a special character | ||||||
|            * and before a backtick. */ |           // and before a backtick. | ||||||
|           if (intick |           if (intick | ||||||
|               || vim_strchr(SHELL_SPECIAL, pat[i][j + 1]) != NULL |               || vim_strchr(SHELL_SPECIAL, pat[i][j + 1]) != NULL | ||||||
|               || pat[i][j + 1] == '`') |               || pat[i][j + 1] == '`') { | ||||||
|             *p++ = '\\'; |             *p++ = '\\'; | ||||||
|           ++j; |           } | ||||||
|  |           j++; | ||||||
|         } else if (!intick |         } else if (!intick | ||||||
|                    && ((flags & EW_KEEPDOLLAR) == 0 || pat[i][j] != '$') |                    && ((flags & EW_KEEPDOLLAR) == 0 || pat[i][j] != '$') | ||||||
|             && vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) |                    && vim_strchr(SHELL_SPECIAL, pat[i][j]) != NULL) { | ||||||
|           /* Put a backslash before a special character, but not |           // Put a backslash before a special character, but not | ||||||
|            * when inside ``. And not for $var when EW_KEEPDOLLAR is |           // when inside ``. And not for $var when EW_KEEPDOLLAR is | ||||||
|            * set. */ |           // set. | ||||||
|           *p++ = '\\'; |           *p++ = '\\'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         /* Copy one character. */ |         // Copy one character. | ||||||
|         *p++ = pat[i][j]; |         *p++ = pat[i][j]; | ||||||
|       } |       } | ||||||
|       *p = NUL; |       *p = NUL; | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (flags & EW_SILENT) { |   if (flags & EW_SILENT) { | ||||||
|     shellopts |= kShellOptHideMess; |     shellopts |= kShellOptHideMess; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (ampersent) |   if (ampersent) { | ||||||
|     STRCAT(command, "&");               /* put the '&' after the redirection */ |     STRCAT(command, "&");               // put the '&' after the redirection | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /* |   // Using zsh -G: If a pattern has no matches, it is just deleted from | ||||||
|    * 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 | ||||||
|    * the argument list, otherwise zsh gives an error message and doesn't |   // expand any other pattern. | ||||||
|    * expand any other pattern. |   if (shell_style == STYLE_PRINT) { | ||||||
|    */ |     extra_shell_arg = (char_u *)"-G";       // Use zsh NULL_GLOB option | ||||||
|   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. | ||||||
|    * 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 "$" | ||||||
|    * 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. | ||||||
|    * 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 | ||||||
|   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 | ||||||
|    * execute the shell command |  | ||||||
|    */ |  | ||||||
|   i = call_shell( |   i = call_shell( | ||||||
|       command, |       command, | ||||||
|       shellopts, |       shellopts, | ||||||
|       extra_shell_arg |       extra_shell_arg | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|   /* When running in the background, give it some time to create the temp |   // When running in the background, give it some time to create the temp | ||||||
|    * file, but don't wait for it to finish. */ |   // file, but don't wait for it to finish. | ||||||
|   if (ampersent) |   if (ampersent) { | ||||||
|     os_delay(10L, true); |     os_delay(10L, true); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   xfree(command); |   xfree(command); | ||||||
|  |  | ||||||
|   if (i) {                         /* os_call_shell() failed */ |   if (i) {                         // os_call_shell() failed | ||||||
|     os_remove((char *)tempname); |     os_remove((char *)tempname); | ||||||
|     xfree(tempname); |     xfree(tempname); | ||||||
|     /* |     // With interactive completion, the error message is not printed. | ||||||
|      * With interactive completion, the error message is not printed. |     if (!(flags & EW_SILENT)) { | ||||||
|      */ |       redraw_later_clear();             // probably messed up screen | ||||||
|     if (!(flags & EW_SILENT)) |       msg_putchar('\n');                // clear bottom line quickly | ||||||
|     { |  | ||||||
|       redraw_later_clear();             /* probably messed up screen */ |  | ||||||
|       msg_putchar('\n');                /* clear bottom line quickly */ |  | ||||||
| #if SIZEOF_LONG > SIZEOF_INT | #if SIZEOF_LONG > SIZEOF_INT | ||||||
|       assert(Rows <= (long)INT_MAX + 1); |       assert(Rows <= (long)INT_MAX + 1); | ||||||
| #endif | #endif | ||||||
|       cmdline_row = (int)(Rows - 1);           /* continue on last line */ |       cmdline_row = (int)(Rows - 1);           // continue on last line | ||||||
|       MSG(_(e_wildexpand)); |       MSG(_(e_wildexpand)); | ||||||
|       msg_start();                    /* don't overwrite this message */ |       msg_start();                    // don't overwrite this message | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If a `cmd` expansion failed, don't list `cmd` as a match, even when |     // If a `cmd` expansion failed, don't list `cmd` as a match, even when | ||||||
|      * EW_NOTFOUND is given */ |     // EW_NOTFOUND is given | ||||||
|     if (shell_style == STYLE_BT) |     if (shell_style == STYLE_BT) { | ||||||
|       return FAIL; |       return FAIL; | ||||||
|  |     } | ||||||
|     goto notfound; |     goto notfound; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* |   // read the names from the file into memory | ||||||
|    * read the names from the file into memory |  | ||||||
|    */ |  | ||||||
|   fd = fopen((char *)tempname, READBIN); |   fd = fopen((char *)tempname, READBIN); | ||||||
|   if (fd == NULL) { |   if (fd == NULL) { | ||||||
|     /* Something went wrong, perhaps a file name with a special char. */ |     // Something went wrong, perhaps a file name with a special char. | ||||||
|     if (!(flags & EW_SILENT)) { |     if (!(flags & EW_SILENT)) { | ||||||
|       MSG(_(e_wildexpand)); |       MSG(_(e_wildexpand)); | ||||||
|       msg_start();                      /* don't overwrite this message */ |       msg_start();                      // don't overwrite this message | ||||||
|     } |     } | ||||||
|     xfree(tempname); |     xfree(tempname); | ||||||
|     goto notfound; |     goto notfound; | ||||||
| @@ -438,7 +446,7 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, | |||||||
|     fclose(fd); |     fclose(fd); | ||||||
|     return FAIL; |     return FAIL; | ||||||
|   } |   } | ||||||
|   long long templen = ftell(fd);        /* get size of temp file */ |   int64_t templen = ftell(fd);        // get size of temp file | ||||||
|   if (templen < 0) { |   if (templen < 0) { | ||||||
|     xfree(tempname); |     xfree(tempname); | ||||||
|     fclose(fd); |     fclose(fd); | ||||||
| @@ -456,7 +464,7 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, | |||||||
|   fclose(fd); |   fclose(fd); | ||||||
|   os_remove((char *)tempname); |   os_remove((char *)tempname); | ||||||
|   if (readlen != len) { |   if (readlen != len) { | ||||||
|     /* unexpected read error */ |     // unexpected read error | ||||||
|     EMSG2(_(e_notread), tempname); |     EMSG2(_(e_notread), tempname); | ||||||
|     xfree(tempname); |     xfree(tempname); | ||||||
|     xfree(buffer); |     xfree(buffer); | ||||||
| @@ -464,41 +472,40 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, | |||||||
|   } |   } | ||||||
|   xfree(tempname); |   xfree(tempname); | ||||||
|  |  | ||||||
|   /* file names are separated with Space */ |   // file names are separated with Space | ||||||
|   if (shell_style == STYLE_ECHO) { |   if (shell_style == STYLE_ECHO) { | ||||||
|     buffer[len] = '\n';                 /* make sure the buffer ends in NL */ |     buffer[len] = '\n';                 // make sure the buffer ends in NL | ||||||
|     p = buffer; |     p = buffer; | ||||||
|     for (i = 0; *p != '\n'; ++i) {      /* count number of entries */ |     for (i = 0; *p != '\n'; i++) {      // count number of entries | ||||||
|       while (*p != ' ' && *p != '\n') |       while (*p != ' ' && *p != '\n') { | ||||||
|         ++p; |         p++; | ||||||
|       p = skipwhite(p);                 /* skip to next entry */ |  | ||||||
|       } |       } | ||||||
|  |       p = skipwhite(p);                 // skip to next entry | ||||||
|     } |     } | ||||||
|   /* file names are separated with NL */ |   // file names are separated with NL | ||||||
|   else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) { |   } else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) { | ||||||
|     buffer[len] = NUL;                  /* make sure the buffer ends in NUL */ |     buffer[len] = NUL;                  // make sure the buffer ends in NUL | ||||||
|     p = buffer; |     p = buffer; | ||||||
|     for (i = 0; *p != NUL; ++i) {       /* count number of entries */ |     for (i = 0; *p != NUL; i++) {       // count number of entries | ||||||
|       while (*p != '\n' && *p != NUL) |       while (*p != '\n' && *p != NUL) { | ||||||
|         ++p; |         p++; | ||||||
|       if (*p != NUL) |  | ||||||
|         ++p; |  | ||||||
|       p = skipwhite(p);                 /* skip leading white space */ |  | ||||||
|       } |       } | ||||||
|  |       if (*p != NUL) { | ||||||
|  |         p++; | ||||||
|       } |       } | ||||||
|   /* file names are separated with NUL */ |       p = skipwhite(p);                 // skip leading white space | ||||||
|   else { |     } | ||||||
|     /* |   // file names are separated with NUL | ||||||
|      * Some versions of zsh use spaces instead of NULs to separate |   } else { | ||||||
|      * results.  Only do this when there is no NUL before the end of the |     // Some versions of zsh use spaces instead of NULs to separate | ||||||
|      * buffer, otherwise we would never be able to use file names with |     // results.  Only do this when there is no NUL before the end of the | ||||||
|      * embedded spaces when zsh does use NULs. |     // buffer, otherwise we would never be able to use file names with | ||||||
|      * When we found a NUL once, we know zsh is OK, set did_find_nul and |     // embedded spaces when zsh does use NULs. | ||||||
|      * don't check for spaces again. |     // 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; |     check_spaces = false; | ||||||
|     if (shell_style == STYLE_PRINT && !did_find_nul) { |     if (shell_style == STYLE_PRINT && !did_find_nul) { | ||||||
|       /* If there is a NUL, set did_find_nul, else set check_spaces */ |       // If there is a NUL, set did_find_nul, else set check_spaces | ||||||
|       buffer[len] = NUL; |       buffer[len] = NUL; | ||||||
|       if (len && (int)STRLEN(buffer) < (int)len) |       if (len && (int)STRLEN(buffer) < (int)len) | ||||||
|         did_find_nul = true; |         did_find_nul = true; | ||||||
| @@ -506,72 +513,69 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, | |||||||
|         check_spaces = true; |         check_spaces = true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* |     // Make sure the buffer ends with a NUL.  For STYLE_PRINT there | ||||||
|      * Make sure the buffer ends with a NUL.  For STYLE_PRINT there |     // already is one, for STYLE_GLOB it needs to be added. | ||||||
|      * already is one, for STYLE_GLOB it needs to be added. |     if (len && buffer[len - 1] == NUL) { | ||||||
|      */ |       len--; | ||||||
|     if (len && buffer[len - 1] == NUL) |     } else { | ||||||
|       --len; |  | ||||||
|     else |  | ||||||
|       buffer[len] = NUL; |       buffer[len] = NUL; | ||||||
|  |     } | ||||||
|     i = 0; |     i = 0; | ||||||
|     for (p = buffer; p < buffer + len; ++p) |     for (p = buffer; p < buffer + len; p++) { | ||||||
|       if (*p == NUL || (*p == ' ' && check_spaces)) {       /* count entry */ |       if (*p == NUL || (*p == ' ' && check_spaces)) {       // count entry | ||||||
|         ++i; |         i++; | ||||||
|         *p = NUL; |         *p = NUL; | ||||||
|       } |       } | ||||||
|     if (len) |     } | ||||||
|       ++i;                              /* count last entry */ |     if (len) { | ||||||
|  |       i++;                              // count last entry | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|   assert(buffer[len] == NUL || buffer[len] == '\n'); |   assert(buffer[len] == NUL || buffer[len] == '\n'); | ||||||
|  |  | ||||||
|   if (i == 0) { |   if (i == 0) { | ||||||
|     /* |     // Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I". | ||||||
|      * 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 | ||||||
|      * /bin/sh will happily expand it to nothing rather than returning an |     // error; and hey, it's good to check anyway -- webb. | ||||||
|      * error; and hey, it's good to check anyway -- webb. |  | ||||||
|      */ |  | ||||||
|     xfree(buffer); |     xfree(buffer); | ||||||
|     goto notfound; |     goto notfound; | ||||||
|   } |   } | ||||||
|   *num_file = i; |   *num_file = i; | ||||||
|   *file = xmalloc(sizeof(char_u *) * (size_t)i); |   *file = xmalloc(sizeof(char_u *) * (size_t)i); | ||||||
|  |  | ||||||
|   /* |   // Isolate the individual file names. | ||||||
|    * Isolate the individual file names. |  | ||||||
|    */ |  | ||||||
|   p = buffer; |   p = buffer; | ||||||
|   for (i = 0; i < *num_file; ++i) { |   for (i = 0; i < *num_file; ++i) { | ||||||
|     (*file)[i] = p; |     (*file)[i] = p; | ||||||
|     /* Space or NL separates */ |     // Space or NL separates | ||||||
|     if (shell_style == STYLE_ECHO || shell_style == STYLE_BT |     if (shell_style == STYLE_ECHO || shell_style == STYLE_BT | ||||||
|         || shell_style == STYLE_VIMGLOB) { |         || shell_style == STYLE_VIMGLOB) { | ||||||
|       while (!(shell_style == STYLE_ECHO && *p == ' ') |       while (!(shell_style == STYLE_ECHO && *p == ' ') | ||||||
|              && *p != '\n' && *p != NUL) |              && *p != '\n' && *p != NUL) { | ||||||
|         ++p; |         p++; | ||||||
|       if (p == buffer + len)                    /* last entry */ |  | ||||||
|         *p = NUL; |  | ||||||
|       else { |  | ||||||
|         *p++ = NUL; |  | ||||||
|         p = skipwhite(p);                       /* skip to next entry */ |  | ||||||
|       } |       } | ||||||
|     } else {          /* NUL separates */ |       if (p == buffer + len) {                  // last entry | ||||||
|       while (*p && p < buffer + len)            /* skip entry */ |         *p = NUL; | ||||||
|         ++p; |       } else { | ||||||
|       ++p;                                      /* skip NUL */ |         *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. | ||||||
|    * Move the file names to allocated memory. |  | ||||||
|    */ |  | ||||||
|   for (j = 0, i = 0; i < *num_file; i++) { |   for (j = 0, i = 0; i < *num_file; i++) { | ||||||
|     // Require the files to exist. Helps when using /bin/sh |     // Require the files to exist. Helps when using /bin/sh | ||||||
|     if (!(flags & EW_NOTFOUND) && !os_path_exists((*file)[i])) { |     if (!(flags & EW_NOTFOUND) && !os_path_exists((*file)[i])) { | ||||||
|       continue; |       continue; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* check if this entry should be included */ |     // check if this entry should be included | ||||||
|     dir = (os_isdir((*file)[i])); |     dir = (os_isdir((*file)[i])); | ||||||
|     if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) |     if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) | ||||||
|       continue; |       continue; | ||||||
| @@ -584,14 +588,15 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, | |||||||
|  |  | ||||||
|     p = xmalloc(STRLEN((*file)[i]) + 1 + dir); |     p = xmalloc(STRLEN((*file)[i]) + 1 + dir); | ||||||
|     STRCPY(p, (*file)[i]); |     STRCPY(p, (*file)[i]); | ||||||
|     if (dir) |     if (dir) { | ||||||
|       add_pathsep((char *)p);             /* add '/' to a directory name */ |       add_pathsep((char *)p);             // add '/' to a directory name | ||||||
|  |     } | ||||||
|     (*file)[j++] = p; |     (*file)[j++] = p; | ||||||
|   } |   } | ||||||
|   xfree(buffer); |   xfree(buffer); | ||||||
|   *num_file = j; |   *num_file = j; | ||||||
|  |  | ||||||
|   if (*num_file == 0) {     /* rejected all entries */ |   if (*num_file == 0) {     // rejected all entries | ||||||
|     xfree(*file); |     xfree(*file); | ||||||
|     *file = NULL; |     *file = NULL; | ||||||
|     goto notfound; |     goto notfound; | ||||||
| @@ -619,8 +624,8 @@ static void save_patterns(int num_pat, char_u **pat, int *num_file, | |||||||
|  |  | ||||||
|   for (i = 0; i < num_pat; i++) { |   for (i = 0; i < num_pat; i++) { | ||||||
|     s = vim_strsave(pat[i]); |     s = vim_strsave(pat[i]); | ||||||
|     /* Be compatible with expand_filename(): halve the number of |     // Be compatible with expand_filename(): halve the number of | ||||||
|      * backslashes. */ |     // backslashes. | ||||||
|     backslash_halve(s); |     backslash_halve(s); | ||||||
|     (*file)[i] = s; |     (*file)[i] = s; | ||||||
|   } |   } | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								test/functional/eval/backtick_expansion_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								test/functional/eval/backtick_expansion_spec.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | local lfs = require('lfs') | ||||||
|  | local helpers = require('test.functional.helpers')(after_each) | ||||||
|  | local clear, command, eval, eq = helpers.clear, helpers.command, helpers.eval, helpers.eq | ||||||
|  | local write_file = helpers.write_file | ||||||
|  |  | ||||||
|  | describe("backtick expansion", function() | ||||||
|  |   setup(function() | ||||||
|  |     clear() | ||||||
|  |     lfs.mkdir("test-backticks") | ||||||
|  |     write_file("test-backticks/file1", "test file 1") | ||||||
|  |     write_file("test-backticks/file2", "test file 2") | ||||||
|  |     write_file("test-backticks/file3", "test file 3") | ||||||
|  |     lfs.mkdir("test-backticks/subdir") | ||||||
|  |     write_file("test-backticks/subdir/file4", "test file 4") | ||||||
|  |     -- Long path might cause "Press ENTER" prompt; use :silent to avoid it. | ||||||
|  |     command('silent cd test-backticks') | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   teardown(function() | ||||||
|  |     helpers.rmdir('test-backticks') | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   it("with default 'shell'", function() | ||||||
|  |     if helpers.pending_win32(pending) then return end  -- Need win32 shell fixes | ||||||
|  |     command(":silent args `echo ***2`") | ||||||
|  |     eq({ "file2", }, eval("argv()")) | ||||||
|  |     command(":silent args `echo */*4`") | ||||||
|  |     eq({ "subdir/file4", }, eval("argv()")) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   it("with shell=fish", function() | ||||||
|  |     if eval("executable('fish')") == 0 then | ||||||
|  |       pending('missing "fish" command') | ||||||
|  |       return | ||||||
|  |     end | ||||||
|  |     command("set shell=fish") | ||||||
|  |     command(":silent args `echo ***2`") | ||||||
|  |     eq({ "file2", }, eval("argv()")) | ||||||
|  |     command(":silent args `echo */*4`") | ||||||
|  |     eq({ "subdir/file4", }, eval("argv()")) | ||||||
|  |   end) | ||||||
|  | end) | ||||||
| @@ -198,10 +198,6 @@ describe('terminal buffer', function() | |||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   it('term_close() use-after-free #4393', function() |   it('term_close() use-after-free #4393', function() | ||||||
|     if eval("executable('yes')") == 0 then |  | ||||||
|       pending('missing "yes" command') |  | ||||||
|       return |  | ||||||
|     end |  | ||||||
|     execute('terminal yes') |     execute('terminal yes') | ||||||
|     feed([[<C-\><C-n>]]) |     feed([[<C-\><C-n>]]) | ||||||
|     execute('bdelete!') |     execute('bdelete!') | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes