Patch 8.0.0902
Problem:    Cannot specify directory or environment for a job.
Solution:   Add the "cwd" and "env" arguments to job options. (Yasuhiro
            Matsumoto, closes #1160)
Files:      runtime/doc/channel.txt, src/channel.c, src/terminal.c,
            src/os_unix.c, src/os_win32.c, src/structs.h,
            src/testdir/test_channel.vim, src/testdir/test_terminal.vim


*** ../vim-8.0.0901/runtime/doc/channel.txt     2017-01-25 22:40:45.076782542 
+0100
--- runtime/doc/channel.txt     2017-08-11 18:02:28.198366352 +0200
***************
*** 1,4 ****
! *channel.txt*      For Vim version 8.0.  Last change: 2016 Oct 27
  
  
                  VIM REFERENCE MANUAL    by Bram Moolenaar
--- 1,4 ----
! *channel.txt*      For Vim version 8.0.  Last change: 2017 Aug 11
  
  
                  VIM REFERENCE MANUAL    by Bram Moolenaar
***************
*** 375,381 ****
  ==============================================================================
  6. Using a RAW or NL channel                          *channel-raw*
  
! If mode is RAW or NL then a message can be send like this: >
      let response = ch_evalraw(channel, {string})
  
  The {string} is sent as-is.  The response will be what can be read from the
--- 375,381 ----
  ==============================================================================
  6. Using a RAW or NL channel                          *channel-raw*
  
! If mode is RAW or NL then a message can be sent like this: >
      let response = ch_evalraw(channel, {string})
  
  The {string} is sent as-is.  The response will be what can be read from the
***************
*** 427,434 ****
  channels, an empty string for a RAW or NL channel.  You can use |ch_canread()|
  to check if there is something to read.
  
! Note that when there is no callback message are dropped.  To avoid that add a
! close callback to the channel.
  
  To read all output from a RAW channel that is available: >
        let output = ch_readraw(channel)
--- 427,434 ----
  channels, an empty string for a RAW or NL channel.  You can use |ch_canread()|
  to check if there is something to read.
  
! Note that when there is no callback, messages are dropped.  To avoid that add
! a close callback to the channel.
  
  To read all output from a RAW channel that is available: >
        let output = ch_readraw(channel)
***************
*** 480,490 ****
  of a pipe causes the read end to get EOF).  To avoid this make the job sleep
  for a short while before it exits.
  
