[git commit] libbb: introduce and use xasprintf_inplace()

Denys Vlasenko vda.linux at googlemail.com
Thu Feb 5 12:36:27 UTC 2026


commit: https://git.busybox.net/busybox/commit/?id=fddd93edbdbf8c5afbfdb3c01d82e082a8a82d1a
branch: https://git.busybox.net/busybox/log/?h=master

function                                             old     new   delta
xasprintf_and_free                                     -      49     +49
watch_main                                           269     282     +13
singlemount                                         1313    1315      +2
append_mount_options                                 157     149      -8
ip_port_str                                          122     112     -10
lsblk_main                                           869     858     -11
add_cmd                                             1178    1167     -11
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/4 up/down: 64/-40)             Total: 24 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 editors/sed.c             |  5 ++---
 include/libbb.h           |  2 ++
 libbb/xfuncs_printf.c     | 16 ++++++++++++++++
 modutils/modprobe-small.c |  4 +---
 networking/ifupdown.c     |  5 ++---
 networking/netstat.c      |  7 +++----
 procps/watch.c            |  7 ++-----
 util-linux/lsblk.c        | 11 +++++------
 util-linux/mount.c        | 25 +++++++++++++++----------
 9 files changed, 48 insertions(+), 34 deletions(-)

diff --git a/editors/sed.c b/editors/sed.c
index 6179c5e80..029e9b8e7 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -657,9 +657,8 @@ static void add_cmd(const char *cmdstr)
 
 	/* Append this line to any unfinished line from last time. */
 	if (G.add_cmd_line) {
-		char *tp = xasprintf("%s\n%s", G.add_cmd_line, cmdstr);
-		free(G.add_cmd_line);
-		cmdstr = G.add_cmd_line = tp;
+		cmdstr = xasprintf_inplace(G.add_cmd_line,
+			"%s\n%s", G.add_cmd_line, cmdstr);
 	}
 
 	/* If this line ends with unescaped backslash, request next line. */
diff --git a/include/libbb.h b/include/libbb.h
index 7cca925b9..6ce01ea94 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -950,6 +950,8 @@ int bb_putchar(int ch) FAST_FUNC;
 int bb_putchar_stderr(char ch) FAST_FUNC;
 int fputs_stdout(const char *s) FAST_FUNC;
 char *xasprintf(const char *format, ...) __attribute__ ((format(printf, 1, 2))) FAST_FUNC RETURNS_MALLOC;
+char *xasprintf_and_free(char *allocated, const char *format, ...) __attribute__ ((format(printf, 2, 3))) FAST_FUNC RETURNS_MALLOC;
+#define xasprintf_inplace(allocated, ...) ((allocated) = xasprintf_and_free((allocated), __VA_ARGS__))
 char *auto_string(char *str) FAST_FUNC;
 // gcc-4.1.1 still isn't good enough at optimizing it
 // (+200 bytes compared to macro)
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index 5d26e2bfa..ed10084b3 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -349,6 +349,22 @@ char* FAST_FUNC xasprintf(const char *format, ...)
 	return string_ptr;
 }
 
