]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
`rename -s` treats paths the same as without `-s`.
authorPhilip Hazelden <philip.hazelden@gmail.com>
Tue, 3 Jan 2023 19:43:28 +0000 (19:43 +0000)
committerPhilip Hazelden <philip.hazelden@gmail.com>
Tue, 3 Jan 2023 19:43:28 +0000 (19:43 +0000)
That is, it only updates the final path component, unless either the
expression or the replacement contains a '/'.

As discussed in #1962.

misc-utils/rename.1.adoc
misc-utils/rename.c
tests/expected/rename/subdir
tests/ts/rename/subdir

index 11518e94cf9af6ab58b9007c63cc2e0be884272e..1f6225fb14a479ad86ecf3e0b89d2999e2094ab0 100644 (file)
@@ -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
 
index 80a0f31ca4945edcc9636910ff1c9dc89a46a502..04e61ed5bbc757c0caec6e5e98a35cea7df88e3c 100644 (file)
@@ -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)
index 684cdb8fa842d4ba7006de14613c8a6bb1dceb54..c58d18eb153c1418e603d220276ce9959b7abedd 100644 (file)
@@ -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'
index d4a3281a100fd729a90536b0b5bc8183e4d49a34..c3de0acfcdecda385f1a60e9ce35517c1c7cb4c8 100755 (executable)
@@ -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