/* Licensed under BSD-MIT - see LICENSE file for details */ #include #include #include #include #include #include "talstr.h" #include #include #include #include #include #include "str.h" char *tal_strdup_(const tal_t *ctx, const char *p, const char *label) { /* We have to let through NULL for take(). */ return tal_dup_arr_label(ctx, char, p, p ? strlen(p) + 1: 1, 0, label); } char *tal_strndup_(const tal_t *ctx, const char *p, size_t n, const char *label) { size_t len; char *ret; /* We have to let through NULL for take(). */ if (likely(p)) len = strnlen(p, n); else len = n; ret = tal_dup_arr_label(ctx, char, p, len, 1, label); if (ret) ret[len] = '\0'; return ret; } char *tal_fmt_(const tal_t *ctx, const char *label, const char *fmt, ...) { va_list ap; char *ret; va_start(ap, fmt); ret = tal_vfmt_(ctx, fmt, ap, label); va_end(ap); return ret; } static bool do_vfmt(char **buf, size_t off, const char *fmt, va_list ap) { /* A decent guess to start. */ size_t max = strlen(fmt) * 2 + 1; bool ok; for (;;) { va_list ap2; int ret; if (!tal_resize(buf, off + max)) { ok = false; break; } va_copy(ap2, ap); ret = vsnprintf(*buf + off, max, fmt, ap2); va_end(ap2); if (ret < max) { ok = true; /* Make sure tal_count() is correct! */ tal_resize(buf, off + ret + 1); break; } max *= 2; } if (taken(fmt)) tal_free(fmt); return ok; } char *tal_vfmt_(const tal_t *ctx, const char *fmt, va_list ap, const char *label) { char *buf; if (!fmt && taken(fmt)) return NULL; /* A decent guess to start. */ buf = tal_arr_label(ctx, char, strlen(fmt) * 2, label); if (!do_vfmt(&buf, 0, fmt, ap)) buf = tal_free(buf); return buf; } bool tal_append_vfmt(char **baseptr, const char *fmt, va_list ap) { if (!fmt && taken(fmt)) return false; return do_vfmt(baseptr, strlen(*baseptr), fmt, ap); } bool tal_append_fmt(char **baseptr, const char *fmt, ...) { va_list ap; bool ret; va_start(ap, fmt); ret = tal_append_vfmt(baseptr, fmt, ap); va_end(ap); return ret; } char *tal_strcat_(const tal_t *ctx, const char *s1, const char *s2, const char *label) { size_t len1, len2; char *ret; if (unlikely(!s2) && taken(s2)) { if (taken(s1)) tal_free(s1); return NULL; } /* We have to let through NULL for take(). */ len1 = s1 ? strlen(s1) : 0; len2 = strlen(s2); ret = tal_dup_arr_label(ctx, char, s1, len1, len2 + 1, label); if (likely(ret)) memcpy(ret + len1, s2, len2 + 1); if (taken(s2)) tal_free(s2); return ret; } char **tal_strsplit_(const tal_t *ctx, const char *string, const char *delims, enum strsplit flags, const char *label) { char **parts, *str; size_t max = 64, num = 0; parts = tal_arr(ctx, char *, max + 1); if (unlikely(!parts)) { if (taken(string)) tal_free(string); if (taken(delims)) tal_free(delims); return NULL; } str = tal_strdup(parts, string); if (unlikely(!str)) goto fail; if (unlikely(!delims) && is_taken(delims)) goto fail; if (flags == STR_NO_EMPTY) str += strspn(str, delims); while (*str != '\0') { size_t len = strcspn(str, delims), dlen; parts[num] = str; dlen = strspn(str + len, delims); parts[num][len] = '\0'; if (flags == STR_EMPTY_OK && dlen) dlen = 1; str += len + dlen; if (++num == max && !tal_resize(&parts, max*=2 + 1)) goto fail; } parts[num] = NULL; /* Ensure that tal_count() is correct. */ if (unlikely(!tal_resize(&parts, num+1))) goto fail; if (taken(delims)) tal_free(delims); return parts; fail: tal_free(parts); if (taken(delims)) tal_free(delims); return NULL; } char *tal_strjoin_(const tal_t *ctx, char *strings[], const char *delim, enum strjoin flags, const char *label) { unsigned int i; char *ret = NULL; size_t totlen = 0, dlen; if (unlikely(!strings) && is_taken(strings)) goto fail; if (unlikely(!delim) && is_taken(delim)) goto fail; dlen = strlen(delim); ret = tal_arr_label(ctx, char, dlen*2+1, label); if (!ret) goto fail; ret[0] = '\0'; for (i = 0; strings[i]; i++) { size_t len = strlen(strings[i]); if (flags == STR_NO_TRAIL && !strings[i+1]) dlen = 0; if (!tal_resize(&ret, totlen + len + dlen + 1)) goto fail; memcpy(ret + totlen, strings[i], len); totlen += len; memcpy(ret + totlen, delim, dlen); totlen += dlen; } ret[totlen] = '\0'; /* Make sure tal_count() is correct! */ tal_resize(&ret, totlen+1); out: if (taken(strings)) tal_free(strings); if (taken(delim)) tal_free(delim); return ret; fail: ret = tal_free(ret); goto out; } static size_t count_open_braces(const char *string) { #if 1 size_t num = 0, esc = 0; while (*string) { if (*string == '\\') esc++; else { /* An odd number of \ means it's escaped. */ if (*string == '(' && (esc & 1) == 0) num++; esc = 0; } string++; } return num; #else return strcount(string, "("); #endif } bool tal_strreg_(const tal_t *ctx, const char *string, const char *label, const char *regex, ...) { size_t nmatch = 1 + count_open_braces(regex); regmatch_t matches[nmatch]; regex_t r; bool ret = false; unsigned int i; va_list ap; if (unlikely(!regex) && is_taken(regex)) goto fail_no_re; if (regcomp(&r, regex, REG_EXTENDED) != 0) goto fail_no_re; if (unlikely(!string) && is_taken(string)) goto fail; if (regexec(&r, string, nmatch, matches, 0) != 0) goto fail; ret = true; va_start(ap, regex); for (i = 1; i < nmatch; i++) { char **arg = va_arg(ap, char **); if (arg) { /* eg. ([a-z])? can give "no match". */ if (matches[i].rm_so == -1) *arg = NULL; else { *arg = tal_strndup_(ctx, string + matches[i].rm_so, matches[i].rm_eo - matches[i].rm_so, label); /* FIXME: If we fail, we set some and leak! */ if (!*arg) { ret = false; break; } } } } va_end(ap); fail: regfree(&r); fail_no_re: if (taken(regex)) tal_free(regex); if (taken(string)) tal_free(string); return ret; }