I recently had a need for the "-r" option to ln while using Busybox, and it
turned out to be easy enough to add, so here's a patch against v1.24.2.

This is the first time I've poked at the BB source so it's possible I
overlooked something helpful in libbb, or am not using approved style, or
whatever. Suggestions welcome!

Thanks,

R
--- busybox-1.24.2/coreutils/ln.c.orig	2016-06-18 07:56:12.821783032 -0700
+++ busybox-1.24.2/coreutils/ln.c	2016-06-18 07:58:34.309778849 -0700
@@ -18,6 +18,7 @@
 //usage:     "\n	-s	Make symlinks instead of hardlinks"
 //usage:     "\n	-f	Remove existing destinations"
 //usage:     "\n	-n	Don't dereference symlinks - treat like normal file"
+//usage:     "\n	-r	Create symlinks relative to link location"
 //usage:     "\n	-b	Make a backup of the target (if exists) before link operation"
 //usage:     "\n	-S suf	Use suffix instead of ~ when making backup files"
 //usage:     "\n	-T	2nd arg must be a DIR"
@@ -40,6 +41,74 @@
 #define LN_SUFFIX           (1 << 4)
 #define LN_VERBOSE          (1 << 5)
 #define LN_LINKFILE         (1 << 6)
+#define LN_RELATIVE         (1 << 7)
+
+
+static int common_prefix_length(const char *path1, const char *path2)
+{
+	const char *start1 = path1;
+
+	/* NB: Function assumes absolute paths */
+
+	/* Find common string prefix */
+
+	while (*path1 && *path2 && *path1 == *path2) {
+		path1++;
+		path2++;
+	}
+
+	/* Search backwards to common path-component prefix */
+
+	while (path1 > start1 && *path1 != '/') {
+		path1--;
+	}
+
+	return (path1 + 1) - start1;
+}
+
+static char *xmalloc_relativized_target(const char *target, const char *src)
+{
+	const char *target_abs, *src_abs, *src_last, *sp;
+	char *out, *op;
+	int out_len, common_len;
+
+	/* Absolutize both inputs */
+
+	target_abs = bb_simplify_path(target);
+	src_abs = bb_simplify_path(src);
+
+	/* Output can't be longer than twice the longest absolutized input */
+
+	out_len = 1 + 2 * MAX(strlen(target_abs), strlen(src_abs));
+	out = op = xmalloc(out_len);
+
+	/* Skip common leading components */
+
+	common_len = common_prefix_length(target_abs, src_abs);
+	sp = src_abs + common_len;
+
+	/* Output "../" for each non-tail component remaining in src */
+
+	src_last = bb_get_last_path_component_nostrip(src_abs);
+	while (sp < src_last) {
+		if ('/' == *sp) {
+			*op++ = '.';
+			*op++ = '.';
+			*op++ = '/';
+		}
+		sp++;
+	}
+
+	/* Append target postfix */
+
+	strcpy(op, target_abs + common_len);
+
+	free((char *)target_abs);
+	free((char *)src_abs);
+
+	return out;
+}
+
 
 int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int ln_main(int argc, char **argv)
@@ -49,12 +118,13 @@
 	char *last;
 	char *src_name;
 	char *src;
+	char *target;
 	char *suffix = (char*)"~";
 	struct stat statbuf;
 	int (*link_func)(const char *, const char *);
 
 	opt_complementary = "-1"; /* min one arg */
-	opts = getopt32(argv, "sfnbS:vT", &suffix);
+	opts = getopt32(argv, "sfnbS:vTr", &suffix);
 
 	last = argv[argc - 1];
 	argv += optind;
@@ -120,19 +190,28 @@
 		}
 
 		link_func = link;
+		target = *argv;
 		if (opts & LN_SYMLINK) {
 			link_func = symlink;
+			if (opts & LN_RELATIVE) {
+				target = xmalloc_relativized_target(*argv, src);
+			}
 		}
 
 		if (opts & LN_VERBOSE) {
-			printf("'%s' -> '%s'\n", src, *argv);
+			printf("'%s' -> '%s'\n", src, target);
 		}
 
-		if (link_func(*argv, src) != 0) {
+		if (link_func(target, src) != 0) {
 			bb_simple_perror_msg(src);
 			status = EXIT_FAILURE;
 		}
 
+		if (target != *argv) {
+			free(target);
+			target = NULL;
+		}
+
 		free(src_name);
 	} while ((++argv)[1]);
 
_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to