Patch 8.1.2080
Problem:    The terminal API is limited and can't be disabled.
Solution:   Add term_setapi() to set the function prefix. (Ozaki Kiichi,
            closes #2907)
Files:      runtime/doc/eval.txt, runtime/doc/terminal.txt, src/channel.c,
            src/evalfunc.c, src/proto/terminal.pro, src/structs.h,
            src/terminal.c, src/testdir/term_util.vim,
            src/testdir/test_terminal.vim


*** ../vim-8.1.2079/runtime/doc/eval.txt        2019-09-18 21:15:25.413966524 
+0200
--- runtime/doc/eval.txt        2019-09-26 22:20:41.803905716 +0200
***************
*** 1,4 ****
! *eval.txt*    For Vim version 8.1.  Last change: 2019 May 05
  
  
                  VIM REFERENCE MANUAL    by Bram Moolenaar
--- 1,4 ----
! *eval.txt*    For Vim version 8.1.  Last change: 2019 Sep 26
  
  
                  VIM REFERENCE MANUAL    by Bram Moolenaar
***************
*** 2052,2065 ****
                Read-only.
  
                                        *v:progpath* *progpath-variable*
! v:progpath    Contains the command with which Vim was invoked, including the
!               path.  Useful if you want to message a Vim server using a
                |--remote-expr|.
                To get the full path use: >
                        echo exepath(v:progpath)
! <             If the path is relative it will be expanded to the full path,
!               so that it still works after `:cd`. Thus starting "./vim"
!               results in "/home/user/path/to/vim/src/vim".
                On MS-Windows the executable may be called "vim.exe", but the
                ".exe" is not added to v:progpath.
                Read-only.
--- 2052,2070 ----
                Read-only.
  
                                        *v:progpath* *progpath-variable*
! v:progpath    Contains the command with which Vim was invoked, in a form
!               that when passed to the shell will run the same Vim executable
!               as the current one (if $PATH remains unchanged).
!               Useful if you want to message a Vim server using a
                |--remote-expr|.
                To get the full path use: >
                        echo exepath(v:progpath)
! <             If the command has a relative path it will be expanded to the
!               full path, so that it still works after `:cd`. Thus starting
!               "./vim" results in "/home/user/path/to/vim/src/vim".
!               On Linux and other systems it will always be the full path.
!               On Mac it may just be "vim" and using exepath() as mentioned
!               above should be used to get the full path.
                On MS-Windows the executable may be called "vim.exe", but the
                ".exe" is not added to v:progpath.
                Read-only.
***************
*** 2590,2596 ****
  pathshorten({expr})           String  shorten directory names in a path
  perleval({expr})              any     evaluate |Perl| expression
  popup_atcursor({what}, {options}) Number create popup window near the cursor
! popup_beval({what}, {options})        Number  create popup window for 
'ballooneval'
  popup_clear()                 none    close all popup windows
  popup_close({id} [, {result}])        none    close popup window {id}
  popup_create({what}, {options}) Number        create a popup window
--- 2595,2601 ----
  pathshorten({expr})           String  shorten directory names in a path
  perleval({expr})              any     evaluate |Perl| expression
  popup_atcursor({what}, {options}) Number create popup window near the cursor
! popup_beval({what}, {options})        Number  create popup window for 
'ballooneval'
  popup_clear()                 none    close all popup windows
  popup_close({id} [, {result}])        none    close popup window {id}
  popup_create({what}, {options}) Number        create a popup window
***************
*** 2814,2819 ****
--- 2819,2825 ----
  term_list()                   List    get the list of terminal buffers
  term_scrape({buf}, {row})     List    get row of a terminal screen
  term_sendkeys({buf}, {keys})  none    send keystrokes to a terminal
+ term_setapi({buf}, {expr})    none    set |terminal-api| function name prefix
  term_setansicolors({buf}, {colors})
                                none    set ANSI palette in GUI color mode
  term_setkill({buf}, {how})    none    set signal to stop job in terminal
***************
*** 3133,3140 ****
                    {title}     title for the requester
                    {initdir}   directory to start browsing in
                    {default}   default file name
!               When the "Cancel" button is hit, something went wrong, or
!               browsing is not possible, an empty string is returned.
  
                                                        *browsedir()*
  browsedir({title}, {initdir})
--- 3139,3146 ----
                    {title}     title for the requester
                    {initdir}   directory to start browsing in
                    {default}   default file name
!               An empty string is returned when the "Cancel" button is hit,
!               something went wrong, or browsing is not possible.
  
                                                        *browsedir()*
  browsedir({title}, {initdir})
***************
*** 3155,3161 ****
                number.  Otherwise return the buffer number of the newly
                created buffer.  When {name} is an empty string then a new
                buffer is always created.
!               The buffer will not have' 'buflisted' set and not be loaded
                yet.  To add some text to the buffer use this: >
                        let bufnr = bufadd('someName')
                        call bufload(bufnr)
--- 3161,3167 ----
                number.  Otherwise return the buffer number of the newly
                created buffer.  When {name} is an empty string then a new
                buffer is always created.
!               The buffer will not have 'buflisted' set and not be loaded
                yet.  To add some text to the buffer use this: >
                        let bufnr = bufadd('someName')
                        call bufload(bufnr)
***************
*** 3257,3265 ****
                The result is the number of a buffer, as it is displayed by
                the ":ls" command.  For the use of {expr}, see |bufname()|
                above.
                If the buffer doesn't exist, -1 is returned.  Or, if the
                {create} argument is present and not zero, a new, unlisted,
!               buffer is created and its number is returned.
                bufnr("$") is the last buffer: >
                        :let last_buffer = bufnr("$")
  <             The result is a Number, which is the highest buffer number
--- 3263,3276 ----
                The result is the number of a buffer, as it is displayed by
                the ":ls" command.  For the use of {expr}, see |bufname()|
                above.
+ 
                If the buffer doesn't exist, -1 is returned.  Or, if the
                {create} argument is present and not zero, a new, unlisted,
!               buffer is created and its number is returned.  Example: >
!                       let newbuf = bufnr('Scratch001', 1)
! <             Using an empty name uses the current buffer. To create a new
!               buffer with an empty name use |bufadd()|.
! 
                bufnr("$") is the last buffer: >
                        :let last_buffer = bufnr("$")
  <             The result is a Number, which is the highest buffer number
***************
*** 4723,4729 ****
  get({dict}, {key} [, {default}])
                Get item with key {key} from |Dictionary| {dict}.  When this
                item is not available return {default}.  Return zero when
!               {default} is omitted.
  get({func}, {what})
                Get an item with from Funcref {func}.  Possible values for
                {what} are:
--- 4734,4743 ----
  get({dict}, {key} [, {default}])
                Get item with key {key} from |Dictionary| {dict}.  When this
                item is not available return {default}.  Return zero when
!               {default} is omitted.  Useful example: >
!                       let val = get(g:, 'var_name', 'default')
! <             This gets the value of g:var_name if it exists, and uses
!               'default' when it does not exist.
  get({func}, {what})
                Get an item with from Funcref {func}.  Possible values for
                {what} are:
***************
*** 5691,5698 ****
                        GetExpr()->glob2regpat()
  <                                                             *globpath()*
  globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]])
!               Perform glob() on all directories in {path} and concatenate
!               the results.  Example: >
                        :echo globpath(&rtp, "syntax/c.vim")
  <
                {path} is a comma-separated list of directory names.  Each
--- 5705,5712 ----
                        GetExpr()->glob2regpat()
  <                                                             *globpath()*
  globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]])
!               Perform glob() for {expr} on all directories in {path} and
!               concatenate the results.  Example: >
                        :echo globpath(&rtp, "syntax/c.vim")
  <
                {path} is a comma-separated list of directory names.  Each
***************
*** 5793,5799 ****
                If no matching mapping is found 0 is returned.
                The following characters are recognized in {mode}:
                        n       Normal mode
!                       v       Visual mode
                        o       Operator-pending mode
                        i       Insert mode
                        l       Language-Argument ("r", "f", "t", etc.)
--- 5807,5815 ----
                If no matching mapping is found 0 is returned.
                The following characters are recognized in {mode}:
                        n       Normal mode
!                       v       Visual and Select mode
!                       x       Visual mode
!                       s       Select mode
                        o       Operator-pending mode
                        i       Insert mode
                        l       Language-Argument ("r", "f", "t", etc.)
***************
*** 6466,6472 ****
                buffer is used.
                Returns a unique ID that can be passed to |listener_remove()|.
  
!               The {callback} is invoked with four arguments:
                    a:bufnr     the buffer that was changed
                    a:start     first changed line number
                    a:end       first line number below the change
--- 6482,6488 ----
                buffer is used.
                Returns a unique ID that can be passed to |listener_remove()|.
  
!               The {callback} is invoked with five arguments:
                    a:bufnr     the buffer that was changed
                    a:start     first changed line number
                    a:end       first line number below the change
***************
*** 9038,9068 ****
                current state.  Mostly useful in callbacks that want to do
                work that may not always be safe.  Roughly this works like:
                - callback uses state() to check if work is safe to do.
!                 If yes, then do it right away.
!                 Otherwise add to work queue and add SafeState and/or
!                 SafeStateAgain autocommand.
!               - When SafeState or SafeStateAgain is triggered, check with
!                 state() if the work can be done now, and if yes remove it
!                 from the queue and execute.
                Also see |mode()|.
  
                When {what} is given only characters in this string will be
                added.  E.g, this checks if the screen has scrolled: >
!                       if state('s') != ''
  <
                These characters indicate the state, generally indicating that
                something is busy:
!                   m  halfway a mapping, :normal command, feedkeys() or
!                      stuffed command
!                   o  operator pending or waiting for a command argument
!                   a  Insert mode autocomplete active
!                   x  executing an autocommand
!                   w  blocked on waiting, e.g. ch_evalexpr() and
!                      ch_read(), ch_readraw() when reading json.
!                   S  not triggering SafeState or SafeStateAgain
!                   c  callback invoked, including timer (repeats for
!                      recursiveness up to "ccc")
!                   s  screen has scrolled for messages
  
  str2float({expr})                                     *str2float()*
                Convert String {expr} to a Float.  This mostly works the same
--- 9054,9089 ----
                current state.  Mostly useful in callbacks that want to do
                work that may not always be safe.  Roughly this works like:
                - callback uses state() to check if work is safe to do.
!                 Yes: then do it right away.
!                 No:  add to work queue and add a |SafeState| and/or
!                      |SafeStateAgain| autocommand (|SafeState| triggers at
!                      toplevel, |SafeStateAgain| triggers after handling
!                      messages and callbacks).
!               - When SafeState or SafeStateAgain is triggered and executes
!                 your autocommand, check with `state()` if the work can be
!                 done now, and if yes remove it from the queue and execute.
!                 Remove the autocommand if the queue is now empty.
                Also see |mode()|.
  
                When {what} is given only characters in this string will be
                added.  E.g, this checks if the screen has scrolled: >
!                       if state('s') == ''
!                          " screen has not scrolled
  <
                These characters indicate the state, generally indicating that
                something is busy:
!                   m   halfway a mapping, :normal command, feedkeys() or
!                       stuffed command
!                   o   operator pending or waiting for a command argument,
!                       e.g. after |f|
!                   a   Insert mode autocomplete active
!                   x   executing an autocommand
!                   w   blocked on waiting, e.g. ch_evalexpr() and
!                       ch_read(), ch_readraw() when reading json.
!                   S   not triggering SafeState or SafeStateAgain
!                   c   callback invoked, including timer (repeats for
!                       recursiveness up to "ccc")
!                   s   screen has scrolled for messages
  
  str2float({expr})                                     *str2float()*
                Convert String {expr} to a Float.  This mostly works the same
***************
*** 9758,9764 ****
  
  term_ functions are documented here: |terminal-function-details|
  
! test_ functions are documented here: |test-functions|
  
  
                                                        *timer_info()*
--- 9779,9785 ----
  
  term_ functions are documented here: |terminal-function-details|
  
! test_ functions are documented here: |test-functions-details|
  
  
                                                        *timer_info()*
***************
*** 10556,10561 ****
--- 10577,10583 ----
  menu                  Compiled with support for |:menu|.
  mksession             Compiled with support for |:mksession|.
  modify_fname          Compiled with file name modifiers. |filename-modifiers|
+                       (always true)
  mouse                 Compiled with support mouse.
  mouse_dec             Compiled with support for Dec terminal mouse.
  mouse_gpm             Compiled with support for gpm (Linux console mouse)
***************
*** 11203,11208 ****
--- 11225,11236 ----
  :let ${env-name} = {expr1}                    *:let-environment* *:let-$*
                        Set environment variable {env-name} to the result of
                        the expression {expr1}.  The type is always String.
+ 
+                       On some systems making an environment variable empty
+                       causes it to be deleted.  Many systems do not make a
+                       difference between an environment variable that is not
+                       set and an environment variable that is empty.
+ 
  :let ${env-name} .= {expr1}
                        Append {expr1} to the environment variable {env-name}.
                        If the environment variable didn't exist yet this
*** ../vim-8.1.2079/runtime/doc/terminal.txt    2019-09-08 20:55:03.146072987 
+0200
--- runtime/doc/terminal.txt    2019-09-26 22:32:54.242424613 +0200
***************
*** 1,4 ****
! *terminal.txt*        For Vim version 8.1.  Last change: 2019 May 05
  
  
                  VIM REFERENCE MANUAL    by Bram Moolenaar
