talstr.c (7232B)
1 /* Licensed under BSD-MIT - see LICENSE file for details */ 2 #include <unistd.h> 3 #include <stdint.h> 4 #include <string.h> 5 #include <limits.h> 6 #include <stdlib.h> 7 #include "talstr.h" 8 #include <sys/types.h> 9 #include <regex.h> 10 #include <stdarg.h> 11 #include <unistd.h> 12 #include <stdio.h> 13 #include "str.h" 14 15 char *tal_strdup_(const tal_t *ctx, const char *p, const char *label) 16 { 17 /* We have to let through NULL for take(). */ 18 return tal_dup_arr_label(ctx, char, p, p ? strlen(p) + 1: 1, 0, label); 19 } 20 21 char *tal_strndup_(const tal_t *ctx, const char *p, size_t n, const char *label) 22 { 23 size_t len; 24 char *ret; 25 26 /* We have to let through NULL for take(). */ 27 if (likely(p)) 28 len = strnlen(p, n); 29 else 30 len = n; 31 32 ret = tal_dup_arr_label(ctx, char, p, len, 1, label); 33 if (ret) 34 ret[len] = '\0'; 35 return ret; 36 } 37 38 char *tal_fmt_(const tal_t *ctx, const char *label, const char *fmt, ...) 39 { 40 va_list ap; 41 char *ret; 42 43 va_start(ap, fmt); 44 ret = tal_vfmt_(ctx, fmt, ap, label); 45 va_end(ap); 46 47 return ret; 48 } 49 50 static bool do_vfmt(char **buf, size_t off, const char *fmt, va_list ap) 51 { 52 /* A decent guess to start. */ 53 size_t max = strlen(fmt) * 2 + 1; 54 bool ok; 55 56 for (;;) { 57 va_list ap2; 58 int ret; 59 60 if (!tal_resize(buf, off + max)) { 61 ok = false; 62 break; 63 } 64 65 va_copy(ap2, ap); 66 ret = vsnprintf(*buf + off, max, fmt, ap2); 67 va_end(ap2); 68 69 if (ret < max) { 70 ok = true; 71 /* Make sure tal_count() is correct! */ 72 tal_resize(buf, off + ret + 1); 73 break; 74 } 75 max *= 2; 76 } 77 78 if (taken(fmt)) 79 tal_free(fmt); 80 return ok; 81 } 82 83 char *tal_vfmt_(const tal_t *ctx, const char *fmt, va_list ap, const char *label) 84 { 85 char *buf; 86 87 if (!fmt && taken(fmt)) 88 return NULL; 89 90 /* A decent guess to start. */ 91 buf = tal_arr_label(ctx, char, strlen(fmt) * 2, label); 92 if (!do_vfmt(&buf, 0, fmt, ap)) 93 buf = tal_free(buf); 94 return buf; 95 } 96 97 bool tal_append_vfmt(char **baseptr, const char *fmt, va_list ap) 98 { 99 if (!fmt && taken(fmt)) 100 return false; 101 102 return do_vfmt(baseptr, strlen(*baseptr), fmt, ap); 103 } 104 105 bool tal_append_fmt(char **baseptr, const char *fmt, ...) 106 { 107 va_list ap; 108 bool ret; 109 110 va_start(ap, fmt); 111 ret = tal_append_vfmt(baseptr, fmt, ap); 112 va_end(ap); 113 114 return ret; 115 } 116 117 char *tal_strcat_(const tal_t *ctx, const char *s1, const char *s2, 118 const char *label) 119 { 120 size_t len1, len2; 121 char *ret; 122 123 if (unlikely(!s2) && taken(s2)) { 124 if (taken(s1)) 125 tal_free(s1); 126 return NULL; 127 } 128 /* We have to let through NULL for take(). */ 129 len1 = s1 ? strlen(s1) : 0; 130 len2 = strlen(s2); 131 132 ret = tal_dup_arr_label(ctx, char, s1, len1, len2 + 1, label); 133 if (likely(ret)) 134 memcpy(ret + len1, s2, len2 + 1); 135 136 if (taken(s2)) 137 tal_free(s2); 138 return ret; 139 } 140 141 char **tal_strsplit_(const tal_t *ctx, 142 const char *string, const char *delims, enum strsplit flags, 143 const char *label) 144 { 145 char **parts, *str; 146 size_t max = 64, num = 0; 147 148 parts = tal_arr(ctx, char *, max + 1); 149 if (unlikely(!parts)) { 150 if (taken(string)) 151 tal_free(string); 152 if (taken(delims)) 153 tal_free(delims); 154 return NULL; 155 } 156 str = tal_strdup(parts, string); 157 if (unlikely(!str)) 158 goto fail; 159 if (unlikely(!delims) && is_taken(delims)) 160 goto fail; 161 162 if (flags == STR_NO_EMPTY) 163 str += strspn(str, delims); 164 165 while (*str != '\0') { 166 size_t len = strcspn(str, delims), dlen; 167 168 parts[num] = str; 169 dlen = strspn(str + len, delims); 170 parts[num][len] = '\0'; 171 if (flags == STR_EMPTY_OK && dlen) 172 dlen = 1; 173 str += len + dlen; 174 if (++num == max && !tal_resize(&parts, max*=2 + 1)) 175 goto fail; 176 } 177 parts[num] = NULL; 178 179 /* Ensure that tal_count() is correct. */ 180 if (unlikely(!tal_resize(&parts, num+1))) 181 goto fail; 182 183 if (taken(delims)) 184 tal_free(delims); 185 return parts; 186 187 fail: 188 tal_free(parts); 189 if (taken(delims)) 190 tal_free(delims); 191 return NULL; 192 } 193 194 char *tal_strjoin_(const tal_t *ctx, 195 char *strings[], const char *delim, enum strjoin flags, 196 const char *label) 197 { 198 unsigned int i; 199 char *ret = NULL; 200 size_t totlen = 0, dlen; 201 202 if (unlikely(!strings) && is_taken(strings)) 203 goto fail; 204 205 if (unlikely(!delim) && is_taken(delim)) 206 goto fail; 207 208 dlen = strlen(delim); 209 ret = tal_arr_label(ctx, char, dlen*2+1, label); 210 if (!ret) 211 goto fail; 212 213 ret[0] = '\0'; 214 for (i = 0; strings[i]; i++) { 215 size_t len = strlen(strings[i]); 216 217 if (flags == STR_NO_TRAIL && !strings[i+1]) 218 dlen = 0; 219 if (!tal_resize(&ret, totlen + len + dlen + 1)) 220 goto fail; 221 memcpy(ret + totlen, strings[i], len); 222 totlen += len; 223 memcpy(ret + totlen, delim, dlen); 224 totlen += dlen; 225 } 226 ret[totlen] = '\0'; 227 /* Make sure tal_count() is correct! */ 228 tal_resize(&ret, totlen+1); 229 out: 230 if (taken(strings)) 231 tal_free(strings); 232 if (taken(delim)) 233 tal_free(delim); 234 return ret; 235 fail: 236 ret = tal_free(ret); 237 goto out; 238 } 239 240 static size_t count_open_braces(const char *string) 241 { 242 #if 1 243 size_t num = 0, esc = 0; 244 245 while (*string) { 246 if (*string == '\\') 247 esc++; 248 else { 249 /* An odd number of \ means it's escaped. */ 250 if (*string == '(' && (esc & 1) == 0) 251 num++; 252 esc = 0; 253 } 254 string++; 255 } 256 return num; 257 #else 258 return strcount(string, "("); 259 #endif 260 } 261 262 bool tal_strreg_(const tal_t *ctx, const char *string, const char *label, 263 const char *regex, ...) 264 { 265 size_t nmatch = 1 + count_open_braces(regex); 266 regmatch_t matches[nmatch]; 267 regex_t r; 268 bool ret = false; 269 unsigned int i; 270 va_list ap; 271 272 if (unlikely(!regex) && is_taken(regex)) 273 goto fail_no_re; 274 275 if (regcomp(&r, regex, REG_EXTENDED) != 0) 276 goto fail_no_re; 277 278 if (unlikely(!string) && is_taken(string)) 279 goto fail; 280 281 if (regexec(&r, string, nmatch, matches, 0) != 0) 282 goto fail; 283 284 ret = true; 285 va_start(ap, regex); 286 for (i = 1; i < nmatch; i++) { 287 char **arg = va_arg(ap, char **); 288 if (arg) { 289 /* eg. ([a-z])? can give "no match". */ 290 if (matches[i].rm_so == -1) 291 *arg = NULL; 292 else { 293 *arg = tal_strndup_(ctx, 294 string + matches[i].rm_so, 295 matches[i].rm_eo 296 - matches[i].rm_so, 297 label); 298 /* FIXME: If we fail, we set some and leak! */ 299 if (!*arg) { 300 ret = false; 301 break; 302 } 303 } 304 } 305 } 306 va_end(ap); 307 fail: 308 regfree(&r); 309 fail_no_re: 310 if (taken(regex)) 311 tal_free(regex); 312 if (taken(string)) 313 tal_free(string); 314 return ret; 315 }