mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 04:42:03 +00:00
vim-patch:8.1.1979: code for handling file names is spread out (#36176)
Problem: Code for handling file names is spread out.
Solution: Move code to new filepath.c file. Graduate FEAT_MODIFY_FNAME.
b005cd80cf
Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
301
src/nvim/eval.c
301
src/nvim/eval.c
@@ -6919,307 +6919,6 @@ void last_set_msg(sctx_T script_ctx)
|
||||
verbose_leave();
|
||||
}
|
||||
|
||||
/// Adjust a filename, according to a string of modifiers.
|
||||
/// *fnamep must be NUL terminated when called. When returning, the length is
|
||||
/// determined by *fnamelen.
|
||||
/// Returns VALID_ flags or -1 for failure.
|
||||
/// When there is an error, *fnamep is set to NULL.
|
||||
///
|
||||
/// @param src string with modifiers
|
||||
/// @param tilde_file "~" is a file name, not $HOME
|
||||
/// @param usedlen characters after src that are used
|
||||
/// @param fnamep file name so far
|
||||
/// @param bufp buffer for allocated file name or NULL
|
||||
/// @param fnamelen length of fnamep
|
||||
int modify_fname(char *src, bool tilde_file, size_t *usedlen, char **fnamep, char **bufp,
|
||||
size_t *fnamelen)
|
||||
{
|
||||
int valid = 0;
|
||||
char *s, *p, *pbuf;
|
||||
char dirname[MAXPATHL];
|
||||
bool has_fullname = false;
|
||||
bool has_homerelative = false;
|
||||
|
||||
repeat:
|
||||
// ":p" - full path/file_name
|
||||
if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') {
|
||||
has_fullname = true;
|
||||
|
||||
valid |= VALID_PATH;
|
||||
*usedlen += 2;
|
||||
|
||||
// Expand "~/path" for all systems and "~user/path" for Unix
|
||||
if ((*fnamep)[0] == '~'
|
||||
#if !defined(UNIX)
|
||||
&& ((*fnamep)[1] == '/'
|
||||
# ifdef BACKSLASH_IN_FILENAME
|
||||
|| (*fnamep)[1] == '\\'
|
||||
# endif
|
||||
|| (*fnamep)[1] == NUL)
|
||||
#endif
|
||||
&& !(tilde_file && (*fnamep)[1] == NUL)) {
|
||||
*fnamep = expand_env_save(*fnamep);
|
||||
xfree(*bufp); // free any allocated file name
|
||||
*bufp = *fnamep;
|
||||
if (*fnamep == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// When "/." or "/.." is used: force expansion to get rid of it.
|
||||
for (p = *fnamep; *p != NUL; MB_PTR_ADV(p)) {
|
||||
if (vim_ispathsep(*p)
|
||||
&& p[1] == '.'
|
||||
&& (p[2] == NUL
|
||||
|| vim_ispathsep(p[2])
|
||||
|| (p[2] == '.'
|
||||
&& (p[3] == NUL || vim_ispathsep(p[3]))))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FullName_save() is slow, don't use it when not needed.
|
||||
if (*p != NUL || !vim_isAbsName(*fnamep)) {
|
||||
*fnamep = FullName_save(*fnamep, *p != NUL);
|
||||
xfree(*bufp); // free any allocated file name
|
||||
*bufp = *fnamep;
|
||||
if (*fnamep == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Append a path separator to a directory.
|
||||
if (os_isdir(*fnamep)) {
|
||||
// Make room for one or two extra characters.
|
||||
*fnamep = xstrnsave(*fnamep, strlen(*fnamep) + 2);
|
||||
xfree(*bufp); // free any allocated file name
|
||||
*bufp = *fnamep;
|
||||
add_pathsep(*fnamep);
|
||||
}
|
||||
}
|
||||
|
||||
int c;
|
||||
|
||||
// ":." - path relative to the current directory
|
||||
// ":~" - path relative to the home directory
|
||||
// ":8" - shortname path - postponed till after
|
||||
while (src[*usedlen] == ':'
|
||||
&& ((c = (uint8_t)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) {
|
||||
*usedlen += 2;
|
||||
if (c == '8') {
|
||||
continue;
|
||||
}
|
||||
pbuf = NULL;
|
||||
// Need full path first (use expand_env() to remove a "~/")
|
||||
if (!has_fullname && !has_homerelative) {
|
||||
if (**fnamep == '~') {
|
||||
p = pbuf = expand_env_save(*fnamep);
|
||||
} else {
|
||||
p = pbuf = FullName_save(*fnamep, false);
|
||||
}
|
||||
} else {
|
||||
p = *fnamep;
|
||||
}
|
||||
|
||||
has_fullname = false;
|
||||
|
||||
if (p != NULL) {
|
||||
if (c == '.') {
|
||||
os_dirname(dirname, MAXPATHL);
|
||||
if (has_homerelative) {
|
||||
s = xstrdup(dirname);
|
||||
home_replace(NULL, s, dirname, MAXPATHL, true);
|
||||
xfree(s);
|
||||
}
|
||||
size_t namelen = strlen(dirname);
|
||||
|
||||
// Do not call shorten_fname() here since it removes the prefix
|
||||
// even though the path does not have a prefix.
|
||||
if (path_fnamencmp(p, dirname, namelen) == 0) {
|
||||
p += namelen;
|
||||
if (vim_ispathsep(*p)) {
|
||||
while (*p && vim_ispathsep(*p)) {
|
||||
p++;
|
||||
}
|
||||
*fnamep = p;
|
||||
if (pbuf != NULL) {
|
||||
// free any allocated file name
|
||||
xfree(*bufp);
|
||||
*bufp = pbuf;
|
||||
pbuf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
home_replace(NULL, p, dirname, MAXPATHL, true);
|
||||
// Only replace it when it starts with '~'
|
||||
if (*dirname == '~') {
|
||||
s = xstrdup(dirname);
|
||||
assert(s != NULL); // suppress clang "Argument with 'nonnull' attribute passed null"
|
||||
*fnamep = s;
|
||||
xfree(*bufp);
|
||||
*bufp = s;
|
||||
has_homerelative = true;
|
||||
}
|
||||
}
|
||||
xfree(pbuf);
|
||||
}
|
||||
}
|
||||
|
||||
char *tail = path_tail(*fnamep);
|
||||
*fnamelen = strlen(*fnamep);
|
||||
|
||||
// ":h" - head, remove "/file_name", can be repeated
|
||||
// Don't remove the first "/" or "c:\"
|
||||
while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') {
|
||||
valid |= VALID_HEAD;
|
||||
*usedlen += 2;
|
||||
s = get_past_head(*fnamep);
|
||||
while (tail > s && after_pathsep(s, tail)) {
|
||||
MB_PTR_BACK(*fnamep, tail);
|
||||
}
|
||||
*fnamelen = (size_t)(tail - *fnamep);
|
||||
if (*fnamelen == 0) {
|
||||
// Result is empty. Turn it into "." to make ":cd %:h" work.
|
||||
xfree(*bufp);
|
||||
*bufp = *fnamep = tail = xstrdup(".");
|
||||
*fnamelen = 1;
|
||||
} else {
|
||||
while (tail > s && !after_pathsep(s, tail)) {
|
||||
MB_PTR_BACK(*fnamep, tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ":8" - shortname
|
||||
if (src[*usedlen] == ':' && src[*usedlen + 1] == '8') {
|
||||
*usedlen += 2;
|
||||
}
|
||||
|
||||
// ":t" - tail, just the basename
|
||||
if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') {
|
||||
*usedlen += 2;
|
||||
*fnamelen -= (size_t)(tail - *fnamep);
|
||||
*fnamep = tail;
|
||||
}
|
||||
|
||||
// ":e" - extension, can be repeated
|
||||
// ":r" - root, without extension, can be repeated
|
||||
while (src[*usedlen] == ':'
|
||||
&& (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) {
|
||||
// find a '.' in the tail:
|
||||
// - for second :e: before the current fname
|
||||
// - otherwise: The last '.'
|
||||
const bool is_second_e = *fnamep > tail;
|
||||
if (src[*usedlen + 1] == 'e' && is_second_e) {
|
||||
s = (*fnamep) - 2;
|
||||
} else {
|
||||
s = (*fnamep) + *fnamelen - 1;
|
||||
}
|
||||
|
||||
for (; s > tail; s--) {
|
||||
if (s[0] == '.') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (src[*usedlen + 1] == 'e') {
|
||||
if (s > tail || (0 && is_second_e && s == tail)) {
|
||||
// we stopped at a '.' (so anchor to &'.' + 1)
|
||||
char *newstart = s + 1;
|
||||
size_t distance_stepped_back = (size_t)(*fnamep - newstart);
|
||||
*fnamelen += distance_stepped_back;
|
||||
*fnamep = newstart;
|
||||
} else if (*fnamep <= tail) {
|
||||
*fnamelen = 0;
|
||||
}
|
||||
} else {
|
||||
// :r - Remove one extension
|
||||
//
|
||||
// Ensure that `s` doesn't go before `*fnamep`,
|
||||
// since then we're taking too many roots:
|
||||
//
|
||||
// "path/to/this.file.ext" :e:e:r:r
|
||||
// ^ ^-------- *fnamep
|
||||
// +------------- tail
|
||||
//
|
||||
// Also ensure `s` doesn't go before `tail`,
|
||||
// since then we're taking too many roots again:
|
||||
//
|
||||
// "path/to/this.file.ext" :r:r:r
|
||||
// ^ ^------------- tail
|
||||
// +--------------------- *fnamep
|
||||
if (s > MAX(tail, *fnamep)) {
|
||||
*fnamelen = (size_t)(s - *fnamep);
|
||||
}
|
||||
}
|
||||
*usedlen += 2;
|
||||
}
|
||||
|
||||
// ":s?pat?foo?" - substitute
|
||||
// ":gs?pat?foo?" - global substitute
|
||||
if (src[*usedlen] == ':'
|
||||
&& (src[*usedlen + 1] == 's'
|
||||
|| (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) {
|
||||
bool didit = false;
|
||||
|
||||
char *flags = "";
|
||||
s = src + *usedlen + 2;
|
||||
if (src[*usedlen + 1] == 'g') {
|
||||
flags = "g";
|
||||
s++;
|
||||
}
|
||||
|
||||
int sep = (uint8_t)(*s++);
|
||||
if (sep) {
|
||||
// find end of pattern
|
||||
p = vim_strchr(s, sep);
|
||||
if (p != NULL) {
|
||||
char *const pat = xmemdupz(s, (size_t)(p - s));
|
||||
s = p + 1;
|
||||
// find end of substitution
|
||||
p = vim_strchr(s, sep);
|
||||
if (p != NULL) {
|
||||
char *const sub = xmemdupz(s, (size_t)(p - s));
|
||||
char *const str = xmemdupz(*fnamep, *fnamelen);
|
||||
*usedlen = (size_t)(p + 1 - src);
|
||||
size_t slen;
|
||||
s = do_string_sub(str, *fnamelen, pat, sub, NULL, flags, &slen);
|
||||
*fnamep = s;
|
||||
*fnamelen = slen;
|
||||
xfree(*bufp);
|
||||
*bufp = s;
|
||||
didit = true;
|
||||
xfree(sub);
|
||||
xfree(str);
|
||||
}
|
||||
xfree(pat);
|
||||
}
|
||||
// after using ":s", repeat all the modifiers
|
||||
if (didit) {
|
||||
goto repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') {
|
||||
// vim_strsave_shellescape() needs a NUL terminated string.
|
||||
c = (uint8_t)(*fnamep)[*fnamelen];
|
||||
if (c != NUL) {
|
||||
(*fnamep)[*fnamelen] = NUL;
|
||||
}
|
||||
p = vim_strsave_shellescape(*fnamep, false, false);
|
||||
if (c != NUL) {
|
||||
(*fnamep)[*fnamelen] = (char)c;
|
||||
}
|
||||
xfree(*bufp);
|
||||
*bufp = *fnamep = p;
|
||||
*fnamelen = strlen(p);
|
||||
*usedlen += 2;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/// Perform a substitution on "str" with pattern "pat" and substitute "sub".
|
||||
/// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL.
|
||||
/// "flags" can be "g" to do a global substitute.
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "nvim/gettext_defs.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/macros_defs.h"
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/message.h"
|
||||
#include "nvim/option_vars.h"
|
||||
@@ -38,7 +39,7 @@
|
||||
#include "nvim/os/fileio_defs.h"
|
||||
#include "nvim/os/fs.h"
|
||||
#include "nvim/os/fs_defs.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/path.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
#include "nvim/strings.h"
|
||||
@@ -50,6 +51,307 @@
|
||||
|
||||
static const char e_error_while_writing_str[] = N_("E80: Error while writing: %s");
|
||||
|
||||
/// Adjust a filename, according to a string of modifiers.
|
||||
/// *fnamep must be NUL terminated when called. When returning, the length is
|
||||
/// determined by *fnamelen.
|
||||
/// Returns VALID_ flags or -1 for failure.
|
||||
/// When there is an error, *fnamep is set to NULL.
|
||||
///
|
||||
/// @param src string with modifiers
|
||||
/// @param tilde_file "~" is a file name, not $HOME
|
||||
/// @param usedlen characters after src that are used
|
||||
/// @param fnamep file name so far
|
||||
/// @param bufp buffer for allocated file name or NULL
|
||||
/// @param fnamelen length of fnamep
|
||||
int modify_fname(char *src, bool tilde_file, size_t *usedlen, char **fnamep, char **bufp,
|
||||
size_t *fnamelen)
|
||||
{
|
||||
int valid = 0;
|
||||
char *s, *p, *pbuf;
|
||||
char dirname[MAXPATHL];
|
||||
bool has_fullname = false;
|
||||
bool has_homerelative = false;
|
||||
|
||||
repeat:
|
||||
// ":p" - full path/file_name
|
||||
if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') {
|
||||
has_fullname = true;
|
||||
|
||||
valid |= VALID_PATH;
|
||||
*usedlen += 2;
|
||||
|
||||
// Expand "~/path" for all systems and "~user/path" for Unix
|
||||
if ((*fnamep)[0] == '~'
|
||||
#if !defined(UNIX)
|
||||
&& ((*fnamep)[1] == '/'
|
||||
# ifdef BACKSLASH_IN_FILENAME
|
||||
|| (*fnamep)[1] == '\\'
|
||||
# endif
|
||||
|| (*fnamep)[1] == NUL)
|
||||
#endif
|
||||
&& !(tilde_file && (*fnamep)[1] == NUL)) {
|
||||
*fnamep = expand_env_save(*fnamep);
|
||||
xfree(*bufp); // free any allocated file name
|
||||
*bufp = *fnamep;
|
||||
if (*fnamep == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// When "/." or "/.." is used: force expansion to get rid of it.
|
||||
for (p = *fnamep; *p != NUL; MB_PTR_ADV(p)) {
|
||||
if (vim_ispathsep(*p)
|
||||
&& p[1] == '.'
|
||||
&& (p[2] == NUL
|
||||
|| vim_ispathsep(p[2])
|
||||
|| (p[2] == '.'
|
||||
&& (p[3] == NUL || vim_ispathsep(p[3]))))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FullName_save() is slow, don't use it when not needed.
|
||||
if (*p != NUL || !vim_isAbsName(*fnamep)) {
|
||||
*fnamep = FullName_save(*fnamep, *p != NUL);
|
||||
xfree(*bufp); // free any allocated file name
|
||||
*bufp = *fnamep;
|
||||
if (*fnamep == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Append a path separator to a directory.
|
||||
if (os_isdir(*fnamep)) {
|
||||
// Make room for one or two extra characters.
|
||||
*fnamep = xstrnsave(*fnamep, strlen(*fnamep) + 2);
|
||||
xfree(*bufp); // free any allocated file name
|
||||
*bufp = *fnamep;
|
||||
add_pathsep(*fnamep);
|
||||
}
|
||||
}
|
||||
|
||||
int c;
|
||||
|
||||
// ":." - path relative to the current directory
|
||||
// ":~" - path relative to the home directory
|
||||
// ":8" - shortname path - postponed till after
|
||||
while (src[*usedlen] == ':'
|
||||
&& ((c = (uint8_t)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) {
|
||||
*usedlen += 2;
|
||||
if (c == '8') {
|
||||
continue;
|
||||
}
|
||||
pbuf = NULL;
|
||||
// Need full path first (use expand_env() to remove a "~/")
|
||||
if (!has_fullname && !has_homerelative) {
|
||||
if (**fnamep == '~') {
|
||||
p = pbuf = expand_env_save(*fnamep);
|
||||
} else {
|
||||
p = pbuf = FullName_save(*fnamep, false);
|
||||
}
|
||||
} else {
|
||||
p = *fnamep;
|
||||
}
|
||||
|
||||
has_fullname = false;
|
||||
|
||||
if (p != NULL) {
|
||||
if (c == '.') {
|
||||
os_dirname(dirname, MAXPATHL);
|
||||
if (has_homerelative) {
|
||||
s = xstrdup(dirname);
|
||||
home_replace(NULL, s, dirname, MAXPATHL, true);
|
||||
xfree(s);
|
||||
}
|
||||
size_t namelen = strlen(dirname);
|
||||
|
||||
// Do not call shorten_fname() here since it removes the prefix
|
||||
// even though the path does not have a prefix.
|
||||
if (path_fnamencmp(p, dirname, namelen) == 0) {
|
||||
p += namelen;
|
||||
if (vim_ispathsep(*p)) {
|
||||
while (*p && vim_ispathsep(*p)) {
|
||||
p++;
|
||||
}
|
||||
*fnamep = p;
|
||||
if (pbuf != NULL) {
|
||||
// free any allocated file name
|
||||
xfree(*bufp);
|
||||
*bufp = pbuf;
|
||||
pbuf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
home_replace(NULL, p, dirname, MAXPATHL, true);
|
||||
// Only replace it when it starts with '~'
|
||||
if (*dirname == '~') {
|
||||
s = xstrdup(dirname);
|
||||
assert(s != NULL); // suppress clang "Argument with 'nonnull' attribute passed null"
|
||||
*fnamep = s;
|
||||
xfree(*bufp);
|
||||
*bufp = s;
|
||||
has_homerelative = true;
|
||||
}
|
||||
}
|
||||
xfree(pbuf);
|
||||
}
|
||||
}
|
||||
|
||||
char *tail = path_tail(*fnamep);
|
||||
*fnamelen = strlen(*fnamep);
|
||||
|
||||
// ":h" - head, remove "/file_name", can be repeated
|
||||
// Don't remove the first "/" or "c:\"
|
||||
while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') {
|
||||
valid |= VALID_HEAD;
|
||||
*usedlen += 2;
|
||||
s = get_past_head(*fnamep);
|
||||
while (tail > s && after_pathsep(s, tail)) {
|
||||
MB_PTR_BACK(*fnamep, tail);
|
||||
}
|
||||
*fnamelen = (size_t)(tail - *fnamep);
|
||||
if (*fnamelen == 0) {
|
||||
// Result is empty. Turn it into "." to make ":cd %:h" work.
|
||||
xfree(*bufp);
|
||||
*bufp = *fnamep = tail = xstrdup(".");
|
||||
*fnamelen = 1;
|
||||
} else {
|
||||
while (tail > s && !after_pathsep(s, tail)) {
|
||||
MB_PTR_BACK(*fnamep, tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ":8" - shortname
|
||||
if (src[*usedlen] == ':' && src[*usedlen + 1] == '8') {
|
||||
*usedlen += 2;
|
||||
}
|
||||
|
||||
// ":t" - tail, just the basename
|
||||
if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') {
|
||||
*usedlen += 2;
|
||||
*fnamelen -= (size_t)(tail - *fnamep);
|
||||
*fnamep = tail;
|
||||
}
|
||||
|
||||
// ":e" - extension, can be repeated
|
||||
// ":r" - root, without extension, can be repeated
|
||||
while (src[*usedlen] == ':'
|
||||
&& (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) {
|
||||
// find a '.' in the tail:
|
||||
// - for second :e: before the current fname
|
||||
// - otherwise: The last '.'
|
||||
const bool is_second_e = *fnamep > tail;
|
||||
if (src[*usedlen + 1] == 'e' && is_second_e) {
|
||||
s = (*fnamep) - 2;
|
||||
} else {
|
||||
s = (*fnamep) + *fnamelen - 1;
|
||||
}
|
||||
|
||||
for (; s > tail; s--) {
|
||||
if (s[0] == '.') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (src[*usedlen + 1] == 'e') {
|
||||
if (s > tail || (0 && is_second_e && s == tail)) {
|
||||
// we stopped at a '.' (so anchor to &'.' + 1)
|
||||
char *newstart = s + 1;
|
||||
size_t distance_stepped_back = (size_t)(*fnamep - newstart);
|
||||
*fnamelen += distance_stepped_back;
|
||||
*fnamep = newstart;
|
||||
} else if (*fnamep <= tail) {
|
||||
*fnamelen = 0;
|
||||
}
|
||||
} else {
|
||||
// :r - Remove one extension
|
||||
//
|
||||
// Ensure that `s` doesn't go before `*fnamep`,
|
||||
// since then we're taking too many roots:
|
||||
//
|
||||
// "path/to/this.file.ext" :e:e:r:r
|
||||
// ^ ^-------- *fnamep
|
||||
// +------------- tail
|
||||
//
|
||||
// Also ensure `s` doesn't go before `tail`,
|
||||
// since then we're taking too many roots again:
|
||||
//
|
||||
// "path/to/this.file.ext" :r:r:r
|
||||
// ^ ^------------- tail
|
||||
// +--------------------- *fnamep
|
||||
if (s > MAX(tail, *fnamep)) {
|
||||
*fnamelen = (size_t)(s - *fnamep);
|
||||
}
|
||||
}
|
||||
*usedlen += 2;
|
||||
}
|
||||
|
||||
// ":s?pat?foo?" - substitute
|
||||
// ":gs?pat?foo?" - global substitute
|
||||
if (src[*usedlen] == ':'
|
||||
&& (src[*usedlen + 1] == 's'
|
||||
|| (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) {
|
||||
bool didit = false;
|
||||
|
||||
char *flags = "";
|
||||
s = src + *usedlen + 2;
|
||||
if (src[*usedlen + 1] == 'g') {
|
||||
flags = "g";
|
||||
s++;
|
||||
}
|
||||
|
||||
int sep = (uint8_t)(*s++);
|
||||
if (sep) {
|
||||
// find end of pattern
|
||||
p = vim_strchr(s, sep);
|
||||
if (p != NULL) {
|
||||
char *const pat = xmemdupz(s, (size_t)(p - s));
|
||||
s = p + 1;
|
||||
// find end of substitution
|
||||
p = vim_strchr(s, sep);
|
||||
if (p != NULL) {
|
||||
char *const sub = xmemdupz(s, (size_t)(p - s));
|
||||
char *const str = xmemdupz(*fnamep, *fnamelen);
|
||||
*usedlen = (size_t)(p + 1 - src);
|
||||
size_t slen;
|
||||
s = do_string_sub(str, *fnamelen, pat, sub, NULL, flags, &slen);
|
||||
*fnamep = s;
|
||||
*fnamelen = slen;
|
||||
xfree(*bufp);
|
||||
*bufp = s;
|
||||
didit = true;
|
||||
xfree(sub);
|
||||
xfree(str);
|
||||
}
|
||||
xfree(pat);
|
||||
}
|
||||
// after using ":s", repeat all the modifiers
|
||||
if (didit) {
|
||||
goto repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') {
|
||||
// vim_strsave_shellescape() needs a NUL terminated string.
|
||||
c = (uint8_t)(*fnamep)[*fnamelen];
|
||||
if (c != NUL) {
|
||||
(*fnamep)[*fnamelen] = NUL;
|
||||
}
|
||||
p = vim_strsave_shellescape(*fnamep, false, false);
|
||||
if (c != NUL) {
|
||||
(*fnamep)[*fnamelen] = (char)c;
|
||||
}
|
||||
xfree(*bufp);
|
||||
*bufp = *fnamep = p;
|
||||
*fnamelen = strlen(p);
|
||||
*usedlen += 2;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/// "chdir(dir)" function
|
||||
void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
@@ -279,6 +581,34 @@ void f_findfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
findfilendir(argvars, rettv, FINDFILE_FILE);
|
||||
}
|
||||
|
||||
/// "fnamemodify({fname}, {mods})" function
|
||||
void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
char *fbuf = NULL;
|
||||
size_t len = 0;
|
||||
char buf[NUMBUFLEN];
|
||||
const char *fname = tv_get_string_chk(&argvars[0]);
|
||||
const char *const mods = tv_get_string_buf_chk(&argvars[1], buf);
|
||||
if (mods == NULL || fname == NULL) {
|
||||
fname = NULL;
|
||||
} else {
|
||||
len = strlen(fname);
|
||||
if (*mods != NUL) {
|
||||
size_t usedlen = 0;
|
||||
modify_fname((char *)mods, false, &usedlen,
|
||||
(char **)&fname, &fbuf, &len);
|
||||
}
|
||||
}
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
if (fname == NULL) {
|
||||
rettv->vval.v_string = NULL;
|
||||
} else {
|
||||
rettv->vval.v_string = xmemdupz(fname, len);
|
||||
}
|
||||
xfree(fbuf);
|
||||
}
|
||||
|
||||
/// `getcwd([{win}[, {tab}]])` function
|
||||
///
|
||||
/// Every scope not specified implies the currently selected scope object.
|
||||
@@ -1501,3 +1831,16 @@ void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// "browse(save, title, initdir, default)" function
|
||||
void f_browse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
rettv->vval.v_string = NULL;
|
||||
rettv->v_type = VAR_STRING;
|
||||
}
|
||||
|
||||
/// "browsedir(title, initdir)" function
|
||||
void f_browsedir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
f_browse(argvars, rettv, fptr);
|
||||
}
|
||||
|
||||
@@ -421,19 +421,6 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
}
|
||||
}
|
||||
|
||||
/// "browse(save, title, initdir, default)" function
|
||||
static void f_browse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
rettv->vval.v_string = NULL;
|
||||
rettv->v_type = VAR_STRING;
|
||||
}
|
||||
|
||||
/// "browsedir(title, initdir)" function
|
||||
static void f_browsedir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
f_browse(argvars, rettv, fptr);
|
||||
}
|
||||
|
||||
/// Get buffer by number or pattern.
|
||||
buf_T *tv_get_buf(typval_T *tv, int curtab_only)
|
||||
{
|
||||
@@ -1666,34 +1653,6 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
rettv->v_type = VAR_STRING;
|
||||
}
|
||||
|
||||
/// "fnamemodify({fname}, {mods})" function
|
||||
static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
char *fbuf = NULL;
|
||||
size_t len = 0;
|
||||
char buf[NUMBUFLEN];
|
||||
const char *fname = tv_get_string_chk(&argvars[0]);
|
||||
const char *const mods = tv_get_string_buf_chk(&argvars[1], buf);
|
||||
if (mods == NULL || fname == NULL) {
|
||||
fname = NULL;
|
||||
} else {
|
||||
len = strlen(fname);
|
||||
if (*mods != NUL) {
|
||||
size_t usedlen = 0;
|
||||
modify_fname((char *)mods, false, &usedlen,
|
||||
(char **)&fname, &fbuf, &len);
|
||||
}
|
||||
}
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
if (fname == NULL) {
|
||||
rettv->vval.v_string = NULL;
|
||||
} else {
|
||||
rettv->vval.v_string = xmemdupz(fname, len);
|
||||
}
|
||||
xfree(fbuf);
|
||||
}
|
||||
|
||||
/// "foreground()" function
|
||||
static void f_foreground(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "nvim/drawscreen.h"
|
||||
#include "nvim/edit.h"
|
||||
#include "nvim/errors.h"
|
||||
#include "nvim/eval/fs.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/eval/userfunc.h"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "nvim/cmdexpand.h"
|
||||
#include "nvim/cmdexpand_defs.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/fs.h"
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/log.h"
|
||||
|
||||
Reference in New Issue
Block a user