--- 1,4 ----
! *terminal.txt*        For Vim version 8.1.  Last change: 2019 Sep 26
  
  
                  VIM REFERENCE MANUAL    by Bram Moolenaar
***************
*** 222,228 ****
                                        Vim width (no window left or right of
                                        the terminal window) this value is
                                        ignored.
!                       ++eof={text}    when using [range]: text to send after
                                        the last line was written. Cannot
                                        contain white space.  A CR is
                                        appended.  For MS-Windows the default
--- 222,228 ----
                                        Vim width (no window left or right of
                                        the terminal window) this value is
                                        ignored.
!                       ++eof={text}    When using [range]: text to send after
                                        the last line was written. Cannot
                                        contain white space.  A CR is
                                        appended.  For MS-Windows the default
***************
*** 234,239 ****
--- 234,243 ----
                        ++type={pty}    (MS-Windows only): Use {pty} as the
                                        virtual console.  See 'termwintype'
                                        for the values.
+                       ++api={expr}    Permit the function name starting with
+                                       {expr} to be called as |terminal-api|
+                                       function.  If {expr} is empty then no
+                                       function can be called.
  
                        If you want to use more options use the |term_start()|
                        function.
***************
*** 701,706 ****
--- 705,719 ----
                        GetBufnr()->term_sendkeys(keys)
  
  
+ term_setapi({buf}, {expr})                            *term_setapi()*
+               Set the function name prefix to be used for the |terminal-api|
+               function in terminal {buf}.  For example: >
+                   :call term_setapi(buf, "Myapi_")
+                   :call term_setapi(buf, "")
+ <
+               The default is "Tapi_".  When {expr} is an empty string then
+               no |terminal-api| function can be used for {buf}.
+ 
  term_setansicolors({buf}, {colors})                   *term_setansicolors()*
                Set the ANSI color palette used by terminal {buf}.
                {colors} must be a List of 16 valid color names or hexadecimal
***************
*** 843,848 ****
--- 856,864 ----
                                     color modes.  See |g:terminal_ansi_colors|.
                   "tty_type"        (MS-Windows only): Specify which pty to
                                     use.  See 'termwintype' for the values.
+                  "term_api"        function name prefix for the
+                                    |terminal-api| function.  See
+                                    |term_setapi()|.
  
                Can also be used as a |method|: >
                        GetCommand()->term_start()
***************
*** 902,910 ****
                Call a user defined function with {argument}.
                The function is called with two arguments: the buffer number
                of the terminal and {argument}, the decoded JSON argument. 
!               The function name must start with "Tapi_" to avoid
                accidentally calling a function not meant to be used for the
!               terminal API.
                The user function should sanity check the argument.
                The function can use |term_sendkeys()| to send back a reply.
                Example in JSON: >
--- 918,926 ----
                Call a user defined function with {argument}.
                The function is called with two arguments: the buffer number
                of the terminal and {argument}, the decoded JSON argument. 
!               By default, the function name must start with "Tapi_" to avoid
                accidentally calling a function not meant to be used for the
!               terminal API.  This can be changed with |term_setapi()|.
                The user function should sanity check the argument.
                The function can use |term_sendkeys()| to send back a reply.
                Example in JSON: >
*** ../vim-8.1.2079/src/channel.c       2019-09-25 21:43:07.275251603 +0200
--- src/channel.c       2019-09-26 22:25:12.202839993 +0200
***************
*** 5144,5149 ****
--- 5144,5157 ----
                memcpy(opt->jo_ansi_colors, rgb, sizeof(rgb));
            }
  # endif
