comparison src/channel.c @ 12096:0a61213afdd2 v8.0.0928

patch 8.0.0928: MS-Windows: passing arglist to job has escaping problems commit https://github.com/vim/vim/commit/dcaa61384ca76e42f7feda5640fb85b58cee03e5 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Aug 13 17:13:09 2017 +0200 patch 8.0.0928: MS-Windows: passing arglist to job has escaping problems Problem: MS-Windows: passing arglist to job has escaping problems. Solution: Improve escaping. (Yasuhiro Matsumoto, closes https://github.com/vim/vim/issues/1954)
author Christian Brabandt <cb@256bit.org>
date Sun, 13 Aug 2017 17:15:03 +0200
parents f4e1e1e6886b
children 71e10b81226d
comparison
equal deleted inserted replaced
12095:975ec6e45168 12096:0a61213afdd2
4718 job_still_useful(job_T *job) 4718 job_still_useful(job_T *job)
4719 { 4719 {
4720 return job_need_end_check(job) || job_channel_still_useful(job); 4720 return job_need_end_check(job) || job_channel_still_useful(job);
4721 } 4721 }
4722 4722
4723 #if !defined(USE_ARGV) || defined(PROTO)
4724 /*
4725 * Escape one argument for an external command.
4726 * Returns the escaped string in allocated memory. NULL when out of memory.
4727 */
4728 static char_u *
4729 win32_escape_arg(char_u *arg)
4730 {
4731 int slen, dlen;
4732 int escaping = 0;
4733 int i;
4734 char_u *s, *d;
4735 char_u *escaped_arg;
4736 int has_spaces = FALSE;
4737
4738 /* First count the number of extra bytes required. */
4739 slen = STRLEN(arg);
4740 dlen = slen;
4741 for (s = arg; *s != NUL; MB_PTR_ADV(s))
4742 {
4743 if (*s == '"' || *s == '\\')
4744 ++dlen;
4745 if (*s == ' ' || *s == '\t')
4746 has_spaces = TRUE;
4747 }
4748
4749 if (has_spaces)
4750 dlen += 2;
4751
4752 if (dlen == slen)
4753 return vim_strsave(arg);
4754
4755 /* Allocate memory for the result and fill it. */
4756 escaped_arg = alloc(dlen + 1);
4757 if (escaped_arg == NULL)
4758 return NULL;
4759 memset(escaped_arg, 0, dlen+1);
4760
4761 d = escaped_arg;
4762
4763 if (has_spaces)
4764 *d++ = '"';
4765
4766 for (s = arg; *s != NUL;)
4767 {
4768 switch (*s)
4769 {
4770 case '"':
4771 for (i = 0; i < escaping; i++)
4772 *d++ = '\\';
4773 escaping = 0;
4774 *d++ = '\\';
4775 *d++ = *s++;
4776 break;
4777 case '\\':
4778 escaping++;
4779 *d++ = *s++;
4780 break;
4781 default:
4782 escaping = 0;
4783 MB_COPY_CHAR(s, d);
4784 break;
4785 }
4786 }
4787
4788 /* add terminating quote and finish with a NUL */
4789 if (has_spaces)
4790 {
4791 for (i = 0; i < escaping; i++)
4792 *d++ = '\\';
4793 *d++ = '"';
4794 }
4795 *d = NUL;
4796
4797 return escaped_arg;
4798 }
4799
4800 /*
4801 * Build a command line from a list, taking care of escaping.
4802 * The result is put in gap->ga_data.
4803 * Returns FAIL when out of memory.
4804 */
4805 int
4806 win32_build_cmd(list_T *l, garray_T *gap)
4807 {
4808 listitem_T *li;
4809 char_u *s;
4810
4811 for (li = l->lv_first; li != NULL; li = li->li_next)
4812 {
4813 s = get_tv_string_chk(&li->li_tv);
4814 if (s == NULL)
4815 return FAIL;
4816 s = win32_escape_arg(s);
4817 if (s == NULL)
4818 return FAIL;
4819 ga_concat(gap, s);
4820 vim_free(s);
4821 if (li->li_next != NULL)
4822 ga_append(gap, ' ');
4823 }
4824 return OK;
4825 }
4826 #endif
4827
4723 /* 4828 /*
4724 * NOTE: Must call job_cleanup() only once right after the status of "job" 4829 * NOTE: Must call job_cleanup() only once right after the status of "job"
4725 * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or 4830 * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or
4726 * mch_detect_ended_job() returned non-NULL). 4831 * mch_detect_ended_job() returned non-NULL).
4727 */ 4832 */
5091 goto theend; 5196 goto theend;
5092 } 5197 }
5093 else 5198 else
5094 { 5199 {
5095 list_T *l = argvars[0].vval.v_list; 5200 list_T *l = argvars[0].vval.v_list;
5201 #ifdef USE_ARGV
5096 listitem_T *li; 5202 listitem_T *li;
5097 char_u *s; 5203 char_u *s;
5098 5204
5099 #ifdef USE_ARGV
5100 /* Pass argv[] to mch_call_shell(). */ 5205 /* Pass argv[] to mch_call_shell(). */
5101 argv = (char **)alloc(sizeof(char *) * (l->lv_len + 1)); 5206 argv = (char **)alloc(sizeof(char *) * (l->lv_len + 1));
5102 if (argv == NULL) 5207 if (argv == NULL)
5103 goto theend; 5208 goto theend;
5104 #endif
5105 for (li = l->lv_first; li != NULL; li = li->li_next) 5209 for (li = l->lv_first; li != NULL; li = li->li_next)
5106 { 5210 {
5107 s = get_tv_string_chk(&li->li_tv); 5211 s = get_tv_string_chk(&li->li_tv);
5108 if (s == NULL) 5212 if (s == NULL)
5109 goto theend; 5213 goto theend;
5110 #ifdef USE_ARGV
5111 argv[argc++] = (char *)s; 5214 argv[argc++] = (char *)s;
5112 #else 5215 }
5113 /* Only escape when needed, double quotes are not always allowed. */
5114 if (li != l->lv_first && vim_strpbrk(s, (char_u *)" \t\"") != NULL)
5115 {
5116 # ifdef WIN32
5117 int old_ssl = p_ssl;
5118
5119 /* This is using CreateProcess, not cmd.exe. Always use
5120 * double quote and backslashes. */
5121 p_ssl = 0;
5122 # endif
5123 s = vim_strsave_shellescape(s, FALSE, TRUE);
5124 # ifdef WIN32
5125 p_ssl = old_ssl;
5126 # endif
5127 if (s == NULL)
5128 goto theend;
5129 ga_concat(&ga, s);
5130 vim_free(s);
5131 }
5132 else
5133 ga_concat(&ga, s);
5134 if (li->li_next != NULL)
5135 ga_append(&ga, ' ');
5136 #endif
5137 }
5138 #ifdef USE_ARGV
5139 argv[argc] = NULL; 5216 argv[argc] = NULL;
5140 #else 5217 #else
5218 if (win32_build_cmd(l, &ga) == FAIL)
5219 goto theend;
5141 cmd = ga.ga_data; 5220 cmd = ga.ga_data;
5142 #endif 5221 #endif
5143 } 5222 }
5144 5223
5145 #ifdef USE_ARGV 5224 #ifdef USE_ARGV