- Note that if the job exits before you read the output, the output may be lost.
- This depends on the system (on Unix this happens because closing the write end
- of a pipe causes the read end to get EOF).  To avoid this make the job sleep
- for a short while before it exits.
- 
  The handler defined for "out_cb" will not receive stderr.  If you want to
  handle that separately, add an "err_cb" handler: >
      let job = job_start(command, {"out_cb": "MyHandler",
--- 480,485 ----
***************
*** 494,499 ****
--- 489,499 ----
  "callback" option: >
      let job = job_start(command, {"callback": "MyHandler"}) 
  
+ Depending on the system, starting a job can put Vim in the background, the
+ started job gets the focus.  To avoid that, use the `foreground()` function.
+ This might not always work when called early, put in the callback handler or
+ use a timer to call it after the job has started.
+ 
  You can send a message to the command with ch_evalraw().  If the channel is in
  JSON or JS mode you can use ch_evalexpr().
  
***************
*** 518,524 ****
  and "in_bot" options.
  
  A special mode is when "in_top" is set to zero and "in_bot" is not set: Every
! time a line is added to the buffer, the last-but-one line will be send to the
  job stdin.  This allows for editing the last line and sending it when pressing
  Enter.
                                                        *channel-close-in*
--- 518,524 ----
  and "in_bot" options.
  
  A special mode is when "in_top" is set to zero and "in_bot" is not set: Every
! time a line is added to the buffer, the last-but-one line will be sent to the
  job stdin.  This allows for editing the last line and sending it when pressing
  Enter.
                                                        *channel-close-in*
***************
*** 549,555 ****
  
  To start another process without creating a channel: >
      let job = job_start(command,
!       \ {"in_io": "null", "out_io": "null", "err_io": "null"})
  
  This starts {command} in the background, Vim does not wait for it to finish.
  
--- 549,555 ----
  
  To start another process without creating a channel: >
      let job = job_start(command,
!       \ {"in_io": "null", "out_io": "null", "err_io": "null"})
  
  This starts {command} in the background, Vim does not wait for it to finish.
  
***************
*** 610,620 ****
                                                *job-close_cb*
  "close_cb": handler   Callback for when the channel is closed.  Same as
                        "close_cb" on |ch_open()|, see |close_cb|.
!                                               *job-exit_cb*
! "drop"                        Specifies when to drop messages.  Same as 
"drop" on
                        |ch_open()|, see |channel-drop|.  For "auto" the
                        exit_cb is not considered.
! 
  "exit_cb": handler    Callback for when the job ends.  The arguments are the
                        job and the exit status.
                        Vim checks up to 10 times per second for jobs that
--- 610,620 ----
                                                *job-close_cb*
  "close_cb": handler   Callback for when the channel is closed.  Same as
                        "close_cb" on |ch_open()|, see |close_cb|.
!                                               *job-drop*
! "drop": when          Specifies when to drop messages.  Same as "drop" on
                        |ch_open()|, see |channel-drop|.  For "auto" the
                        exit_cb is not considered.
!                                               *job-exit_cb*
  "exit_cb": handler    Callback for when the job ends.  The arguments are the
                        job and the exit status.
                        Vim checks up to 10 times per second for jobs that
***************
*** 624,635 ****
                        Note that data can be buffered, callbacks may still be
                        called after the process ends.
                                                        *job-timeout*
! "timeout"             The time to wait for a request when blocking, E.g.
                        when using ch_evalexpr().  In milliseconds.  The
                        default is 2000 (2 seconds).
                                                *out_timeout* *err_timeout*
! "out_timeout"         Timeout for stdout.  Only when using pipes.
! "err_timeout"         Timeout for stderr.  Only when using pipes.
                        Note: when setting "timeout" the part specific mode is
                        overwritten.  Therefore set "timeout" first and the
                        part specific mode later.
--- 624,635 ----
                        Note that data can be buffered, callbacks may still be
                        called after the process ends.
                                                        *job-timeout*
! "timeout": time               The time to wait for a request when blocking, 
E.g.
                        when using ch_evalexpr().  In milliseconds.  The
                        default is 2000 (2 seconds).
                                                *out_timeout* *err_timeout*
! "out_timeout": time   Timeout for stdout.  Only when using pipes.
! "err_timeout": time   Timeout for stderr.  Only when using pipes.
                        Note: when setting "timeout" the part specific mode is
                        overwritten.  Therefore set "timeout" first and the
                        part specific mode later.
***************
*** 641,657 ****
                        The default is "term".
  
                                                *job-term*
! "term": "open"                Start a terminal and connect the job
!                       stdin/stdout/stderr to it.
                        NOTE: Not implemented yet!
  
  "channel": {channel}  Use an existing channel instead of creating a new one.
                        The parts of the channel that get used for the new job
                        will be disconnected from what they were used before.
!                       If the channel was still use by another job this may
                        cause I/O errors.
                        Existing callbacks and other settings remain.
  
                                *job-in_io* *in_top* *in_bot* *in_name* *in_buf*
  "in_io": "null"               disconnect stdin (read from /dev/null)
  "in_io": "pipe"               stdin is connected to the channel (default)
--- 641,663 ----
                        The default is "term".
  
                                                *job-term*
! "term": "open"                Start a terminal in a new window and connect 
the job
!                       stdin/stdout/stderr to it.  Similar to using
!                       `:terminal`.
                        NOTE: Not implemented yet!
  
  "channel": {channel}  Use an existing channel instead of creating a new one.
                        The parts of the channel that get used for the new job
                        will be disconnected from what they were used before.
!                       If the channel was still used by another job this may
                        cause I/O errors.
                        Existing callbacks and other settings remain.
  
+ "pty": 1              Use a pty (pseudo-tty) instead of a pipe when
+                       possible.  This is most useful in combination with a
+                       terminal window, see |terminal|.
+                       {only on Unix and Unix-like systems}
+ 
                                *job-in_io* *in_top* *in_bot* *in_name* *in_buf*
  "in_io": "null"               disconnect stdin (read from /dev/null)
  "in_io": "pipe"               stdin is connected to the channel (default)
***************
*** 666,672 ****
  "out_io": "null"      disconnect stdout (goes to /dev/null)
  "out_io": "pipe"      stdout is connected to the channel (default)
  "out_io": "file"      stdout writes to a file
! "out_io": "buffer"    stdout appends to a buffer (see below)
  "out_name": "/path/file" the name of the file or buffer to write to
  "out_buf": number     the number of the buffer to write to
  "out_modifiable": 0   when writing to a buffer, 'modifiable' will be off
--- 672,678 ----
  "out_io": "null"      disconnect stdout (goes to /dev/null)
  "out_io": "pipe"      stdout is connected to the channel (default)
  "out_io": "file"      stdout writes to a file
! "out_io": "buffer"    stdout appends to a buffer (see below)
  "out_name": "/path/file" the name of the file or buffer to write to
  "out_buf": number     the number of the buffer to write to
  "out_modifiable": 0   when writing to a buffer, 'modifiable' will be off
***************
*** 679,685 ****
  "err_io": "null"      disconnect stderr  (goes to /dev/null)
  "err_io": "pipe"      stderr is connected to the channel (default)
  "err_io": "file"      stderr writes to a file
! "err_io": "buffer"    stderr appends to a buffer (see below)
  "err_name": "/path/file" the name of the file or buffer to write to
  "err_buf": number     the number of the buffer to write to
  "err_modifiable": 0   when writing to a buffer, 'modifiable' will be off
--- 685,691 ----
  "err_io": "null"      disconnect stderr  (goes to /dev/null)
  "err_io": "pipe"      stderr is connected to the channel (default)
  "err_io": "file"      stderr writes to a file
! "err_io": "buffer"    stderr appends to a buffer (see below)
  "err_name": "/path/file" the name of the file or buffer to write to
  "err_buf": number     the number of the buffer to write to
  "err_modifiable": 0   when writing to a buffer, 'modifiable' will be off
***************
*** 690,695 ****
--- 696,705 ----
  "block_write": number only for testing: pretend every other write to stdin
                        will block
  
+ "env": dict           environment variables for the new process
+ "cwd": "/path/to/dir" current working directory for the new process;
+                       if the directory does not exist an error is given
+ 
  
  Writing to a buffer ~
                                                        *out_io-buffer*
***************
*** 725,734 ****
  first line set to "Reading from channel output...".  The default is to add the
  message.  "err_msg" does the same for channel error.
  
- 'modifiable' option off, or write to a buffer that has 'modifiable' off.  That
- means that lines will be appended to the buffer, but the user can't easily
- change the buffer.
- 
  When an existing buffer is to be written where 'modifiable' is off and the
  "out_modifiable" or "err_modifiable" options is not zero, an error is given
  and the buffer will not be written to.
--- 735,740 ----
*** ../vim-8.0.0901/src/channel.c       2017-08-11 16:31:50.325234460 +0200
--- src/channel.c       2017-08-11 18:23:09.601610683 +0200
***************
*** 4153,4158 ****
--- 4153,4160 ----
        partial_unref(opt->jo_exit_partial);
      else if (opt->jo_exit_cb != NULL)
        func_unref(opt->jo_exit_cb);
+     if (opt->jo_env != NULL)
+       dict_unref(opt->jo_env);
  }
  
  /*
***************
*** 4433,4438 ****
--- 4435,4460 ----
                opt->jo_term_finish = *val;
            }
  #endif
+           else if (STRCMP(hi->hi_key, "env") == 0)
+           {
+               if (!(supported & JO2_ENV))
+                   break;
+               opt->jo_set |= JO2_ENV;
+               opt->jo_env = item->vval.v_dict;
+               ++item->vval.v_dict->dv_refcount;
+           }
+           else if (STRCMP(hi->hi_key, "cwd") == 0)
+           {
+               if (!(supported & JO2_CWD))
+                   break;
+               opt->jo_cwd = get_tv_string_buf_chk(item, opt->jo_cwd_buf);
+               if (opt->jo_cwd == NULL || !mch_isdir(opt->jo_cwd))
+               {
+                   EMSG2(_(e_invarg2), "cwd");
+                   return FAIL;
+               }
+               opt->jo_set |= JO2_CWD;
+           }
            else if (STRCMP(hi->hi_key, "waittime") == 0)
            {
                if (!(supported & JO_WAITTIME))
*** ../vim-8.0.0901/src/terminal.c      2017-08-11 16:31:50.329234432 +0200
--- src/terminal.c      2017-08-11 18:25:15.192723599 +0200
***************
*** 2362,2368 ****
            && get_job_options(&argvars[1], &opt,
                JO_TIMEOUT_ALL + JO_STOPONEXIT
                + JO_EXIT_CB + JO_CLOSE_CALLBACK
!               + JO2_TERM_NAME + JO2_TERM_FINISH) == FAIL)
        return;
  
      term_start(cmd, &opt);
--- 2362,2369 ----
            && get_job_options(&argvars[1], &opt,
                JO_TIMEOUT_ALL + JO_STOPONEXIT
                + JO_EXIT_CB + JO_CLOSE_CALLBACK
!               + JO2_TERM_NAME + JO2_TERM_FINISH
!               + JO2_CWD + JO2_ENV) == FAIL)
        return;
  
      term_start(cmd, &opt);
*** ../vim-8.0.0901/src/os_unix.c       2017-08-11 16:31:50.325234460 +0200
--- src/os_unix.c       2017-08-11 19:04:00.768368595 +0200
***************
*** 5320,5325 ****
--- 5320,5341 ----
  # endif
            set_default_child_environment();
  
+       if (options->jo_env != NULL)
+       {
+           dict_T      *dict = options->jo_env;
+           hashitem_T  *hi;
+           int         todo = (int)dict->dv_hashtab.ht_used;
+ 
+           for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi)
+               if (!HASHITEM_EMPTY(hi))
+               {
+                   typval_T *item = &dict_lookup(hi)->di_tv;
+ 
+                   vim_setenv((char_u*)hi->hi_key, get_tv_string(item));
+                   --todo;
+               }
+       }
+ 
        if (use_null_for_in || use_null_for_out || use_null_for_err)
            null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
  
***************
*** 5387,5392 ****
--- 5403,5411 ----
        if (null_fd >= 0)
            close(null_fd);
  
+       if (options->jo_cwd != NULL && mch_chdir((char *)options->jo_cwd) != 0)
+           _exit(EXEC_FAILED);
+ 
        /* See above for type of argv. */
        execvp(argv[0], argv);
  
*** ../vim-8.0.0901/src/os_win32.c      2017-08-11 16:31:50.329234432 +0200
--- src/os_win32.c      2017-08-11 18:13:57.105511911 +0200
***************
*** 3981,4011 ****
      BOOL              inherit_handles,
      DWORD             flags,
      STARTUPINFO               *si,
!     PROCESS_INFORMATION *pi)
  {
  #ifdef FEAT_MBYTE
      if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
      {
!       WCHAR   *wcmd = enc_to_utf16((char_u *)cmd, NULL);
  
!       if (wcmd != 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;
        }
      }
  #endif
      return CreateProcess(
        NULL,                   /* Executable name */
--- 3981,4026 ----
      BOOL              inherit_handles,
      DWORD             flags,
      STARTUPINFO               *si,
!     PROCESS_INFORMATION *pi,
!     LPVOID            *env,
!     char              *cwd)
  {
  #ifdef FEAT_MBYTE
      if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
      {
!       BOOL    ret;
!       WCHAR   *wcmd, *wcwd = NULL;
  
!       wcmd = enc_to_utf16((char_u *)cmd, NULL);
!       if (wcmd == NULL)
!           goto fallback;
!       if (cwd != NULL)
        {
!           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,4021 ****
        NULL,                   /* Thread security attributes */
        inherit_handles,        /* Inherit handles */
        flags,                  /* Creation flags */
!       NULL,                   /* Environment */
!       NULL,                   /* Current directory */
        si,                     /* Startup information */
        pi);                    /* Process information */
  }
--- 4029,4036 ----
        NULL,                   /* Thread security attributes */
        inherit_handles,        /* Inherit handles */
        flags,                  /* Creation flags */
!       env,                    /* Environment */
!       cwd,                    /* Current directory */
        si,                     /* Startup information */
        pi);                    /* Process information */
  }
