libbb: better coreutils compatibility for realpath
No binary (build failed)
—
(log yok)
| @@ -578,7 +578,7 @@ DIR *xopendir(const char *path) FAST_FUNC; | |
| DIR *warn_opendir(const char *path) FAST_FUNC; | |
| char *xmalloc_realpath(const char *path) FAST_FUNC RETURNS_MALLOC; | |
| - | char *xmalloc_realpath_coreutils(const char *path) FAST_FUNC RETURNS_MALLOC; |
| + | char *xmalloc_realpath_coreutils(char *path) FAST_FUNC RETURNS_MALLOC; |
| char *xmalloc_readlink(const char *path) FAST_FUNC RETURNS_MALLOC; | |
| char *xmalloc_readlink_or_warn(const char *path) FAST_FUNC RETURNS_MALLOC; | |
| /* !RETURNS_MALLOC: it's a realloc-like function */ | |
| @@ -123,7 +123,7 @@ char* FAST_FUNC xmalloc_realpath(const char *path) | |
| #endif | |
| } | |
| - | char* FAST_FUNC xmalloc_realpath_coreutils(const char *path) |
| + | char* FAST_FUNC xmalloc_realpath_coreutils(char *path) |
| { | |
| char *buf; | |
| @@ -137,32 +137,19 @@ char* FAST_FUNC xmalloc_realpath_coreutils(const char *path) | |
| * (the directory must exist). | |
| */ | |
| if (!buf && errno == ENOENT) { | |
| - | char *last_slash = strrchr(path, '/'); |
| - | if (last_slash) { |
| - | *last_slash++ = '\0'; |
| - | buf = xmalloc_realpath(path); |
| - | if (buf) { |
| - | unsigned len = strlen(buf); |
| - | buf = xrealloc(buf, len + strlen(last_slash) + 2); |
| - | buf[len++] = '/'; |
| - | strcpy(buf + len, last_slash); |
| - | } |
| - | } else { |
| - | char *target = xmalloc_readlink(path); |
| - | if (target) { |
| - | char *cwd; |
| - | if (target[0] == '/') { |
| - | /* |
| - | * $ ln -s /bin/qwe symlink # note: /bin is a link to /usr/bin |
| - | * $ readlink -f symlink |
| - | * /usr/bin/qwe/target_does_not_exist |
| - | * $ realpath symlink |
| - | * /usr/bin/qwe/target_does_not_exist |
| - | */ |
| - | buf = xmalloc_realpath_coreutils(target); |
| - | free(target); |
| - | return buf; |
| - | } |
| + | char *target, c, *last_slash; |
| + | size_t i; |
| + | |
| + | target = xmalloc_readlink(path); |
| + | if (target) { |
| + | /* |
| + | * $ ln -s /bin/qwe symlink # note: /bin is a link to /usr/bin |
| + | * $ readlink -f symlink |
| + | * /usr/bin/qwe |
| + | * $ realpath symlink |
| + | * /usr/bin/qwe |
| + | */ |
| + | if (target[0] != '/') { |
| /* | |
| * $ ln -s target_does_not_exist symlink | |
| * $ readlink -f symlink | |
| @@ -170,13 +157,41 @@ char* FAST_FUNC xmalloc_realpath_coreutils(const char *path) | |
| * $ realpath symlink | |
| * /CURDIR/target_does_not_exist | |
| */ | |
| - | cwd = xrealloc_getcwd_or_warn(NULL); |
| - | buf = concat_path_file(cwd, target); |
| + | char *cwd = xrealloc_getcwd_or_warn(NULL); |
| + | char *tmp = concat_path_file(cwd, target); |
| free(cwd); | |
| free(target); | |
| - | return buf; |
| + | target = tmp; |
| + | } |
| + | buf = xmalloc_realpath_coreutils(target); |
| + | free(target); |
| + | return buf; |
| + | } |
| + | |
| + | /* ignore leading and trailing slashes */ |
| + | while (path[0] == '/' && path[1] == '/') |
| + | ++path; |
| + | i = strlen(path) - 1; |
| + | while (i > 0 && path[i] == '/') |
| + | i--; |
| + | c = path[i + 1]; |
| + | path[i + 1] = '\0'; |
| + | |
| + | last_slash = strrchr(path, '/'); |
| + | if (last_slash == path) |
| + | buf = xstrdup(path); |
| + | else if (last_slash) { |
| + | *last_slash = '\0'; |
| + | buf = xmalloc_realpath(path); |
| + | *last_slash++ = '/'; |
| + | if (buf) { |
| + | unsigned len = strlen(buf); |
| + | buf = xrealloc(buf, len + strlen(last_slash) + 2); |
| + | buf[len++] = '/'; |
| + | strcpy(buf + len, last_slash); |
| } | |
| } | |
| + | path[i + 1] = c; |
| } | |
| return buf; | |
| @@ -0,0 +1,45 @@ | |
| + | #!/bin/sh |
| + | |
| + | # Realpath tests. |
| + | # Copyright 2006 by Natanael Copa <[email protected]> |
| + | # Copyright 2021 by Ron Yorston <[email protected]> |
| + | # Licensed under GPLv2, see file LICENSE in this source tree. |
| + | |
| + | . ./testing.sh |
| + | |
| + | unset LC_ALL |
| + | unset LC_MESSAGES |
| + | unset LANG |
| + | unset LANGUAGE |
| + | |
| + | TESTDIR=realpath_testdir |
| + | TESTLINK1="link1" |
| + | TESTLINK2="link2" |
| + | |
| + | # create the dir and test files |
| + | mkdir -p "./$TESTDIR" |
| + | ln -s "./$TESTDIR/not_file" "./$TESTLINK1" |
| + | ln -s "./$TESTDIR/not_file/not_dir" "./$TESTLINK2" |
| + | |
| + | # shell's $PWD may leave symlinks unresolved. |
| + | # "pwd" may be a built-in and have the same problem. |
| + | # External pwd _can't_ have that problem (current dir on Unix is physical). |
| + | pwd=`which pwd` |
| + | pwd=`$pwd` |
| + | testing "realpath on non-existent absolute path 1" "realpath /not_file" "/not_file\n" "" "" |
| + | testing "realpath on non-existent absolute path 2" "realpath /not_file/" "/not_file\n" "" "" |
| + | testing "realpath on non-existent absolute path 3" "realpath //not_file" "/not_file\n" "" "" |
| + | testing "realpath on non-existent absolute path 4" "realpath /not_dir/not_file 2>&1" "realpath: /not_dir/not_file: No such file or directory\n" "" "" |
| + | |
| + | testing "realpath on non-existent local file 1" "realpath $TESTDIR/not_file" "$pwd/$TESTDIR/not_file\n" "" "" |
| + | testing "realpath on non-existent local file 2" "realpath $TESTDIR/not_dir/not_file 2>&1" "realpath: $TESTDIR/not_dir/not_file: No such file or directory\n" "" "" |
| + | |
| + | testing "realpath on link to non-existent file 1" "realpath $TESTLINK1" "$pwd/$TESTDIR/not_file\n" "" "" |
| + | testing "realpath on link to non-existent file 2" "realpath $TESTLINK2 2>&1" "realpath: $TESTLINK2: No such file or directory\n" "" "" |
| + | testing "realpath on link to non-existent file 3" "realpath ./$TESTLINK1" "$pwd/$TESTDIR/not_file\n" "" "" |
| + | testing "realpath on link to non-existent file 4" "realpath ./$TESTLINK2 2>&1" "realpath: ./$TESTLINK2: No such file or directory\n" "" "" |
| + | |
| + | # clean up |
| + | rm -r "$TESTLINK1" "$TESTLINK2" "$TESTDIR" |
| + | |
| + | exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) |