+           else if (STRCMP(hi->hi_key, "term_api") == 0)
+           {
+               if (!(supported2 & JO2_TERM_API))
+                   break;
+               opt->jo_set2 |= JO2_TERM_API;
+               opt->jo_term_api = tv_get_string_buf_chk(item,
+                                                        opt->jo_term_api_buf);
+           }
  #endif
            else if (STRCMP(hi->hi_key, "env") == 0)
            {
*** ../vim-8.1.2079/src/evalfunc.c      2019-09-16 22:55:57.728006887 +0200
--- src/evalfunc.c      2019-09-26 22:13:13.745597982 +0200
***************
*** 787,792 ****
--- 787,793 ----
  # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
      {"term_setansicolors", 2, 2, FEARG_1, f_term_setansicolors},
  # endif
+     {"term_setapi",   2, 2, FEARG_1,    f_term_setapi},
      {"term_setkill",  2, 2, FEARG_1,    f_term_setkill},
      {"term_setrestore",       2, 2, FEARG_1,    f_term_setrestore},
      {"term_setsize",  3, 3, FEARG_1,    f_term_setsize},
*** ../vim-8.1.2079/src/proto/terminal.pro      2019-09-10 21:27:15.175646978 
+0200
--- src/proto/terminal.pro      2019-09-26 22:07:13.654822573 +0200
***************
*** 50,55 ****
--- 50,56 ----
  void f_term_sendkeys(typval_T *argvars, typval_T *rettv);
  void f_term_getansicolors(typval_T *argvars, typval_T *rettv);
  void f_term_setansicolors(typval_T *argvars, typval_T *rettv);
+ void f_term_setapi(typval_T *argvars, typval_T *rettv);
  void f_term_setrestore(typval_T *argvars, typval_T *rettv);
  void f_term_setkill(typval_T *argvars, typval_T *rettv);
  void f_term_start(typval_T *argvars, typval_T *rettv);
*** ../vim-8.1.2079/src/structs.h       2019-09-25 22:36:57.300103046 +0200
--- src/structs.h       2019-09-26 22:25:01.002884547 +0200
***************
*** 1938,1943 ****
--- 1938,1944 ----
  #define JO2_ANSI_COLORS           0x8000      // "ansi_colors"
  #define JO2_TTY_TYPE      0x10000     // "tty_type"
  #define JO2_BUFNR         0x20000     // "bufnr"
+ #define JO2_TERM_API      0x40000     // "term_api"
  
  #define JO_MODE_ALL   (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
  #define JO_CB_ALL \
***************
*** 2007,2012 ****
--- 2008,2015 ----
      long_u    jo_ansi_colors[16];
  # endif
      int               jo_tty_type;        // first character of "tty_type"
+     char_u    *jo_term_api;
+     char_u    jo_term_api_buf[NUMBUFLEN];
  #endif
  } jobopt_T;
  
*** ../vim-8.1.2079/src/terminal.c      2019-09-25 22:14:26.698530906 +0200
--- src/terminal.c      2019-09-26 22:44:23.488918273 +0200
***************
*** 109,114 ****
--- 109,115 ----
  #define TL_FINISH_OPEN            'o' /* ++open */
      char_u    *tl_opencmd;
      char_u    *tl_eof_chars;
+     char_u    *tl_api;        // prefix for terminal API function
  
      char_u    *tl_arg0_cmd;   // To format the status bar
  
***************
*** 641,646 ****
--- 642,652 ----
        term->tl_kill = vim_strnsave(opt->jo_term_kill, p - opt->jo_term_kill);
      }
  
+     if (opt->jo_term_api != NULL)
+       term->tl_api = vim_strsave(opt->jo_term_api);
+     else
+       term->tl_api = vim_strsave((char_u *)"Tapi_");
+ 
      /* System dependent: setup the vterm and maybe start the job in it. */
      if (argv == NULL
            && argvar->v_type == VAR_STRING
***************
*** 708,751 ****
        cmd += 2;
        p = skiptowhite(cmd);
        ep = vim_strchr(cmd, '=');
!       if (ep != NULL && ep < p)
!           p = ep;
  
!       if ((int)(p - cmd) == 5 && STRNICMP(cmd, "close", 5) == 0)
            opt.jo_term_finish = 'c';
!       else if ((int)(p - cmd) == 7 && STRNICMP(cmd, "noclose", 7) == 0)
            opt.jo_term_finish = 'n';
!       else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "open", 4) == 0)
            opt.jo_term_finish = 'o';
!       else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "curwin", 6) == 0)
            opt.jo_curwin = 1;
!       else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "hidden", 6) == 0)
            opt.jo_hidden = 1;
!       else if ((int)(p - cmd) == 9 && STRNICMP(cmd, "norestore", 9) == 0)
            opt.jo_term_norestore = 1;
!       else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "kill", 4) == 0
!               && ep != NULL)
        {
            opt.jo_set2 |= JO2_TERM_KILL;
            opt.jo_term_kill = ep + 1;
            p = skiptowhite(cmd);
        }
!       else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "rows", 4) == 0
!               && ep != NULL && isdigit(ep[1]))
        {
            opt.jo_set2 |= JO2_TERM_ROWS;
            opt.jo_term_rows = atoi((char *)ep + 1);
            p = skiptowhite(cmd);
        }
!       else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "cols", 4) == 0
!               && ep != NULL && isdigit(ep[1]))
        {
            opt.jo_set2 |= JO2_TERM_COLS;
            opt.jo_term_cols = atoi((char *)ep + 1);
            p = skiptowhite(cmd);
        }