***************
*** 4079,4085 ****
  
      /* Now, run the command */
      vim_create_process(cmd, FALSE,
!           CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE, &si, &pi);
  
      /* Wait for the command to terminate before continuing */
      {
--- 4094,4101 ----
  
      /* Now, run the command */
      vim_create_process(cmd, FALSE,
!           CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE,
!           &si, &pi, NULL, NULL);
  
      /* Wait for the command to terminate before continuing */
      {
***************
*** 4398,4404 ****
       * 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);
  
      if (p != cmd)
        vim_free(p);
--- 4414,4421 ----
       * 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, NULL, NULL);
  
      if (p != cmd)
        vim_free(p);
***************
*** 4835,4841 ****
             * 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))
                x = 0;
            else if (vim_shell_execute((char *)newcmd, n_show_cmd)
                                                               > (HINSTANCE)32)
--- 4852,4859 ----
             * 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, NULL, NULL))
                x = 0;
            else if (vim_shell_execute((char *)newcmd, n_show_cmd)
                                                               > (HINSTANCE)32)
***************
*** 4976,4981 ****
--- 4994,5060 ----
      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,4992 ****
--- 5066,5072 ----
      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,5010 ****
--- 5085,5091 ----
      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,5018 ****
--- 5094,5102 ----
        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,5113 ****
            CREATE_SUSPENDED |
            CREATE_DEFAULT_ERROR_MODE |
            CREATE_NEW_PROCESS_GROUP |
            CREATE_NEW_CONSOLE,
