]> granicus.if.org Git - vim/commitdiff
patch 9.0.0253: a symlink to an autoload script results in two entries v9.0.0253
authorBram Moolenaar <Bram@vim.org>
Wed, 24 Aug 2022 15:30:36 +0000 (16:30 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 24 Aug 2022 15:30:36 +0000 (16:30 +0100)
Problem:    A symlink to an autoload script results in two entries in the list
            of scripts, items expected in one are actually in the other.
Solution:   Have one script item refer to the actually sourced one.
            (closes #10960)

runtime/doc/builtin.txt
runtime/doc/repeat.txt
src/eval.c
src/evalvars.c
src/proto/vim9script.pro
src/scriptfile.c
src/structs.h
src/testdir/test_vim9_import.vim
src/version.c
src/vim9compile.c
src/vim9script.c

index 00ae665f8b35dfcfb8c903df869dccddae8a65ae..5928929258fb5249264692598cb0520349992599 100644 (file)
@@ -253,7 +253,7 @@ getreg([{regname} [, 1 [, {list}]]])
                                String or List   contents of a register
 getreginfo([{regname}])                Dict    information about a register
 getregtype([{regname}])                String  type of a register
-getscriptinfo()                List    list of sourced scripts
+getscriptinfo()                        List    list of sourced scripts
 gettabinfo([{expr}])           List    list of tab pages
 gettabvar({nr}, {varname} [, {def}])
                                any     variable {varname} in tab {nr} or {def}
@@ -4089,17 +4089,20 @@ getregtype([{regname}])                                 *getregtype()*
                Can also be used as a |method|: >
                        GetRegname()->getregtype()
 
-getscriptinfo()                                        *getscriptinfo()*
+getscriptinfo()                                                *getscriptinfo()*
                Returns a |List| with information about all the sourced Vim
-               scripts in the order they were sourced. (|:scriptinfo|)
+               scripts in the order they were sourced, like what
+               `:scriptnames` shows.
 
                Each item in the returned List is a |Dict| with the following
                items:
                    autoload    set to TRUE for a script that was used with
-                               |import autoload| but was not actually sourced
-                               yet.
+                               `import autoload` but was not actually sourced
+                               yet (see |import-autoload|).
                    name        vim script file name.
                    sid         script ID |<SID>|.
+                   sourced     if this script is an alias this is the script
+                               ID of the actually sourced script, otherwise zero
 
 gettabinfo([{tabnr}])                                  *gettabinfo()*
                If {tabnr} is not specified, then information about all the
index f9d0b6698214ad1dfb8f1e0d74fa6cbd2886b879..f8602ba8c98850b4667fafcf364c18624db66103 100644 (file)
@@ -417,6 +417,10 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
                        For a script that was used with `import autoload` but
                        was not actually sourced yet an "A" is shown after the
                        script ID.
+                       For a script that was referred to by one name but
+                       after resolving symbolic links got sourced with
+                       another name the other script is after "->".  E.g.
+                       "20->22" means script 20 was sourced as script 22.
                        {not available when compiled without the |+eval|
                        feature}
 
index 60daca51ce9de279ceb77a8bcf7a42c30b862c54..3d6d84c2bb4196b5d65818a019db743163edd288 100644 (file)
@@ -1039,6 +1039,7 @@ get_lval(
            ufunc_T *ufunc;
            type_T *type;
 
+           import_check_sourced_sid(&import->imp_sid);
            lp->ll_sid = import->imp_sid;
            lp->ll_name = skipwhite(p + 1);
            p = find_name_end(lp->ll_name, NULL, NULL, fne_flags);
index 0b51ce1da06e0057f3265cea3d4ec527d98d9e78..71cab0a3085f1b9bc1cec48e7f3669869d73238a 100644 (file)
@@ -2953,6 +2953,7 @@ eval_variable(
            {
                if (rettv != NULL)
                {
+                   // special value that is used in handle_subscript()
                    rettv->v_type = VAR_ANY;
                    rettv->vval.v_number = sid != 0 ? sid : import->imp_sid;
                }
index 04c07fc325af329eb176e6fb56dbb2c94f4606fc..0e98039e8b41d54959f8e34b50171eeaef93ccf1 100644 (file)
@@ -12,6 +12,7 @@ void ex_export(exarg_T *eap);
 void free_imports_and_script_vars(int sid);
 void mark_imports_for_reload(int sid);
 void ex_import(exarg_T *eap);
+void import_check_sourced_sid(int *sid);
 int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, cstack_T *cstack, int verbose);
 char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
 void update_vim9_script_var(int create, dictitem_T *di, char_u *name, int flags, typval_T *tv, type_T **type, int do_member);
index c176763d5118c16f4777635d5ae17d9b495dd2fd..48e475bea197d2891bed1d42c4beabf0d11e3cef 100644 (file)
@@ -1357,6 +1357,7 @@ do_source_ext(
 {
     source_cookie_T        cookie;
     char_u                 *p;
+    char_u                 *fname_not_fixed = NULL;
     char_u                 *fname_exp;
     char_u                 *firstline = NULL;
     int                            retval = FAIL;
@@ -1389,13 +1390,12 @@ do_source_ext(
     }
     else
     {
-       p = expand_env_save(fname);
-       if (p == NULL)
-           return retval;
-       fname_exp = fix_fname(p);
-       vim_free(p);
+       fname_not_fixed = expand_env_save(fname);
+       if (fname_not_fixed == NULL)
+           goto theend;
+       fname_exp = fix_fname(fname_not_fixed);
        if (fname_exp == NULL)
-           return retval;
+           goto theend;
        if (mch_isdir(fname_exp))
        {
            smsg(_("Cannot source a directory: \"%s\""), fname);
@@ -1602,14 +1602,15 @@ do_source_ext(
        int error = OK;
 
        // It's new, generate a new SID and initialize the scriptitem.
-       current_sctx.sc_sid = get_new_scriptitem(&error);
+       sid = get_new_scriptitem(&error);
+       current_sctx.sc_sid = sid;
        if (error == FAIL)
            goto almosttheend;
-       si = SCRIPT_ITEM(current_sctx.sc_sid);
+       si = SCRIPT_ITEM(sid);
        si->sn_name = fname_exp;
        fname_exp = vim_strsave(si->sn_name);  // used for autocmd
        if (ret_sid != NULL)
-           *ret_sid = current_sctx.sc_sid;
+           *ret_sid = sid;
 
        // Remember the "is_vimrc" flag for when the file is sourced again.
        si->sn_is_vimrc = is_vimrc;
@@ -1668,7 +1669,7 @@ do_source_ext(
     if (do_profiling == PROF_YES)
     {
        // Get "si" again, "script_items" may have been reallocated.
-       si = SCRIPT_ITEM(current_sctx.sc_sid);
+       si = SCRIPT_ITEM(sid);
        if (si->sn_prof_on)
        {
            profile_end(&si->sn_pr_start);
@@ -1719,7 +1720,7 @@ almosttheend:
     // If "sn_save_cpo" is set that means we encountered "vim9script": restore
     // 'cpoptions', unless in the main .vimrc file.
     // Get "si" again, "script_items" may have been reallocated.
-    si = SCRIPT_ITEM(current_sctx.sc_sid);
+    si = SCRIPT_ITEM(sid);
     if (si->sn_save_cpo != NULL && si->sn_is_vimrc == DOSO_NONE)
     {
        if (STRCMP(p_cpo, CPO_VIM) != 0)
@@ -1774,6 +1775,19 @@ almosttheend:
        apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, FALSE, curbuf);
 
 theend:
+    if (sid > 0 && ret_sid != NULL
+           && fname_not_fixed != NULL && fname_exp != NULL)
+    {
+       int not_fixed_sid = find_script_by_name(fname_not_fixed);
+
+       // If "fname_not_fixed" is a symlink then we source the linked file.
+       // If the original name is in the script list we add the ID of the
+       // script that was actually sourced.
+       if (SCRIPT_ID_VALID(not_fixed_sid) && not_fixed_sid != sid)
+           SCRIPT_ITEM(not_fixed_sid)->sn_sourced_sid = sid;
+    }
+
+    vim_free(fname_not_fixed);
     vim_free(fname_exp);
     sticky_cmdmod_flags = save_sticky_cmdmod_flags;
 #ifdef FEAT_EVAL
@@ -1787,7 +1801,7 @@ do_source(
     char_u     *fname,
     int                check_other,        // check for .vimrc and _vimrc
     int                is_vimrc,           // DOSO_ value
-    int                *ret_sid UNUSED)
+    int                *ret_sid)
 {
     return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL, FALSE);
 }
@@ -1828,9 +1842,16 @@ ex_scriptnames(exarg_T *eap)
 
        if (si->sn_name != NULL)
        {
+           char sourced_buf[20];
+
            home_replace(NULL, si->sn_name, NameBuff, MAXPATHL, TRUE);
-           vim_snprintf((char *)IObuff, IOSIZE, "%3d%s: %s",
+           if (si->sn_sourced_sid > 0)
+               vim_snprintf(sourced_buf, 20, "->%d", si->sn_sourced_sid);
+           else
+               sourced_buf[0] = NUL;
+           vim_snprintf((char *)IObuff, IOSIZE, "%3d%s%s: %s",
                    i,
+                   sourced_buf,
                    si->sn_state == SN_STATE_NOT_LOADED ? " A" : "",
                    NameBuff);
            if (!message_filtered(IObuff))
@@ -1946,6 +1967,7 @@ f_getscriptinfo(typval_T *argvars UNUSED, typval_T *rettv)
                || list_append_dict(l, d) == FAIL
                || dict_add_string(d, "name", si->sn_name) == FAIL
                || dict_add_number(d, "sid", i) == FAIL
+               || dict_add_number(d, "sourced", si->sn_sourced_sid) == FAIL
                || dict_add_bool(d, "autoload",
                                si->sn_state == SN_STATE_NOT_LOADED) == FAIL)
            return;
index 5b1a5cc937d8ac2067b48dcbf1eabd6b5945d50f..a507a37c5e1191183ea664532101377820b9f2e0 100644 (file)
@@ -1850,13 +1850,19 @@ typedef struct {
 #define IMP_FLAGS_AUTOLOAD     4   // script still needs to be loaded
 
 /*
- * Info about an already sourced scripts.
+ * Info about an encoutered script.
+ * When sn_state has the SN_STATE_NOT_LOADED is has not been sourced yet.
  */
 typedef struct
 {
     char_u     *sn_name;           // full path of script file
     int                sn_script_seq;      // latest sctx_T sc_seq value
 
+    // When non-zero the script ID of the actually sourced script.  Used if a
+    // script is used by a name which has a symlink, we list both names, but
+    // only the linked-to script is actually sourced.
+    int                sn_sourced_sid;
+
     // "sn_vars" stores the s: variables currently valid.  When leaving a block
     // variables local to that block are removed.
     scriptvar_T        *sn_vars;
index 4cc53b2c26d7040a0f4eba1764996dca5194a357..5c14b0527b681d731809688e27eae7d99176989f 100644 (file)
@@ -2906,5 +2906,50 @@ def Test_vim9_autoload_error()
   v9.CheckScriptFailure(lines, 'E461: Illegal variable name: foo#bar', 2)
 enddef
 
+def Test_vim9_import_symlink()
+  if !has('unix')
+    CheckUnix
+  else
+    mkdir('Xto/plugin', 'p')
+    var lines =<< trim END
+        vim9script
+        import autoload 'bar.vim'
+        g:resultFunc = bar.Func()
+        g:resultValue = bar.value
+    END
+    writefile(lines, 'Xto/plugin/foo.vim')
+
+    mkdir('Xto/autoload', 'p')
+    lines =<< trim END
+        vim9script
+        export def Func(): string
+          return 'func'
+        enddef
+        export var value = 'val'
+    END
+    writefile(lines, 'Xto/autoload/bar.vim')
+
+    var save_rtp = &rtp
+    &rtp = getcwd() .. '/Xfrom'
+    system('ln -s ' .. getcwd() .. '/Xto Xfrom')
+
+    source Xfrom/plugin/foo.vim
+    assert_equal('func', g:resultFunc)
+    assert_equal('val', g:resultValue)
+
+    var infoTo = getscriptinfo()->filter((_, v) => v.name =~ 'Xto/autoload/bar')
+    var infoFrom = getscriptinfo()->filter((_, v) => v.name =~ 'Xfrom/autoload/bar')
+    assert_equal(1, len(infoTo))
+    assert_equal(1, len(infoFrom))
+    assert_equal(infoTo[0].sid, infoFrom[0].sourced)
+
+    unlet g:resultFunc
+    unlet g:resultValue
+    &rtp = save_rtp
+    delete('Xto', 'rf')
+    delete('Xfrom', 'rf')
+  endif
+enddef
+
 
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 2cd1e50ea81dce7505bcbf04d2eb625b7ef31f80..48b75dce1f1df8db4512f0791138b017927155b1 100644 (file)
@@ -731,6 +731,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    253,
 /**/
     252,
 /**/
index d8596e99effb490dcc9509467b4ab9fc547a884f..bf7390b6fa64978cd8ad9c49d222183ddcdf86ea 100644 (file)
@@ -610,7 +610,7 @@ find_imported(char_u *name, size_t len, int load)
     ret = find_imported_in_script(name, len, current_sctx.sc_sid);
     if (ret != NULL && load && (ret->imp_flags & IMP_FLAGS_AUTOLOAD))
     {
-       scid_T  dummy;
+       scid_T  actual_sid = 0;
        int     save_emsg_off = emsg_off;
 
        // "emsg_off" will be set when evaluating an expression silently, but
@@ -621,7 +621,11 @@ find_imported(char_u *name, size_t len, int load)
        // script found before but not loaded yet
        ret->imp_flags &= ~IMP_FLAGS_AUTOLOAD;
        (void)do_source(SCRIPT_ITEM(ret->imp_sid)->sn_name, FALSE,
-                                                           DOSO_NONE, &dummy);
+                                                      DOSO_NONE, &actual_sid);
+       // If the script is a symlink it may be sourced with another name, may
+       // need to adjust the script ID for that.
+       if (actual_sid != 0)
+           ret->imp_sid = actual_sid;
 
        emsg_off = save_emsg_off;
     }
index 6f843620778e5e7ae54a371b46d55011306d3a6d..913e66c8838430cf5059818c730875216f5a4e47 100644 (file)
@@ -696,6 +696,20 @@ ex_import(exarg_T *eap)
     clear_evalarg(&evalarg, eap);
 }
 
+/*
+ * When a script is a symlink it may be imported with one name and sourced
+ * under another name.  Adjust the import script ID if needed.
+ * "*sid" must be a valid script ID.
+ */
+    void
+import_check_sourced_sid(int *sid)
+{
+    scriptitem_T *script = SCRIPT_ITEM(*sid);
+
+    if (script->sn_sourced_sid > 0)
+       *sid = script->sn_sourced_sid;
+}
+
 /*
  * Find an exported item in "sid" matching "name".
  * Either "cctx" or "cstack" is NULL.