+char* FAST_FUNC xasprintf_and_free(char *allocated, const char *format, ...)
+{
+	va_list p;
+	int r;
+	char *string_ptr;
+
+	va_start(p, format);
+	r = vasprintf(&string_ptr, format, p);
+	va_end(p);
+
+	if (r < 0)
+		bb_die_memory_exhausted();
+	free(allocated);
+	return string_ptr;
+}
+
 void FAST_FUNC xsetenv(const char *key, const char *value)
 {
 	if (setenv(key, value, 1))
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 31a215a29..b3c0768ee 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -1010,9 +1010,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
 		char **arg = argv;
 		while (*++arg) {
 			/* Enclose options in quotes */
-			char *s = options;
-			options = xasprintf("%s \"%s\"", s ? s : "", *arg);
-			free(s);
+			xasprintf_inplace(options, "%s \"%s\"", options ? options : "", *arg);
 			*arg = NULL;
 		}
 # else
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index 9c3640be7..bc2dca506 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -895,16 +895,15 @@ static struct interfaces_file_t *read_interfaces(const char *filename, struct in
 	while ((buf = xmalloc_fgetline(f)) != NULL) {
 #if ENABLE_DESKTOP
 		/* Trailing "\" concatenates lines */
+//TODO: check how to handle "line\\" (double backslashes)
 		char *p;
 		while ((p = last_char_is(buf, '\\')) != NULL) {
 			*p = '\0';
 			rest_of_line = xmalloc_fgetline(f);
 			if (!rest_of_line)
 				break;
-			p = xasprintf("%s%s", buf, rest_of_line);
-			free(buf);
+			xasprintf_inplace(buf, "%s%s", buf, rest_of_line);
 			free(rest_of_line);
-			buf = p;
 		}
 #endif
 		rest_of_line = buf;
diff --git a/networking/netstat.c b/networking/netstat.c
index 807800a62..d7afa8fdd 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -391,7 +391,7 @@ static const char *get_sname(int port, const char *proto, int numeric)
 
 static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric)
 {
-	char *host, *host_port;
+	char *host;
 
 	/* Code which used "*" for INADDR_ANY is removed: it's ambiguous
 	 * in IPv6, while "0.0.0.0" is not. */
@@ -402,9 +402,8 @@ static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int
 	if (!host)
 		host = xmalloc_sockaddr2dotted_noport(addr);
 
-	host_port = xasprintf("%s:%s", host, get_sname(htons(port), proto, numeric));
-	free(host);
-	return host_port;
+	xasprintf_inplace(host, "%s:%s", host, get_sname(htons(port), proto, numeric));
+	return host;
 }
 
 struct inet_params {
diff --git a/procps/watch.c b/procps/watch.c
index b376fdc33..b828079e5 100644
--- a/procps/watch.c
+++ b/procps/watch.c
@@ -76,12 +76,9 @@ int watch_main(int argc UNUSED_PARAM, char **argv)
 	// watch ls -l "a /tmp" "2>&1" - ls won't see "a /tmp" as one param.
 	argv0 = argv;
 	// If -x, cmd is only used for printing header
-	cmd = *argv;
+	cmd = xstrdup(*argv);
 	while (*++argv)
-		cmd = xasprintf("%s %s", cmd, *argv);
-//leaks cmd ^^^
-//TODO: create and use xasprintf_inplace(DST, fmt, args_one_of_which_is_DST)
-//which frees old DST?
+		xasprintf_inplace(cmd, "%s %s", cmd, *argv);
 	if (opt & (1 << 3))
 		argv = argv0; // If -x, restore argv[0] to !NULL
 
diff --git a/util-linux/lsblk.c b/util-linux/lsblk.c
index a482bfbbc..3bd40bcb4 100644
--- a/util-linux/lsblk.c
+++ b/util-linux/lsblk.c
@@ -101,10 +101,10 @@ static char *get_mountpoints(const char *majmin)
 
 	mountpoints = NULL;
 	len = strlen(majmin);
-	p = G.mountinfo; // "/proc/self/mountinfo"
+	p = G.mountinfo; // contents of "/proc/self/mountinfo"
 	/* lines a-la "63 1 259:3 / /MNTPOINT per-mount_options - ext4 /dev/NAME per-superblock_options" */
 	while (*p) {
-		char *e, *f;
+		char *e;
 
 		p = skip_non_whitespace(p);
 		if (*p != ' ') break;
@@ -127,12 +127,11 @@ static char *get_mountpoints(const char *majmin)
 		// at " /MNTPOINT"
 		e = skip_non_whitespace(p + 1);
 		// e is at the end of " /MNTPOINT"
-		f = mountpoints;
 // NO. We return " /MNT1 /MNT2 /MNT3" _with_ leading space!
-//		if (!f)
+//		if (!mountpoints)
 //			p++;
-		mountpoints = xasprintf("%s%.*s", f ? f : "", (int)(e - p), p);
-		free(f);
+		xasprintf_inplace(mountpoints, "%s%.*s",
+			mountpoints ? mountpoints : "", (int)(e - p), p);
 	}
 	return mountpoints;
 }
diff --git a/util-linux/mount.c b/util-linux/mount.c
index d0f0ae1ad..d0d109c09 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -556,27 +556,33 @@ static int verbose_mount(const char *source, const char *target,
 #endif
 
 // Append mount options to string
+// ("merge two comma-separated lists" is a good candidate for libbb!)
 static void append_mount_options(char **oldopts, const char *newopts)
 {
 	if (*oldopts && **oldopts) {
+//TODO: do this unconditionally?
+//this way, newopts of "opt1,opt2,opt1"
+//will be de-duped into "opt1,opt2" in _both_ cases
+//(whether or now old opts are empty)
+//the only modification needed is to not prepend extra comma
+//when old opts is "".
 		// Do not insert options which are already there
-		while (newopts[0]) {
+		while (*newopts) {
 			char *p;
 			int len;
 
+			//if (*newopts == ',') { newopts++; continue; }
 			len = strchrnul(newopts, ',') - newopts;
 			p = *oldopts;
 			while (1) {
-				if (!strncmp(p, newopts, len)
+				if (strncmp(p, newopts, len) == 0
 				 && (p[len] == ',' || p[len] == '\0'))
 					goto skip;
-				p = strchr(p,',');
+				p = strchr(p, ',');
 				if (!p) break;
 				p++;
 			}
-			p = xasprintf("%s,%.*s", *oldopts, len, newopts);
-			free(*oldopts);
-			*oldopts = p;
+			xasprintf_inplace(*oldopts, "%s,%.*s", *oldopts, len, newopts);
  skip:
 			newopts += len;
 			while (*newopts == ',') newopts++;
@@ -2065,12 +2071,11 @@ static int singlemount(struct mntent *mp, int ignore_busy)
 		if (!is_prefixed_with(filteropts, ",ip="+1)
 		 && !strstr(filteropts, ",ip=")
 		) {
-			char *dotted, *ip;
+			char *ip;
 			// Insert "ip=..." option into options
-			dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+			ip = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
 			if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
-			ip = xasprintf("ip=%s", dotted);
-			if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
+			xasprintf_inplace(ip, "ip=%s", ip);
 // Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
 // handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
 // Currently, glibc does not support that (has no NI_NUMERICSCOPE),


More information about the busybox-cvs mailing list