To: vim-dev@vim.org Subject: Patch 6.2.064 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit ------------ Patch 6.2.064 Problem: resolve() only handles one symbolic link, need to repeat it to resolve all of them. Then need to simplify the file name. Solution: Make resolve() resolve all symbolic links and simplify the result. Add simplify() to just simplify a file name. Fix that test49 doesn't work if /tmp is a symbolic link. (Servatius Brandt) Files: runtime/doc/eval.txt, src/eval.c, src/tag.c, src/testdir/test49.vim *** ../vim-6.2.063/runtime/doc/eval.txt Sun Jun 1 14:45:23 2003 --- runtime/doc/eval.txt Thu Aug 7 19:31:16 2003 *************** *** 1,4 **** ! *eval.txt* For Vim version 6.2. Last change: 2003 Jun 01 VIM REFERENCE MANUAL by Bram Moolenaar --- 1,4 ---- ! *eval.txt* For Vim version 6.2. Last change: 2003 Aug 07 VIM REFERENCE MANUAL by Bram Moolenaar *************** *** 885,890 **** --- 887,893 ---- setline( {lnum}, {line}) Number set line {lnum} to {line} setreg( {n}, {v}[, {opt}]) Number set register to value and type setwinvar( {nr}, {varname}, {val}) set {varname} in window {nr} to {val} + simplify( {filename}) String simplify filename as much as possible strftime( {format}[, {time}]) String time in specified format stridx( {haystack}, {needle}) Number first index of {needle} in {haystack} strlen( {expr}) Number length of the String {expr} *************** *** 2104,2116 **** successfully, and non-zero when the renaming failed. This function is not available in the |sandbox|. ! resolve({filename}) *resolve()* On MS-Windows, when {filename} is a shortcut (a .lnk file), ! returns the path the shortcut points to. ! On Unix, when {filename} is a symbolic link, returns the path ! the symlink points to. This only happens once, the returned ! path could be a symlink again. ! Otherwise {filename} is returned. search({pattern} [, {flags}]) *search()* Search for regexp pattern {pattern}. The search starts at the --- 2112,2129 ---- successfully, and non-zero when the renaming failed. This function is not available in the |sandbox|. ! resolve({filename}) *resolve()* *E655* On MS-Windows, when {filename} is a shortcut (a .lnk file), ! returns the path the shortcut points to in a simplified form. ! On Unix, repeat resolving symbolic links in all path ! components of {filename} and return the simplified result. ! To cope with link cycles, resolving of symbolic links is ! stopped after 100 iterations. ! On other systems, return the simplified {filename}. ! The simplification step is done as by |simplify()|. ! resolve() keeps a leading path component specifying the ! current directory (provided the result is still a relative ! path name) and also keeps a trailing path separator. search({pattern} [, {flags}]) *search()* Search for regexp pattern {pattern}. The search starts at the *************** *** 2302,2307 **** --- 2315,2335 ---- :call setwinvar(2, "myvar", "foobar") < This function is not available in the |sandbox|. + simplify({filename}) *simplify()* + Simplify the file name as much as possible without changing + the meaning. Shortcuts (on MS-Windows) or symbolic links (on + Unix) are not resolved. If the first path component in + {filename} designates the current directory, this will be + valid for the result as well. A trailing path separator is + not removed either. + Example: > + simplify("./dir/.././/file/") == "./file/" + < Note: The combination "dir/.." is only removed if "dir" is + a searchable directory or does not exist. On Unix, it is also + removed when "dir" is a symbolic link within the same + directory. In order to resolve all the involved symbolic + links before simplifying the path name, use |resolve()|. + strftime({format} [, {time}]) *strftime()* The result is a String, which is a formatted date and time, as specified by the {format} string. The given {time} is used, *** ../vim-6.2.063/src/eval.c Sun Aug 10 22:24:37 2003 --- src/eval.c Thu Jul 31 19:47:34 2003 *************** *** 318,323 **** --- 318,324 ---- static void f_serverlist __ARGS((VAR argvars, VAR retvar)); static void f_setline __ARGS((VAR argvars, VAR retvar)); static void f_setreg __ARGS((VAR argvars, VAR retvar)); + static void f_simplify __ARGS((VAR argvars, VAR retvar)); static void find_some_match __ARGS((VAR argvars, VAR retvar, int start)); static void f_strftime __ARGS((VAR argvars, VAR retvar)); static void f_stridx __ARGS((VAR argvars, VAR retvar)); *************** *** 2812,2817 **** --- 2813,2819 ---- {"setline", 2, 2, f_setline}, {"setreg", 2, 3, f_setreg}, {"setwinvar", 3, 3, f_setwinvar}, + {"simplify", 1, 1, f_simplify}, #ifdef HAVE_STRFTIME {"strftime", 1, 2, f_strftime}, #endif *************** *** 5808,5813 **** --- 5810,5816 ---- VAR retvar; { char_u *p; + int limit = 100; p = get_var_string(&argvars[0]); #ifdef FEAT_SHORTCUT *************** *** 5826,5858 **** char_u buf[MAXPATHL + 1]; char_u *cpy; int len; ! len = readlink((char *)p, (char *)buf, MAXPATHL); ! if (len > 0) { ! buf[len] = NUL; ! if (gettail(p) > p && !mch_isFullName(buf)) { ! /* symlink is relative to directory of argument */ ! cpy = alloc((unsigned)(STRLEN(p) + STRLEN(buf) + 1)); ! if (cpy != NULL) { ! STRCPY(cpy, p); ! STRCPY(gettail(cpy), buf); ! retvar->var_val.var_string = cpy; ! p = NULL; } } else ! p = buf; } ! if (p != NULL) ! retvar->var_val.var_string = vim_strsave(p); } # else retvar->var_val.var_string = vim_strsave(p); # endif #endif retvar->var_type = VAR_STRING; } --- 5829,6020 ---- char_u buf[MAXPATHL + 1]; char_u *cpy; int len; + char_u *remain = NULL; + char_u *q; + int is_relative_to_current = FALSE; + int has_trailing_pathsep = FALSE; ! p = vim_strsave(p); ! ! if (p[0] == '.' && (vim_ispathsep(p[1]) ! || (p[1] == '.' && (vim_ispathsep(p[2]))))) ! is_relative_to_current = TRUE; ! ! len = STRLEN(p); ! if (len > 0 && vim_ispathsep(p[len-1])) ! has_trailing_pathsep = TRUE; ! ! q = getnextcomp(p); ! if (*q != NUL) ! { ! /* Separate the first path component in "p", and keep the ! * remainder (beginning with the path separator). */ ! remain = vim_strsave(q - 1); ! q[-1] = NUL; ! } ! ! for(;;) { ! for (;;) { ! len = readlink((char *)p, (char *)buf, MAXPATHL); ! if (len <= 0) ! break; ! buf[len] = NUL; ! ! if (limit-- == 0) ! { ! vim_free(p); ! vim_free(remain); ! EMSG(_("E655: Too much symbolic links (cycle?)")); ! retvar->var_val.var_string = NULL; ! goto fail; ! } ! ! /* Ensure that the result will have a trailing path separator ! * if the argument has one. */ ! if (remain == NULL && has_trailing_pathsep) ! add_pathsep(buf); ! ! /* Separate the first path component in the link value and ! * concatenate the remainders. */ ! q = getnextcomp(vim_ispathsep(*buf) ? buf + 1 : buf); ! if (*q != NUL) ! { ! if (remain == NULL) ! remain = vim_strsave(q - 1); ! else ! { ! cpy = vim_strnsave(q-1, STRLEN(q-1)+STRLEN(remain)); ! if (cpy != NULL) ! { ! STRCAT(cpy, remain); ! vim_free(remain); ! remain = cpy; ! } ! } ! q[-1] = NUL; ! } ! ! q = gettail(p); ! if (q > p && *q == NUL) ! { ! /* Ignore trailing path separator. */ ! q[-1] = NUL; ! q = gettail(p); ! } ! if (q > p && !mch_isFullName(buf)) ! { ! /* symlink is relative to directory of argument */ ! cpy = alloc((unsigned)(STRLEN(p) + STRLEN(buf) + 1)); ! if (cpy != NULL) ! { ! STRCPY(cpy, p); ! STRCPY(gettail(cpy), buf); ! vim_free(p); ! p = cpy; ! } ! } ! else { ! vim_free(p); ! p = vim_strsave(buf); } } + + if (remain == NULL) + break; + + /* Append the first path component of "remain" to "p". */ + q = getnextcomp(remain + 1); + len = q - remain - (*q != NUL); + cpy = vim_strnsave(p, STRLEN(p) + len); + if (cpy != NULL) + { + STRNCAT(cpy, remain, len); + vim_free(p); + p = cpy; + } + /* Shorten "remain". */ + if (*q != NUL) + STRCPY(remain, q - 1); else ! { ! vim_free(remain); ! remain = NULL; ! } } ! ! /* If the result is a relative path name, make it explicitly relative to ! * the current directory if and only if the argument had this form. */ ! if (!vim_ispathsep(*p)) ! { ! if (is_relative_to_current ! && *p != NUL ! && !(p[0] == '.' ! && (p[1] == NUL ! || vim_ispathsep(p[1]) ! || (p[1] == '.' ! && (p[2] == NUL ! || vim_ispathsep(p[2])))))) ! { ! /* Prepend "./". */ ! cpy = vim_strnsave((char_u *)"./", 2 + STRLEN(p)); ! if (cpy != NULL) ! { ! STRCAT(cpy, p); ! vim_free(p); ! p = cpy; ! } ! } ! else if (!is_relative_to_current) ! { ! /* Strip leading "./". */ ! q = p; ! while (q[0] == '.' && vim_ispathsep(q[1])) ! q += 2; ! if (q > p) ! mch_memmove(p, p + 2, STRLEN(p + 2) + (size_t)1); ! } ! } ! ! /* Ensure that the result will have no trailing path separator ! * if the argument had none. But keep "/" or "//". */ ! if (!has_trailing_pathsep) ! { ! q = p + STRLEN(p); ! while ((q > p + 2 || (q == p + 2 && !vim_ispathsep(*p))) ! && vim_ispathsep(q[-1])) ! --q; ! *q = NUL; ! } ! ! retvar->var_val.var_string = p; } # else retvar->var_val.var_string = vim_strsave(p); # endif #endif + + simplify_filename(retvar->var_val.var_string); + + fail: + retvar->var_type = VAR_STRING; + } + + /* + * "simplify()" function + */ + static void + f_simplify(argvars, retvar) + VAR argvars; + VAR retvar; + { + char_u *p; + + p = get_var_string(&argvars[0]); + retvar->var_val.var_string = vim_strsave(p); + simplify_filename(retvar->var_val.var_string); /* simplify in place */ retvar->var_type = VAR_STRING; } *** ../vim-6.2.063/src/tag.c Sun Aug 10 22:24:37 2003 --- src/tag.c Thu Jul 31 18:27:34 2003 *************** *** 2864,2875 **** #ifndef AMIGA /* Amiga doesn't have "..", it uses "/" */ int components = 0; char_u *p, *tail, *start; ! #ifdef UNIX ! char_u *orig = vim_strsave(filename); ! ! if (orig == NULL) ! return; ! #endif p = filename; #ifdef BACKSLASH_IN_FILENAME --- 2864,2871 ---- #ifndef AMIGA /* Amiga doesn't have "..", it uses "/" */ int components = 0; char_u *p, *tail, *start; ! int stripping_disabled = FALSE; ! int relative = TRUE; p = filename; #ifdef BACKSLASH_IN_FILENAME *************** *** 2877,2889 **** p += 2; #endif ! while (vim_ispathsep(*p)) ! ++p; ! start = p; /* remember start after "c:/" or "/" or "//" */ do { ! /* At this point "p" is pointing to the char following a "/". */ #ifdef VMS /* VMS allows device:[path] - don't strip the [ in directory */ if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':') --- 2873,2891 ---- p += 2; #endif ! if (vim_ispathsep(*p)) ! { ! relative = FALSE; ! do ! ++p; ! while (vim_ispathsep(*p)); ! } ! start = p; /* remember start after "c:/" or "/" or "///" */ do { ! /* At this point "p" is pointing to the char following a single "/" ! * or "p" is at the "start" of the (absolute or relative) path name. */ #ifdef VMS /* VMS allows device:[path] - don't strip the [ in directory */ if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':') *************** *** 2898,2952 **** /* ":: composition: vms host/passwd component */ ++components; p = getnextcomp(p + 2); - } else #endif if (vim_ispathsep(*p)) movetail(p, p + 1); /* remove duplicate "/" */ ! else if (p[0] == '.' && vim_ispathsep(p[1])) ! movetail(p, p + 2); /* strip "./" */ ! else if (p[0] == '.' && p[1] == '.' && vim_ispathsep(p[2])) { if (components > 0) /* strip one preceding component */ { ! tail = p + 3; /* skip to after "../" or "..///" */ ! while (vim_ispathsep(*tail)) ! ++tail; ! --p; ! /* skip back to after previous '/' */ ! while (p > start && !vim_ispathsep(p[-1])) ! --p; ! /* skip back to after first '/' in a row */ ! while (p - 1 > start && vim_ispathsep(p[-2])) --p; ! movetail(p, tail); /* strip previous component */ ! --components; } - else /* leading "../" */ - p += 3; /* skip to char after "/" */ } else { ++components; /* simple path component */ p = getnextcomp(p); } ! } while (p != NULL && *p != NUL); ! ! #ifdef UNIX ! /* Check that the new file name is really the same file. This will not be ! * the case when using symbolic links: "dir/link/../name" != "dir/name". */ ! { ! struct stat orig_st, new_st; ! ! if ( mch_stat((char *)orig, &orig_st) < 0 ! || mch_stat((char *)filename, &new_st) < 0 ! || orig_st.st_ino != new_st.st_ino ! || orig_st.st_dev != new_st.st_dev) ! STRCPY(filename, orig); ! vim_free(orig); ! } ! #endif #endif /* !AMIGA */ } --- 2900,3064 ---- /* ":: composition: vms host/passwd component */ ++components; p = getnextcomp(p + 2); } else #endif if (vim_ispathsep(*p)) movetail(p, p + 1); /* remove duplicate "/" */ ! else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL)) ! { ! if (p == start && relative) ! p += 1 + (p[1] != NUL); /* keep single "." or leading "./" */ ! else ! { ! /* Strip "./" or ".///". If we are at the end of the file name ! * and there is no trailing path separator, either strip "/." if ! * we are after "start", or strip "." if we are at the beginning ! * of an absolute path name . */ ! tail = p + 1; ! if (p[1] != NUL) ! while (vim_ispathsep(*tail)) ! ++tail; ! else if (p > start) ! --p; /* strip preceding path separator */ ! movetail(p, tail); ! } ! } ! else if (p[0] == '.' && p[1] == '.' && ! (vim_ispathsep(p[2]) || p[2] == NUL)) { + /* Skip to after ".." or "../" or "..///". */ + tail = p + 2; + while (vim_ispathsep(*tail)) + ++tail; + if (components > 0) /* strip one preceding component */ { ! int do_strip = FALSE; ! char_u saved_char; ! struct stat st, new_st; ! ! /* Don't strip for an erroneous file name. */ ! if (!stripping_disabled) ! { ! /* If the preceding component does not exist in the file ! * system, we strip it. On Unix, we don't accept a symbolic ! * link that refers to a non-existent file. */ ! saved_char = p[-1]; ! p[-1] = NUL; ! #ifdef UNIX ! if (mch_lstat((char *)filename, &st) < 0) ! #else ! if (mch_stat((char *)filename, &st) < 0) ! #endif ! do_strip = TRUE; ! p[-1] = saved_char; ! --p; ! /* Skip back to after previous '/'. */ ! while (p > start && !vim_ispathsep(p[-1])) ! --p; ! ! if (!do_strip) ! { ! /* If the component exists in the file system, check ! * that stripping it won't change the meaning of the ! * file name. First get information about the ! * unstripped file name. This may fail if the component ! * to strip is not a searchable directory (but a regular ! * file, for instance), since the trailing "/.." cannot ! * be applied then. We don't strip it then since we ! * don't want to replace an erroneous file name by ! * a valid one, and we disable stripping of later ! * components. */ ! saved_char = *tail; ! *tail = NUL; ! if (mch_stat((char *)filename, &st) >= 0) ! do_strip = TRUE; ! else ! stripping_disabled = TRUE; ! *tail = saved_char; ! #ifdef UNIX ! if (do_strip) ! { ! /* On Unix, the check for the unstripped file name ! * above works also for a symbolic link pointing to ! * a searchable directory. But then the parent of ! * the directory pointed to by the link must be the ! * same as the stripped file name. (The latter ! * exists in the file system since it is the ! * component's parent directory.) */ ! if (p == start && relative) ! (void)mch_stat(".", &new_st); ! else ! { ! saved_char = *p; ! *p = NUL; ! (void)mch_stat((char *)filename, &new_st); ! *p = saved_char; ! } ! ! if (new_st.st_ino != st.st_ino || ! new_st.st_dev != st.st_dev) ! { ! do_strip = FALSE; ! /* We don't disable stripping of later ! * components since the unstripped path name is ! * still valid. */ ! } ! } ! #endif ! } ! } ! ! if (!do_strip) ! { ! /* Skip the ".." or "../" and reset the counter for the ! * components that might be stripped later on. */ ! p = tail; ! components = 0; ! } ! else ! { ! /* Strip previous component. If the result would get empty ! * and there is no trailing path separator, leave a single ! * "." instead. If we are at the end of the file name and ! * there is no trailing path separator and a preceding ! * component is left after stripping, strip its trailing ! * path separator as well. */ ! if (p == start && relative && tail[-1] == '.') ! { ! *p++ = '.'; ! *p = NUL; ! } ! else ! { ! if (p > start && tail[-1] == '.') ! --p; ! movetail(p, tail); /* strip previous component */ ! } ! ! --components; ! } ! } ! else if (p == start && !relative) /* leading "/.." or "/../" */ ! movetail(p, tail); /* strip ".." or "../" */ ! else ! { ! if (p == start + 2 && p[-2] == '.') /* leading "./../" */ ! { ! movetail(p - 2, p); /* strip leading "./" */ ! tail -= 2; ! } ! p = tail; /* skip to char after ".." or "../" */ } } else { ++components; /* simple path component */ p = getnextcomp(p); } ! } while (*p != NUL); #endif /* !AMIGA */ } *** ../vim-6.2.063/src/testdir/test49.vim Fri May 30 21:45:31 2003 --- src/testdir/test49.vim Thu Jul 31 18:25:02 2003 *************** *** 1,6 **** " Vim script language tests " Author: Servatius Brandt ! " Last Change: 2003 May 30 "------------------------------------------------------------------------------- " Test environment {{{1 --- 1,6 ---- " Vim script language tests " Author: Servatius Brandt ! " Last Change: 2003 Jul 30 "------------------------------------------------------------------------------- " Test environment {{{1 *************** *** 5076,5082 **** Xpath 1048576 " X: 1048576 let exception = v:exception let throwpoint = v:throwpoint ! call CHECK(7, "autsch", scriptT, '\<6\>') finally Xpath 2097152 " X: 2097152 let exception = v:exception --- 5076,5085 ---- Xpath 1048576 " X: 1048576 let exception = v:exception let throwpoint = v:throwpoint ! " Symbolic links in tempname()s are not resolved, whereas resolving ! " is done for v:throwpoint. Resolve the temporary file name for ! " scriptT, so that it can be matched against v:throwpoint. ! call CHECK(7, "autsch", resolve(scriptT), '\<6\>') finally Xpath 2097152 " X: 2097152 let exception = v:exception *************** *** 5091,5097 **** Xpath 8388608 " X: 8388608 let exception = v:exception let throwpoint = v:throwpoint ! call CHECK(9, "brrrr", scriptT, '\<8\>') finally Xpath 16777216 " X: 16777216 let exception = v:exception --- 5094,5101 ---- Xpath 8388608 " X: 8388608 let exception = v:exception let throwpoint = v:throwpoint ! " Resolve scriptT for matching it against v:throwpoint. ! call CHECK(9, "brrrr", resolve(scriptT), '\<8\>') finally Xpath 16777216 " X: 16777216 let exception = v:exception *** ../vim-6.2.063/src/version.c Sun Aug 10 22:24:37 2003 --- src/version.c Sun Aug 10 22:26:34 2003 *************** *** 632,633 **** --- 632,635 ---- { /* Add new patch number below this line */ + /**/ + 64, /**/ -- From "know your smileys": :-{} Too much lipstick /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// Creator of Vim - Vi IMproved -- http://www.Vim.org \\\ \\\ Project leader for A-A-P -- http://www.A-A-P.org /// \\\ Help AIDS victims, buy here: http://ICCF-Holland.org/click1.html ///