From: Philip Hazelden Date: Tue, 3 Jan 2023 19:43:28 +0000 (+0000) Subject: `rename -s` treats paths the same as without `-s`. X-Git-Tag: v2.39-rc1~200^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fa7abf332fed6e89535c00b97ca165eb135db588;p=thirdparty%2Futil-linux.git `rename -s` treats paths the same as without `-s`. That is, it only updates the final path component, unless either the expression or the replacement contains a '/'. As discussed in #1962. --- diff --git a/misc-utils/rename.1.adoc b/misc-utils/rename.1.adoc index 11518e94cf..1f6225fb14 100644 --- a/misc-utils/rename.1.adoc +++ b/misc-utils/rename.1.adoc @@ -53,7 +53,7 @@ The renaming has no safeguards by default or without any one of the options *--n If the _expression_ is empty, then by default _replacement_ will be added to the start of the filename. With *--all*, _replacement_ will be inserted in between every two characters of the filename, as well as at the start and end. -Normally, only the final path component of a filename is updated. But if either _expression_ or _replacement_ contains a _/_, the full path is updated. This can cause a file to be moved between folders. Creating folders, and moving files between filesystems, is not supported. With *--symlink*, the update is always applied to the link's full path. +Normally, only the final path component of a filename is updated. (Or with *--symlink*, only the final path component of the link.) But if either _expression_ or _replacement_ contains a _/_, the full path is updated. This can cause a file to be moved between folders. Creating folders, and moving files between filesystems, is not supported. == INTERACTIVE MODE diff --git a/misc-utils/rename.c b/misc-utils/rename.c index 80a0f31ca4..04e61ed5bb 100644 --- a/misc-utils/rename.c +++ b/misc-utils/rename.c @@ -48,12 +48,33 @@ static int tty_cbreak = 0; static int all = 0; static int last = 0; -static int string_replace(char *from, char *to, char *s, char *orig, char **newname) +/* Find the first place in `orig` where we'll perform a replacement. NULL if + there are no replacements to do. */ +static char *find_initial_replace(char *from, char *to, char *orig) +{ + char *search_start = orig; + + if (strchr(from, '/') == NULL && strchr(to, '/') == NULL) { + /* We only want to search in the final path component. Don't + include the final '/' in that component; if `from` is empty, + we want it to first match after the '/', not before. */ + search_start = strrchr(orig, '/'); + + if (search_start == NULL) + search_start = orig; + else + search_start++; + } + + return strstr(search_start, from); +} + +static int string_replace(char *from, char *to, char *orig, char **newname) { char *p, *q, *where; size_t count = 0, fromlen = strlen(from); - p = where = strstr(s, from); + p = where = find_initial_replace(from, to, orig); if (where == NULL) return 1; count++; @@ -157,7 +178,7 @@ static int do_symlink(char *from, char *to, char *s, int verbose, int noact, } target[ssz] = '\0'; - if (string_replace(from, to, target, target, &newname) != 0) + if (string_replace(from, to, target, &newname) != 0) ret = 0; if (ret == 1 && (nooverwrite || interactive) && lstat(newname, &sb) != 0) @@ -191,7 +212,7 @@ static int do_symlink(char *from, char *to, char *s, int verbose, int noact, static int do_file(char *from, char *to, char *s, int verbose, int noact, int nooverwrite, int interactive) { - char *newname = NULL, *file=NULL; + char *newname = NULL; int ret = 1; struct stat sb; @@ -208,16 +229,7 @@ static int do_file(char *from, char *to, char *s, int verbose, int noact, warn(_("stat of %s failed"), s); return 2; } - if (strchr(from, '/') == NULL && strchr(to, '/') == NULL) { - file = strrchr(s, '/'); - /* We're going to search for `from` in `file`. If `from` is - empty, we don't want it to match before the '/'. */ - if (file != NULL) - file++; - } - if (file == NULL) - file = s; - if (string_replace(from, to, file, s, &newname) != 0) + if (string_replace(from, to, s, &newname) != 0) return 0; if ((nooverwrite || interactive) && access(newname, F_OK) != 0) diff --git a/tests/expected/rename/subdir b/tests/expected/rename/subdir index 684cdb8fa8..c58d18eb15 100644 --- a/tests/expected/rename/subdir +++ b/tests/expected/rename/subdir @@ -6,18 +6,18 @@ rename_aa/xa rename_ab rename_ab/xa == symlinks == -rename_aa/sublink.1: `rename/aa' -> `renxme/aa' -rename_aa/sublink.2: `rename/aa' -> `renxme/aa' -rename_aa/sublink.3: `rename/aa' -> `renxme/aa' -rename_ab/sublink.1: `rename/aa' -> `renxme/aa' -rename_ab/sublink.2: `rename/aa' -> `renxme/aa' -rename_ab/sublink.3: `rename/aa' -> `renxme/aa' -renxme/aa -renxme/aa -renxme/aa -renxme/aa -renxme/aa -renxme/aa +rename_aa/sublink.1: `rename/aa' -> `rename/xa' +rename_aa/sublink.2: `rename/aa' -> `rename/xa' +rename_aa/sublink.3: `rename/aa' -> `rename/xa' +rename_ab/sublink.1: `rename/aa' -> `rename/xa' +rename_ab/sublink.2: `rename/aa' -> `rename/xa' +rename_ab/sublink.3: `rename/aa' -> `rename/xa' +rename/xa +rename/xa +rename/xa +rename/xa +rename/xa +rename/xa == fullpath == `./rename_path1' -> `./rename_path2' ./rename_path2 @@ -27,6 +27,10 @@ renxme/aa rename_path_a rename_path_b rename_path_b/test2 +rename_link: `some/nonexistent/path' -> `some/nonexisten_ath' +rename_link: `some/nonexisten_ath' -> `some/non/en_ath' +rename_link: `some/non/en_ath' -> `some/non/xn_ath' +some/non/xn_ath == empty 'from' == `rename_test' -> `_rename_test' `./rename_test' -> `./_rename_test' diff --git a/tests/ts/rename/subdir b/tests/ts/rename/subdir index d4a3281a10..c3de0acfcd 100755 --- a/tests/ts/rename/subdir +++ b/tests/ts/rename/subdir @@ -65,6 +65,13 @@ rm -f rename_path_a/test2 rename_path_b/test2 rmdir rename_path_a rename_path_b +ln -s some/nonexistent/path rename_link +$TS_CMD_RENAME -s -v t/p _ rename_link >> $TS_OUTPUT 2>> $TS_ERRLOG +$TS_CMD_RENAME -s -v exist / rename_link >> $TS_OUTPUT 2>> $TS_ERRLOG +$TS_CMD_RENAME -s -v e x rename_link >> $TS_OUTPUT 2>> $TS_ERRLOG +readlink rename_link >> $TS_OUTPUT 2>> $TS_ERRLOG +rm rename_link + echo "== empty 'from' ==" >> $TS_OUTPUT touch rename_test