!       else if ((int)(p - cmd) == 3 && STRNICMP(cmd, "eof", 3) == 0
!                                                                && ep != NULL)
        {
            char_u *buf = NULL;
            char_u *keys;
--- 714,771 ----
        cmd += 2;
        p = skiptowhite(cmd);
        ep = vim_strchr(cmd, '=');
!       if (ep != NULL)
!       {
!           if (ep < p)
!               p = ep;
!           else
!               ep = NULL;
!       }
  
! # define OPTARG_HAS(name) ((int)(p - cmd) == sizeof(name) - 1 \
!                                && STRNICMP(cmd, name, sizeof(name) - 1) == 0)
!       if (OPTARG_HAS("close"))
            opt.jo_term_finish = 'c';
!       else if (OPTARG_HAS("noclose"))
            opt.jo_term_finish = 'n';
!       else if (OPTARG_HAS("open"))
            opt.jo_term_finish = 'o';
!       else if (OPTARG_HAS("curwin"))
            opt.jo_curwin = 1;
!       else if (OPTARG_HAS("hidden"))
            opt.jo_hidden = 1;
!       else if (OPTARG_HAS("norestore"))
            opt.jo_term_norestore = 1;
!       else if (OPTARG_HAS("kill") && ep != NULL)
        {
            opt.jo_set2 |= JO2_TERM_KILL;
            opt.jo_term_kill = ep + 1;
            p = skiptowhite(cmd);
        }
!       else if (OPTARG_HAS("api"))
!       {
!           opt.jo_set2 |= JO2_TERM_API;
!           if (ep != NULL)
!           {
!               opt.jo_term_api = ep + 1;
!               p = skiptowhite(cmd);
!           }
!           else
!               opt.jo_term_api = NULL;
!       }
!       else if (OPTARG_HAS("rows") && ep != NULL && isdigit(ep[1]))
        {
            opt.jo_set2 |= JO2_TERM_ROWS;
            opt.jo_term_rows = atoi((char *)ep + 1);
            p = skiptowhite(cmd);
        }
!       else if (OPTARG_HAS("cols") && ep != NULL && isdigit(ep[1]))
        {
            opt.jo_set2 |= JO2_TERM_COLS;
            opt.jo_term_cols = atoi((char *)ep + 1);
            p = skiptowhite(cmd);
        }
!       else if (OPTARG_HAS("eof") && ep != NULL)
        {
            char_u *buf = NULL;
            char_u *keys;
***************
*** 785,790 ****
--- 805,811 ----
            semsg(_("E181: Invalid attribute: %s"), cmd);
            goto theend;
        }
+ # undef OPTARG_HAS
        cmd = skipwhite(p);
      }
      if (*cmd == NUL)
***************
*** 933,938 ****
--- 954,960 ----
        free_scrollback(term);
  
        term_free_vterm(term);
+       vim_free(term->tl_api);
        vim_free(term->tl_title);
  #ifdef FEAT_SESSION
        vim_free(term->tl_command);
***************
*** 3770,3775 ****
--- 3792,3806 ----
  }
  
  /*
+  * Return TRUE if "func" starts with "pat" and "pat" isn't empty.
+  */
+     static int
+ is_permitted_term_api(char_u *func, char_u *pat)
+ {
+     return pat != NULL && *pat != NUL && STRNICMP(func, pat, STRLEN(pat)) == 
0;
+ }
+ 
+ /*
   * Handles a function call from the job running in a terminal.
   * "item" is the function name, "item->li_next" has the arguments.
   */
***************
*** 3788,3796 ****
      }
      func = tv_get_string(&item->li_tv);
  
!     if (STRNCMP(func, "Tapi_", 5) != 0)
      {
!       ch_log(channel, "Invalid function name: %s", func);
        return;
      }
  
--- 3819,3827 ----
      }
      func = tv_get_string(&item->li_tv);
  
!     if (!is_permitted_term_api(func, term->tl_api))
      {
!       ch_log(channel, "Unpermitted function: %s", func);
        return;
      }
  
***************
*** 5546,5551 ****
--- 5577,5603 ----
  #endif
  
  /*
+  * "term_setapi(buf, api)" function
+  */
+     void
+ f_term_setapi(typval_T *argvars, typval_T *rettv UNUSED)
+ {
+     buf_T     *buf = term_get_buf(argvars, "term_setapi()");
+     term_T    *term;
+     char_u    *api;
+ 
+     if (buf == NULL)
+       return;
+     term = buf->b_term;
+     vim_free(term->tl_api);
+     api = tv_get_string_chk(&argvars[1]);
+     if (api != NULL)
+       term->tl_api = vim_strsave(api);
+     else
+       term->tl_api = NULL;
+ }
+ 
+ /*
   * "term_setrestore(buf, command)" function
   */
      void
***************
*** 5608,5614 ****
                    + JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN
                    + JO2_CWD + JO2_ENV + JO2_EOF_CHARS
                    + JO2_NORESTORE + JO2_TERM_KILL
!                   + JO2_ANSI_COLORS + JO2_TTY_TYPE) == FAIL)
        return;
  
      buf = term_start(&argvars[0], NULL, &opt, 0);
--- 5660,5666 ----
                    + JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN
                    + JO2_CWD + JO2_ENV + JO2_EOF_CHARS
                    + JO2_NORESTORE + JO2_TERM_KILL
!                   + JO2_ANSI_COLORS + JO2_TTY_TYPE + JO2_TERM_API) == FAIL)
        return;
  
      buf = term_start(&argvars[0], NULL, &opt, 0);
*** ../vim-8.1.2079/src/testdir/term_util.vim   2019-07-04 14:20:38.180325318 
+0200
--- src/testdir/term_util.vim   2019-09-26 23:02:16.746622527 +0200
***************
*** 61,71 ****
  
    let cmd = GetVimCommandCleanTerm() .. a:arguments
  
!   let buf = term_start(cmd, {
        \ 'curwin': 1,
        \ 'term_rows': rows,
        \ 'term_cols': cols,
!       \ })
    if &termwinsize == ''
      " in the GUI we may end up with a different size, try to set it.
      if term_getsize(buf) != [rows, cols]
--- 61,76 ----
  
    let cmd = GetVimCommandCleanTerm() .. a:arguments
  
!   let options = {
        \ 'curwin': 1,
        \ 'term_rows': rows,
        \ 'term_cols': cols,
!       \ }
!   " Accept other options whose name starts with 'term_'.
!   call extend(options, filter(copy(a:options), 'v:key =~# "^term_"'))
! 
!   let buf = term_start(cmd, options)
! 
    if &termwinsize == ''
      " in the GUI we may end up with a different size, try to set it.
      if term_getsize(buf) != [rows, cols]
*** ../vim-8.1.2079/src/testdir/test_terminal.vim       2019-09-22 
21:29:49.659426007 +0200
--- src/testdir/test_terminal.vim       2019-09-26 22:49:33.190913684 +0200
***************
*** 1353,1382 ****
  func Test_terminal_api_call()
    CheckRunVimInTerminal
  
    call WriteApiCall('Tapi_TryThis')
    let buf = RunVimInTerminal('-S Xscript', {})
    call WaitFor({-> exists('g:called_bufnum')})
    call assert_equal(buf, g:called_bufnum)
    call assert_equal(['hello', 123], g:called_arg)
  
    call StopVimInTerminal(buf)
    call delete('Xscript')
!   unlet g:called_bufnum
!   unlet g:called_arg
  endfunc
  
  func Test_terminal_api_call_fails()
    CheckRunVimInTerminal
  
    call WriteApiCall('TryThis')
    call ch_logfile('Xlog', 'w')
!   let buf = RunVimInTerminal('-S Xscript', {})
!   call WaitForAssert({-> assert_match('Invalid function name: TryThis', 
string(readfile('Xlog')))})
  
    call StopVimInTerminal(buf)
    call delete('Xscript')
!   call ch_logfile('', '')
    call delete('Xlog')
  endfunc
  
  let s:caught_e937 = 0
--- 1353,1442 ----
  func Test_terminal_api_call()
    CheckRunVimInTerminal
  
+ call ch_logfile('logfile', 'w')
+   unlet! g:called_bufnum
+   unlet! g:called_arg
+ 
    call WriteApiCall('Tapi_TryThis')
+ 
+   " Default
    let buf = RunVimInTerminal('-S Xscript', {})
    call WaitFor({-> exists('g:called_bufnum')})
    call assert_equal(buf, g:called_bufnum)
    call assert_equal(['hello', 123], g:called_arg)
+   call StopVimInTerminal(buf)
+ 
+   unlet! g:called_bufnum
+   unlet! g:called_arg
+ 
+   " Enable explicitly
+   let buf = RunVimInTerminal('-S Xscript', {'term_api': 'Tapi_Try'})
+   call WaitFor({-> exists('g:called_bufnum')})
+   call assert_equal(buf, g:called_bufnum)
+   call assert_equal(['hello', 123], g:called_arg)
+   call StopVimInTerminal(buf)
  
+   unlet! g:called_bufnum
+   unlet! g:called_arg
+ 
+   func! ApiCall_TryThis(bufnum, arg)
+     let g:called_bufnum2 = a:bufnum
+     let g:called_arg2 = a:arg
+   endfunc
+ 
+   call WriteApiCall('ApiCall_TryThis')
+ 
+   " Use prefix match
+   let buf = RunVimInTerminal('-S Xscript', {'term_api': 'ApiCall_'})
+   call WaitFor({-> exists('g:called_bufnum2')})
+   call assert_equal(buf, g:called_bufnum2)
+   call assert_equal(['hello', 123], g:called_arg2)
    call StopVimInTerminal(buf)
+ 
+   unlet! g:called_bufnum2
+   unlet! g:called_arg2
+ 
    call delete('Xscript')
!   delfunction! ApiCall_TryThis
!   unlet! g:called_bufnum2
!   unlet! g:called_arg2
  endfunc
  
  func Test_terminal_api_call_fails()
    CheckRunVimInTerminal
  
+   func! TryThis(bufnum, arg)
+     let g:called_bufnum3 = a:bufnum
+     let g:called_arg3 = a:arg
+   endfunc
+ 
    call WriteApiCall('TryThis')
+ 
+   unlet! g:called_bufnum3
+   unlet! g:called_arg3
+ 
+   " Not permitted
    call ch_logfile('Xlog', 'w')
!   let buf = RunVimInTerminal('-S Xscript', {'term_api': ''})
!   call WaitForAssert({-> assert_match('Unpermitted function: TryThis', 
string(readfile('Xlog')))})
!   call assert_false(exists('g:called_bufnum3'))
!   call assert_false(exists('g:called_arg3'))
!   call StopVimInTerminal(buf)
  
+   " No match
+   call ch_logfile('Xlog', 'w')
+   let buf = RunVimInTerminal('-S Xscript', {'term_api': 'TryThat'})
+   call WaitFor({-> string(readfile('Xlog')) =~ 'Unpermitted function: 
TryThis'})
+   call assert_false(exists('g:called_bufnum3'))
+   call assert_false(exists('g:called_arg3'))
    call StopVimInTerminal(buf)
+ 
    call delete('Xscript')
!   call ch_logfile('')
    call delete('Xlog')
+   delfunction! TryThis
+   unlet! g:called_bufnum3
+   unlet! g:called_arg3
  endfunc
  
  let s:caught_e937 = 0
***************
*** 2061,2063 ****
--- 2121,2154 ----
    exe buf . "bwipe!"
    call delete('Xtext')
  endfunc
+ 
+ func Test_terminal_setapi_and_call()
+   if !CanRunVimInTerminal()
+     return
+   endif
+ 
+   call WriteApiCall('Tapi_TryThis')
+   call ch_logfile('Xlog', 'w')
+ 
+   unlet! g:called_bufnum
+   unlet! g:called_arg
+ 
+   let buf = RunVimInTerminal('-S Xscript', {'term_api': 0})
+   call WaitForAssert({-> assert_match('Unpermitted function: Tapi_TryThis', 
string(readfile('Xlog')))})
+   call assert_false(exists('g:called_bufnum'))
+   call assert_false(exists('g:called_arg'))
+ 
+   call term_setapi(buf, 'Tapi_TryThis')
+   call term_sendkeys(buf, ":set notitle\<CR>")
+   call term_sendkeys(buf, ":source Xscript\<CR>")
+   call WaitFor({-> exists('g:called_bufnum')})
+   call assert_equal(buf, g:called_bufnum)
+   call assert_equal(['hello', 123], g:called_arg)
+   call StopVimInTerminal(buf)
+ 
+   call delete('Xscript')
+   call ch_logfile('')
+   call delete('Xlog')
+   unlet! g:called_bufnum
+   unlet! g:called_arg
+ endfunc
*** ../vim-8.1.2079/src/version.c       2019-09-25 23:06:35.859483812 +0200
--- src/version.c       2019-09-26 23:05:39.786313095 +0200
***************
*** 759,760 ****
--- 759,762 ----
  {   /* Add new patch number below this line */
+ /**/
+     2080,
  /**/

-- 
MORTICIAN:    What?
CUSTOMER:     Nothing -- here's your nine pence.
DEAD PERSON:  I'm not dead!
MORTICIAN:    Here -- he says he's not dead!
CUSTOMER:     Yes, he is.
DEAD PERSON:  I'm not!
                                  The Quest for the Holy Grail (Monty Python)

 /// Bram Moolenaar -- [email protected] -- 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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/201909262109.x8QL9RH9030340%40masaka.moolenaar.net.

Raspunde prin e-mail lui