From fddd93edbdbf8c5afbfdb3c01d82e082a8a82d1a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 5 Feb 2026 13:36:27 +0100 Subject: [PATCH] libbb: introduce and use xasprintf_inplace() 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 --- 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),