There is no simple way to prepare a file name parameter for bufnr() so
that it reliably finds a specific buffer.


The file name can contain file-pattern characters that can lead to more
than one match, or a match attempt in buflist_findpat() can find a match
on the wrong buffer.

    :let buf = bufnr('[my]_testfile')

    can match           does not match

        m_testfile          [my]_testfile
        y_testfile

According to ':he bufname()', to force a single full match, embed the
filename in '^' and '$'.  Also, the file-pattern characters and special
characters need to be escaped:

    :let buf = bufnr('^'. escape(fname, '*?\,{}[]^$%#') .'$')

But for some character combinations, the escaped characters do not match:

    :echo '^'. escape('test\{,}name', '*?\,{}[]^$%#') .'$'
        ^test\\\{\,\}name$

In the escaped filename, '\\\{\,\}' is treated as the file-pattern
'\\\{n,m\}', equivalent to '\{n,m}' in a regex (':he file-pattern').

But the '\,' in the middle is syntactically incorrect, and the whole
pattern is rejected.  A literal match also fails.  Unless a buffer named
'^test\\\{\,\}name$' exists, which would be a false match.

According to ':he file-pattern', the only construct with magic escaped
characters is '\\\{n,m\}'.

But file_pat_to_reg_pat() knows more, at least when '\' is allowed as a
directory separator.  E.g.:

    '\*'    used as     '\.*'
    '\?'    used as     '\.'
            ...

All functions that use an {expr} as in bufname() have this problem:

    bufname()
    bufnr()
    bufwinnr()
    getbufline()
    getbufvar()
    setbufvar()


Suggested solution:

Add a {literal} parameter to bufnr(), which forces a literal match as in
bufexists().

When a full path is used in the {expr} parameter, a match is also unique.

In all other functions, the result of bufnr() can be used.

---
 runtime/doc/eval.txt |    8 ++++++--
 src/eval.c           |   17 +++++++++++++----
 2 files changed, 19 insertions(+), 6 deletions(-)

Index: b/runtime/doc/eval.txt
===================================================================
--- a/runtime/doc/eval.txt      2015-08-04 22:23:36.705032764 +0200
+++ b/runtime/doc/eval.txt      2015-08-04 22:28:32.096557318 +0200
@@ -1763,7 +1763,8 @@ bufexists( {expr})                Number  TRUE if buffe
 buflisted( {expr})             Number  TRUE if buffer {expr} is listed
 bufloaded( {expr})             Number  TRUE if buffer {expr} is loaded
 bufname( {expr})               String  Name of the buffer {expr}
-bufnr( {expr})                 Number  Number of the buffer {expr}
+bufnr( {expr} [, {create} [, {literal}]])
+                               Number  Number of the buffer {expr}
 bufwinnr( {expr})              Number  window number of buffer {expr}
 byte2line( {byte})             Number  line number at byte count {byte}
 byteidx( {expr}, {nr})         Number  byte index of {nr}'th char in {expr}
@@ -2293,10 +2294,13 @@ bufname({expr})                                         
*bufname()*
                Obsolete name: buffer_name().
 
                                                        *bufnr()*
-bufnr({expr} [, {create}])
+bufnr({expr} [, {create} [, {literal}]])
                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 {literal} argument is present and not zero, {expr} is
+               used as in |bufexists()|.  Use this to prevent expansion of
+               pattern wildcards in {expr}.
                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.
Index: b/src/eval.c
===================================================================
--- a/src/eval.c        2015-08-04 22:24:36.704935227 +0200
+++ b/src/eval.c        2015-08-04 22:28:32.100557312 +0200
@@ -8083,7 +8083,7 @@ static struct fst
     {"buflisted",      1, 1, f_buflisted},
     {"bufloaded",      1, 1, f_bufloaded},
     {"bufname",                1, 1, f_bufname},
-    {"bufnr",          1, 2, f_bufnr},
+    {"bufnr",          1, 3, f_bufnr},
     {"bufwinnr",       1, 1, f_bufwinnr},
     {"byte2line",      1, 1, f_byte2line},
     {"byteidx",                2, 2, f_byteidx},
@@ -9388,9 +9388,18 @@ f_bufnr(argvars, rettv)
     char_u     *name;
 
     (void)get_tv_number(&argvars[0]);      /* issue errmsg if type error */
-    ++emsg_off;
-    buf = get_buf_tv(&argvars[0], FALSE);
-    --emsg_off;
+
+    if (argvars[1].v_type != VAR_UNKNOWN
+           && argvars[2].v_type != VAR_UNKNOWN
+           && get_tv_number_chk(&argvars[2], &error) != 0
+           && !error)
+       buf = find_buffer(&argvars[0]);
+    else
+    {
+       ++emsg_off;
+       buf = get_buf_tv(&argvars[0], FALSE);
+       --emsg_off;
+    }
 
     /* If the buffer isn't found and the second argument is not zero create a
      * new buffer. */
-- 
Olaf Dabrunz (oda <at> fctrace.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].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui