patch 8.0.0902: cannot specify directory or environment for a job
Problem: Cannot specify directory or environment for a job.
Solution: Add the "cwd" and "env" arguments to job options. (Yasuhiro
Matsumoto, closes #1160)
diff --git a/src/os_win32.c b/src/os_win32.c
index 9dc039b..f98bd7e 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -3981,31 +3981,46 @@
BOOL inherit_handles,
DWORD flags,
STARTUPINFO *si,
- PROCESS_INFORMATION *pi)
+ PROCESS_INFORMATION *pi,
+ LPVOID *env,
+ char *cwd)
{
#ifdef FEAT_MBYTE
if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
{
- WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
+ BOOL ret;
+ WCHAR *wcmd, *wcwd = NULL;
- if (wcmd != NULL)
+ wcmd = enc_to_utf16((char_u *)cmd, NULL);
+ if (wcmd == NULL)
+ goto fallback;
+ if (cwd != NULL)
{
- BOOL ret;
- ret = CreateProcessW(
- NULL, /* Executable name */
- wcmd, /* Command to execute */
- NULL, /* Process security attributes */
- NULL, /* Thread security attributes */
- inherit_handles, /* Inherit handles */
- flags, /* Creation flags */
- NULL, /* Environment */
- NULL, /* Current directory */
- (LPSTARTUPINFOW)si, /* Startup information */
- pi); /* Process information */
- vim_free(wcmd);
- return ret;
+ wcwd = enc_to_utf16((char_u *)cwd, NULL);
+ if (wcwd == NULL)
+ {
+ vim_free(wcmd);
+ goto fallback;
+ }
}
+
+ ret = CreateProcessW(
+ NULL, /* Executable name */
+ wcmd, /* Command to execute */
+ NULL, /* Process security attributes */
+ NULL, /* Thread security attributes */
+ inherit_handles, /* Inherit handles */
+ flags, /* Creation flags */
+ env, /* Environment */
+ wcwd, /* Current directory */
+ (LPSTARTUPINFOW)si, /* Startup information */
+ pi); /* Process information */
+ vim_free(wcmd);
+ if (wcwd != NULL)
+ vim_free(wcwd);
+ return ret;
}
+fallback:
#endif
return CreateProcess(
NULL, /* Executable name */
@@ -4014,8 +4029,8 @@
NULL, /* Thread security attributes */
inherit_handles, /* Inherit handles */
flags, /* Creation flags */
- NULL, /* Environment */
- NULL, /* Current directory */
+ env, /* Environment */
+ cwd, /* Current directory */
si, /* Startup information */
pi); /* Process information */
}
@@ -4079,7 +4094,8 @@
/* Now, run the command */
vim_create_process(cmd, FALSE,
- CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE, &si, &pi);
+ CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE,
+ &si, &pi, NULL, NULL);
/* Wait for the command to terminate before continuing */
{
@@ -4398,7 +4414,8 @@
* About "Inherit handles" being TRUE: this command can be litigious,
* handle inheritance was deactivated for pending temp file, but, if we
* deactivate it, the pipes don't work for some reason. */
- vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE, &si, &pi);
+ vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE,
+ &si, &pi, NULL, NULL);
if (p != cmd)
vim_free(p);
@@ -4835,7 +4852,8 @@
* inherit our handles which causes unpleasant dangling swap
* files if we exit before the spawned process
*/
- if (vim_create_process((char *)newcmd, FALSE, flags, &si, &pi))
+ if (vim_create_process((char *)newcmd, FALSE, flags,
+ &si, &pi, NULL, NULL))
x = 0;
else if (vim_shell_execute((char *)newcmd, n_show_cmd)
> (HINSTANCE)32)
@@ -4976,6 +4994,67 @@
return h;
}
+/*
+ * Turn the dictionary "env" into a NUL separated list that can be used as the
+ * environment argument of vim_create_process().
+ */
+ static void
+make_job_env(garray_T *gap, dict_T *env)
+{
+ hashitem_T *hi;
+ int todo = (int)env->dv_hashtab.ht_used;
+ LPVOID base = GetEnvironmentStringsW();
+
+ /* for last \0 */
+ if (ga_grow(gap, 1) == FAIL)
+ return;
+
+ if (base)
+ {
+ WCHAR *p = (WCHAR*) base;
+
+ /* for last \0 */
+ if (ga_grow(gap, 1) == FAIL)
+ return;
+
+ while (*p != 0 || *(p + 1) != 0)
+ {
+ if (ga_grow(gap, 1) == OK)
+ *((WCHAR*)gap->ga_data + gap->ga_len++) = *p;
+ p++;
+ }
+ FreeEnvironmentStrings(base);
+ *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
+ }
+
+ for (hi = env->dv_hashtab.ht_array; todo > 0; ++hi)
+ {
+ if (!HASHITEM_EMPTY(hi))
+ {
+ typval_T *item = &dict_lookup(hi)->di_tv;
+ WCHAR *wkey = enc_to_utf16((char_u *)hi->hi_key, NULL);
+ WCHAR *wval = enc_to_utf16(get_tv_string(item), NULL);
+ --todo;
+ if (wkey != NULL && wval != NULL)
+ {
+ int n, lkey = wcslen(wkey), lval = wcslen(wval);
+ if (ga_grow(gap, lkey + lval + 2) != OK)
+ continue;
+ for (n = 0; n < lkey; n++)
+ *((WCHAR*)gap->ga_data + gap->ga_len++) = wkey[n];
+ *((WCHAR*)gap->ga_data + gap->ga_len++) = L'=';
+ for (n = 0; n < lval; n++)
+ *((WCHAR*)gap->ga_data + gap->ga_len++) = wval[n];
+ *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
+ }
+ if (wkey != NULL) vim_free(wkey);
+ if (wval != NULL) vim_free(wval);
+ }
+ }
+
+ *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
+}
+
void
mch_job_start(char *cmd, job_T *job, jobopt_T *options)
{
@@ -4987,6 +5066,7 @@
HANDLE ifd[2];
HANDLE ofd[2];
HANDLE efd[2];
+ garray_T ga;
int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
@@ -5005,6 +5085,7 @@
ofd[1] = INVALID_HANDLE_VALUE;
efd[0] = INVALID_HANDLE_VALUE;
efd[1] = INVALID_HANDLE_VALUE;
+ ga_init2(&ga, (int)sizeof(wchar_t), 500);
jo = CreateJobObject(NULL, NULL);
if (jo == NULL)
@@ -5013,6 +5094,9 @@
goto failed;
}
+ if (options->jo_env != NULL)
+ make_job_env(&ga, options->jo_env);
+
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
@@ -5100,14 +5184,19 @@
CREATE_SUSPENDED |
CREATE_DEFAULT_ERROR_MODE |
CREATE_NEW_PROCESS_GROUP |
+ CREATE_UNICODE_ENVIRONMENT |
CREATE_NEW_CONSOLE,
- &si, &pi))
+ &si, &pi,
+ ga.ga_data,
+ (char *)options->jo_cwd))
{
CloseHandle(jo);
job->jv_status = JOB_FAILED;
goto failed;
}
+ ga_clear(&ga);
+
if (!AssignProcessToJobObject(jo, pi.hProcess))
{
/* if failing, switch the way to terminate
@@ -5148,6 +5237,7 @@
CloseHandle(ofd[1]);
CloseHandle(efd[1]);
channel_unref(channel);
+ ga_clear(&ga);
}
char *