!           &si, &pi))
      {
        CloseHandle(jo);
        job->jv_status = JOB_FAILED;
        goto failed;
      }
  
      if (!AssignProcessToJobObject(jo, pi.hProcess))
      {
        /* if failing, switch the way to terminate
--- 5184,5202 ----
            CREATE_SUSPENDED |
            CREATE_DEFAULT_ERROR_MODE |
            CREATE_NEW_PROCESS_GROUP |
+           CREATE_UNICODE_ENVIRONMENT |
            CREATE_NEW_CONSOLE,
!           &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,5153 ****
--- 5237,5243 ----
      CloseHandle(ofd[1]);
      CloseHandle(efd[1]);
      channel_unref(channel);
+     ga_clear(&ga);
  }
  
      char *
*** ../vim-8.0.0901/src/structs.h       2017-08-10 23:15:15.002851689 +0200
--- src/structs.h       2017-08-11 18:08:37.555767310 +0200
***************
*** 1686,1692 ****
  #define JO2_ERR_MSG       0x0002      /* "err_msg" (JO_OUT_ << 1) */
  #define JO2_TERM_NAME     0x0004      /* "term_name" */
  #define JO2_TERM_FINISH           0x0008      /* "term_finish" */
! #define JO2_ALL                   0x000F
  
  #define JO_MODE_ALL   (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
  #define JO_CB_ALL \
--- 1686,1694 ----
  #define JO2_ERR_MSG       0x0002      /* "err_msg" (JO_OUT_ << 1) */
  #define JO2_TERM_NAME     0x0004      /* "term_name" */
  #define JO2_TERM_FINISH           0x0008      /* "term_finish" */
! #define JO2_ENV                   0x0010      /* "env" */
! #define JO2_CWD                   0x0020      /* "cwd" */
! #define JO2_ALL                   0x003F
  
  #define JO_MODE_ALL   (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
  #define JO_CB_ALL \
***************
*** 1738,1743 ****
--- 1740,1748 ----
      int               jo_id;
      char_u    jo_soe_buf[NUMBUFLEN];
      char_u    *jo_stoponexit;
+     dict_T    *jo_env;        /* environment variables */
+     char_u    jo_cwd_buf[NUMBUFLEN];
+     char_u    *jo_cwd;
  
  #ifdef FEAT_TERMINAL
      /* when non-zero run the job in a terminal window of this size */
*** ../vim-8.0.0901/src/testdir/test_channel.vim        2017-07-15 
17:01:50.350726204 +0200
--- src/testdir/test_channel.vim        2017-08-11 17:59:25.967647596 +0200
***************
*** 1664,1669 ****
--- 1664,1708 ----
    call assert_equal(1, g:linecount)
  endfunc
  
+ func Test_env()
+   if !has('job')
+     return
+   endif
+ 
+   let s:envstr = ''
+   if has('win32')
+     call job_start(['cmd', '/c', 'echo %FOO%'], {'callback': 
{ch,msg->execute(":let s:envstr .= msg")}, 'env':{'FOO': 'bar'}})
+   else
+     call job_start([&shell, &shellcmdflag, 'echo $FOO'], {'callback': 
{ch,msg->execute(":let s:envstr .= msg")}, 'env':{'FOO': 'bar'}})
+   endif
+   call WaitFor('"" != s:envstr')
+   call assert_equal("bar", s:envstr)
+   unlet s:envstr
+ endfunc
+ 
+ func Test_cwd()
+   if !has('job')
+     return
+   endif
+ 
+   let s:envstr = ''
+   if has('win32')
+     let expect = $TEMP
+     call job_start(['cmd', '/c', 'echo %CD%'], {'callback': 
{ch,msg->execute(":let s:envstr .= msg")}, 'cwd': expect})
+   else
+     let expect = $HOME
+     call job_start(['pwd'], {'callback': {ch,msg->execute(":let s:envstr .= 
msg")}, 'cwd': expect})
+   endif
+   call WaitFor('"" != s:envstr')
+   let expect = substitute(expect, '[/\\]$', '', '')
+   let s:envstr = substitute(s:envstr, '[/\\]$', '', '')
+   if $CI != '' && stridx(s:envstr, '/private/') == 0
+     let s:envstr = s:envstr[8:]
+   endif
+   call assert_equal(expect, s:envstr)
+   unlet s:envstr
+ endfunc
+ 
  function Ch_test_close_lambda(port)
    let handle = ch_open('localhost:' . a:port, s:chopt)
    if ch_status(handle) == "fail"
*** ../vim-8.0.0901/src/testdir/test_terminal.vim       2017-08-10 
23:15:15.002851689 +0200
--- src/testdir/test_terminal.vim       2017-08-11 19:08:30.394474816 +0200
***************
*** 8,15 ****
  
  " Open a terminal with a shell, assign the job to g:job and return the buffer
  " number.
! func Run_shell_in_terminal()
!   let buf = term_start(&shell)
  
    let termlist = term_list()
    call assert_equal(1, len(termlist))
--- 8,15 ----
  
  " Open a terminal with a shell, assign the job to g:job and return the buffer
  " number.
! func Run_shell_in_terminal(options)
!   let buf = term_start(&shell, a:options)
  
    let termlist = term_list()
    call assert_equal(1, len(termlist))
***************
*** 32,38 ****
  endfunc
  
  func Test_terminal_basic()
!   let buf = Run_shell_in_terminal()
    if has("unix")
      call assert_match("^/dev/", job_info(g:job).tty)
      call assert_match("^/dev/", term_gettty(''))
--- 32,38 ----
  endfunc
  
  func Test_terminal_basic()
!   let buf = Run_shell_in_terminal({})
    if has("unix")
      call assert_match("^/dev/", job_info(g:job).tty)
      call assert_match("^/dev/", term_gettty(''))
***************
*** 51,57 ****
  endfunc
  
  func Test_terminal_make_change()
!   let buf = Run_shell_in_terminal()
    call Stop_shell_in_terminal(buf)
    call term_wait(buf)
  
--- 51,57 ----
  endfunc
  
  func Test_terminal_make_change()
!   let buf = Run_shell_in_terminal({})
    call Stop_shell_in_terminal(buf)
    call term_wait(buf)
  
***************
*** 65,71 ****
  endfunc
  
  func Test_terminal_wipe_buffer()
!   let buf = Run_shell_in_terminal()
    call assert_fails(buf . 'bwipe', 'E517')
    exe buf . 'bwipe!'
    call WaitFor('job_status(g:job) == "dead"')
--- 65,71 ----
  endfunc
  
  func Test_terminal_wipe_buffer()
!   let buf = Run_shell_in_terminal({})
    call assert_fails(buf . 'bwipe', 'E517')
    exe buf . 'bwipe!'
    call WaitFor('job_status(g:job) == "dead"')
***************
*** 76,82 ****
  endfunc
  
  func Test_terminal_hide_buffer()
!   let buf = Run_shell_in_terminal()
    quit
    for nr in range(1, winnr('$'))
      call assert_notequal(winbufnr(nr), buf)
--- 76,82 ----
  endfunc
  
  func Test_terminal_hide_buffer()
!   let buf = Run_shell_in_terminal({})
    quit
    for nr in range(1, winnr('$'))
      call assert_notequal(winbufnr(nr), buf)
***************
*** 266,274 ****
  endfunc
  
  func Test_finish_close()
    call assert_equal(1, winnr('$'))
  
-   " TODO: use something that takes much less than a whole second
    if has('win32')
      let cmd = $windir . '\system32\timeout.exe 1'
    else
--- 266,276 ----
  endfunc
  
  func Test_finish_close()
+   return
+   " TODO: use something that takes much less than a whole second
+   echo 'This will take five seconds...'
    call assert_equal(1, winnr('$'))
  
    if has('win32')
      let cmd = $windir . '\system32\timeout.exe 1'
    else
***************
*** 304,306 ****
--- 306,337 ----
  
    bwipe
  endfunc
+ 
+ func Test_terminal_cwd()
+   if !has('unix')
+     return
+   endif
+   call mkdir('Xdir')
+   let buf = term_start('pwd', {'cwd': 'Xdir'})
+   sleep 100m
+   call term_wait(buf)
+   call assert_equal(getcwd() . '/Xdir', getline(1))
+ 
+   exe buf . 'bwipe'
+   call delete('Xdir', 'rf')
+ endfunc
+ 
+ func Test_terminal_env()
+   if !has('unix')
+     return
+   endif
+   let buf = Run_shell_in_terminal({'env': {'TESTENV': 'correct'}})
+   call term_wait(buf)
+   call term_sendkeys(buf, "echo $TESTENV\r")
+   call term_wait(buf)
+   call Stop_shell_in_terminal(buf)
+   call term_wait(buf)
+   call assert_equal('correct', getline(2))
+ 
+   exe buf . 'bwipe'
+ endfunc
*** ../vim-8.0.0901/src/version.c       2017-08-11 17:56:16.044983391 +0200
--- src/version.c       2017-08-11 18:01:04.838952501 +0200
***************
*** 771,772 ****
--- 771,774 ----
  {   /* Add new patch number below this line */
+ /**/
+     902,
  /**/

-- 
It is illegal for anyone to give lighted cigars to dogs, cats, and other
domesticated animal kept as pets.
                [real standing law in Illinois, United States of America]

 /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui