libbb: introduce and use xasprintf_inplace()
No binary (build failed)
gcc -Wp,-MD,applets/.usage.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iinclude -Iinclude -o applets/usage applets/usage.c
(log yok)
| @@ -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. */ | |
| @@ -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) | |
| @@ -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)) | |
| @@ -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 | |
| @@ -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; | |
| @@ -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 { | |
| @@ -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 | |
| @@ -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; | |
| } | |
| @@ -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), | |