Add ln -r and relative_path() to lib, plus test.

And a few small cleanups while I was there.
This commit is contained in:
Rob Landley 2019-10-26 17:22:55 -05:00
parent dec46177db
commit 49feb50f22
4 changed files with 58 additions and 9 deletions

View File

@ -216,6 +216,7 @@ int mkpath(char *dir)
}
// Split a path into linked list of components, tracking head and tail of list.
// Assigns head of list to *list, returns address of ->next entry to extend list
// Filters out // entries with no contents.
struct string_list **splitpath(char *path, struct string_list **list)
{
@ -1028,6 +1029,34 @@ char *fileunderdir(char *file, char *dir)
return rc ? s2 : 0;
}
// return (malloced) relative path to get from "from" to "to"
char *relative_path(char *from, char *to)
{
char *s, *ret = 0;
int i, j, k;
if (!(from = xabspath(from, -1))) return 0;
if (!(to = xabspath(to, -1))) goto error;
// skip common directories from root
for (i = j = 0; from[i] && from[i] == to[i]; i++) if (to[i] == '/') j = i+1;
// count remaining destination directories
for (i = j, k = 0; from[i]; i++) if (from[i] == '/') k++;
if (!k) ret = xstrdup(to+j);
else {
s = ret = xmprintf("%*c%s", 3*k, ' ', to+j);
while (k--) memcpy(s+3*k, "../", 3);
}
error:
free(from);
free(to);
return ret;
}
// Execute a callback for each PID that matches a process name from a list.
void names_to_pid(char **names, int (*callback)(pid_t pid, char *name),
int scripts)

View File

@ -402,6 +402,7 @@ mode_t string_to_mode(char *mode_str, mode_t base);
void mode_to_string(mode_t mode, char *buf);
char *getbasename(char *name);
char *fileunderdir(char *file, char *dir);
char *relative_path(char *from, char *to);
void names_to_pid(char **names, int (*callback)(pid_t pid, char *name),
int scripts);

View File

@ -55,6 +55,7 @@ rm -rf file dir slink
testing "-t" "ln -st . one/two three && readlink two three" "one/two\nthree\n" \
"" ""
rm -f two three
touch file1 file2 && mkdir dir
testing "create_multiple_hardlinks" "ln file* dir/ &&
@ -78,3 +79,12 @@ testing "create_hardlink_and_remove_sourcefile" "ln file hlink &&
[ file -ef hlink ] && rm file && [ -f hlink ] && echo 'yes'" \
"yes\n" "" ""
rm -f file hlink
mkdir -p one/two
ln -s . circular
mkdir -p three
echo hello > three/four
testing "ln -r" \
"ln -sr circular/three/../three/four one/two/five && cat one/two/five" \
"hello\n" "" ""
rm -rf one three circular

View File

@ -4,7 +4,7 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/ln.html
USE_LN(NEWTOY(ln, "<1t:Tvnfs", TOYFLAG_BIN))
USE_LN(NEWTOY(ln, "<1rt:Tvnfs", TOYFLAG_BIN))
config LN
bool "ln"
@ -18,6 +18,7 @@ config LN
-s Create a symbolic link
-f Force the creation of the link, even if TO already exists
-n Symlink at TO treated as file
-r Create relative symlink from -> to
-t Create links in DIR
-T TO always treated as file, max 2 arguments
-v Verbose
@ -34,30 +35,37 @@ void ln_main(void)
{
char *dest = TT.t ? TT.t : toys.optargs[--toys.optc], *new;
struct stat buf;
int i;
int i, rc;
if (FLAG(T) && toys.optc>1) help_exit("Max 2 arguments");
// With one argument, create link in current directory.
if (!toys.optc) {
toys.optc++;
dest=".";
dest = ".";
}
if (FLAG(T) && toys.optc>1) help_exit("Max 2 arguments");
// Is destination a directory?
if (!((FLAG(n)||FLAG(T)) ? lstat : stat)(dest, &buf)) {
i = S_ISDIR(buf.st_mode);
if ((FLAG(T) && i) || (!i && (toys.optc>1 || TT.t)))
if ((i = S_ISDIR(buf.st_mode)) ? FLAG(T) : (toys.optc>1 || TT.t))
error_exit("'%s' %s a directory", dest, i ? "is" : "not");
} else buf.st_mode = 0;
for (i=0; i<toys.optc; i++) {
int rc;
char *oldnew, *try = toys.optargs[i];
char *oldnew = 0, *try = toys.optargs[i];
if (S_ISDIR(buf.st_mode)) new = xmprintf("%s/%s", dest, basename(try));
else new = dest;
if (FLAG(r)) {
try = relative_path(new, try);
if (!try) {
if (new != dest) free(new);
continue;
}
toys.optflags |= FLAG_s;
}
// Force needs to unlink the existing target (if any). Do that by creating
// a temp version and renaming it over the old one, so we can retain the
// old file in cases we can't replace it (such as hardlink between mounts).
@ -88,6 +96,7 @@ void ln_main(void)
FLAG(s) ? "symbolic" : "hard", try, new);
else if (FLAG(v)) fprintf(stderr, "'%s' -> '%s'\n", new, try);
if (try != toys.optargs[i]) free(try);
if (new != dest) free(new);
}
}