grafthistory: support curl
[elinks/elinks-j605.git] / src / util / string.c
blob69ed765dd7bb95139b378061a0f5cbb80aef32fb
1 /* String handling functions */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #ifndef _GNU_SOURCE
8 #define _GNU_SOURCE /* XXX: fseeko, ftello */
9 #endif
11 #include <ctype.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
17 #include "elinks.h"
19 #include "util/conv.h"
20 #include "util/error.h"
21 #include "util/memdebug.h"
22 #include "util/memory.h"
23 #include "util/string.h"
24 #include "util/snprintf.h"
27 /* This file looks to be slowly being overloaded by a lot of various stuff,
28 * like memory managment, stubs, tools, granular and non-granular strings,
29 * struct string object... Perhaps util/memory.* and util/stubs.* (stubs.h
30 * probably included in elinks.h, it's important enough) would be nice to
31 * have. --pasky */
34 #define string_assert(f, l, x, o) \
35 if ((assert_failed = !(x))) { \
36 errfile = f, errline = l, \
37 elinks_internal("[%s] assertion %s failed!", o, #x); \
40 #ifdef DEBUG_MEMLEAK
42 unsigned char *
43 debug_memacpy(unsigned char *f, int l, unsigned char *src, int len)
45 unsigned char *m;
47 string_assert(f, l, len >= 0, "memacpy");
48 if_assert_failed len = 0;
50 m = debug_mem_alloc(f, l, len + 1);
51 if (!m) return NULL;
53 if (src && len) memcpy(m, src, len);
54 m[len] = '\0';
56 return m;
59 unsigned char *
60 debug_stracpy(unsigned char *f, int l, unsigned char *src)
62 string_assert(f, l, src, "stracpy");
63 if_assert_failed return NULL;
65 return debug_memacpy(f, l, src, strlen(src));
68 #else /* DEBUG_MEMLEAK */
70 unsigned char *
71 memacpy(unsigned char *src, int len)
73 unsigned char *m;
75 assertm(len >= 0, "[memacpy]");
76 if_assert_failed { len = 0; }
78 m = mem_alloc(len + 1);
79 if (!m) return NULL;
81 if (src && len) memcpy(m, src, len);
82 m[len] = 0;
84 return m;
87 unsigned char *
88 stracpy(unsigned char *src)
90 assertm(src, "[stracpy]");
91 if_assert_failed return NULL;
93 return memacpy(src, strlen(src));
96 #endif /* DEBUG_MEMLEAK */
99 void
100 add_to_strn(unsigned char **dst, unsigned char *src)
102 unsigned char *newdst;
103 int dstlen;
104 int srclen;
106 assertm(*dst && src, "[add_to_strn]");
107 if_assert_failed return;
109 dstlen = strlen(*dst);
110 srclen = strlen(src) + 1; /* Include the NUL char! */
111 newdst = mem_realloc(*dst, dstlen + srclen);
112 if (!newdst) return;
114 memcpy(newdst + dstlen, src, srclen);
115 *dst = newdst;
118 unsigned char *
119 insert_in_string(unsigned char **dst, int pos, unsigned char *seq, int seqlen)
121 int dstlen = strlen(*dst);
122 unsigned char *string = mem_realloc(*dst, dstlen + seqlen + 1);
124 if (!string) return NULL;
126 memmove(string + pos + seqlen, string + pos, dstlen - pos + 1);
127 memcpy(string + pos, seq, seqlen);
128 *dst = string;
130 return string;
133 unsigned char *
134 straconcat(unsigned char *str, ...)
136 va_list ap;
137 unsigned char *a;
138 unsigned char *s;
139 unsigned int len;
141 assertm(str, "[straconcat]");
142 if_assert_failed { return NULL; }
144 len = strlen(str);
145 s = mem_alloc(len + 1);
146 if (!s) return NULL;
148 if (len) memcpy(s, str, len);
150 va_start(ap, str);
151 while ((a = va_arg(ap, unsigned char *))) {
152 unsigned int l = strlen(a);
153 unsigned char *ns;
155 if (!l) continue;
157 ns = mem_realloc(s, len + 1 + l);
158 if (!ns) {
159 mem_free(s);
160 va_end(ap);
161 return NULL;
164 s = ns;
165 memcpy(s + len, a, l);
166 len += l;
168 va_end(ap);
170 s[len] = '\0';
171 return s;
175 xstrcmp(unsigned char *s1, unsigned char *s2)
177 if (!s1) return -!!s2;
178 if (!s2) return 1;
179 return strcmp(s1, s2);
182 unsigned char *
183 safe_strncpy(unsigned char *dst, const unsigned char *src, size_t dst_size)
185 assertm(dst && src && dst_size > 0, "[safe_strncpy]");
186 if_assert_failed return NULL;
188 strncpy(dst, src, dst_size);
189 dst[dst_size - 1] = '\0';
191 return dst;
194 #define strlcmp_device(c,s1,n1,s2,n2,t1,t2) { \
195 size_t p; \
196 int d; \
198 /* XXX: The return value is inconsistent. Hrmpf. Making it consistent
199 * would make the @n1 != @n2 case significantly more expensive, though.
200 * So noone should better rely on the return value actually meaning
201 * anything quantitatively. --pasky */ \
203 if (!s1 || !s2) \
204 return 1; \
206 /* n1,n2 is unsigned, so don't assume -1 < 0 ! >:) */ \
208 /* TODO: Don't precompute strlen()s but rather make the loop smarter.
209 * --pasky */ \
210 if (n1 == -1) n1 = strlen(s1); \
211 if (n2 == -1) n2 = strlen(s2); \
213 string_assert(errfile, errline, n1 >= 0 && n2 >= 0, c); \
215 d = n1 - n2; \
216 if (d) return d; \
218 for (p = 0; p < n1 && s1[p] && s2[p]; p++) { \
219 d = t1 - t2; \
220 if (d) return d; \
222 return 0; \
226 elinks_strlcmp(const unsigned char *s1, size_t n1,
227 const unsigned char *s2, size_t n2)
229 strlcmp_device("strlcmp", s1, n1, s2, n2, s1[p], s2[p]);
233 elinks_strlcasecmp(const unsigned char *s1, size_t n1,
234 const unsigned char *s2, size_t n2)
236 strlcmp_device("strlcasecmp", s1, n1, s2, n2, toupper(s1[p]), toupper(s2[p]));
240 /* The new string utilities: */
242 /* TODO Currently most of the functions use add_bytes_to_string() as a backend
243 * instead we should optimize each function. */
245 inline struct string *
246 #ifdef DEBUG_MEMLEAK
247 init_string__(unsigned char *file, int line, struct string *string)
248 #else
249 init_string(struct string *string)
250 #endif
252 assertm(string, "[init_string]");
253 if_assert_failed { return NULL; }
255 string->length = 0;
256 #ifdef DEBUG_MEMLEAK
257 string->source = debug_mem_alloc(file, line, STRING_GRANULARITY + 1);
258 #else
259 string->source = mem_alloc(STRING_GRANULARITY + 1);
260 #endif
261 if (!string->source) return NULL;
263 *string->source = '\0';
265 set_string_magic(string);
267 return string;
270 inline void
271 done_string(struct string *string)
273 assertm(string, "[done_string]");
274 if_assert_failed { return; }
276 if (string->source) {
277 /* We only check the magic if we have to free anything so
278 * that done_string() can be called multiple times without
279 * blowing up something */
280 check_string_magic(string);
281 mem_free(string->source);
284 /* Blast everything including the magic */
285 memset(string, 0, sizeof(*string));
288 inline struct string *
289 add_to_string(struct string *string, const unsigned char *source)
291 assertm(string && source, "[add_to_string]");
292 if_assert_failed { return NULL; }
294 check_string_magic(string);
296 if (!*source) return string;
298 return add_bytes_to_string(string, source, strlen(source));
301 inline struct string *
302 add_crlf_to_string(struct string *string)
304 assertm(string, "[add_crlf_to_string]");
305 if_assert_failed { return NULL; }
307 check_string_magic(string);
309 if (!realloc_string(string, string->length + 2))
310 return NULL;
312 string->source[string->length++] = ASCII_CR;
313 string->source[string->length++] = ASCII_LF;
314 string->source[string->length] = '\0';
316 return string;
319 inline struct string *
320 add_string_to_string(struct string *string, struct string *from)
322 assertm(string && from, "[add_string_to_string]");
323 if_assert_failed { return NULL; }
325 check_string_magic(string);
326 check_string_magic(from);
328 if (!from->length) return string; /* optimization only */
330 return add_bytes_to_string(string, from->source, from->length);
333 struct string *
334 add_file_to_string(struct string *string, unsigned char *filename)
336 FILE *file;
337 off_t filelen;
338 int newlength;
340 assertm(string && filename, "[add_file_to_string]");
341 if_assert_failed { return NULL; }
343 check_string_magic(string);
345 file = fopen(filename, "rb");
346 if (!file) return NULL;
348 if (fseeko(file, 0, SEEK_END)) goto err;
350 filelen = ftello(file);
351 if (filelen == -1) goto err;
353 if (fseeko(file, 0, SEEK_SET)) goto err;
355 newlength = string->length + filelen;
356 if (!realloc_string(string, newlength)) goto err;
358 string->length += fread(string->source + string->length, 1,
359 (size_t) filelen, file);
360 string->source[string->length] = 0;
361 fclose(file);
363 if (string->length != newlength) goto err;
365 return string;
367 err:
368 fclose(file);
370 return NULL;
373 struct string *
374 string_concat(struct string *string, ...)
376 va_list ap;
377 unsigned char *source;
379 assertm(string, "[string_concat]");
380 if_assert_failed { return NULL; }
382 check_string_magic(string);
384 va_start(ap, string);
385 while ((source = va_arg(ap, unsigned char *)))
386 if (*source)
387 add_to_string(string, source);
389 va_end(ap);
391 return string;
394 inline struct string *
395 add_char_to_string(struct string *string, unsigned char character)
397 assertm(string && character, "[add_char_to_string]");
398 if_assert_failed { return NULL; }
400 check_string_magic(string);
402 if (!realloc_string(string, string->length + 1))
403 return NULL;
405 string->source[string->length++] = character;
406 string->source[string->length] = '\0';
408 return string;
411 inline struct string *
412 add_xchar_to_string(struct string *string, unsigned char character, int times)
414 int newlength;
416 assertm(string && character && times >= 0, "[add_xchar_to_string]");
417 if_assert_failed { return NULL; }
419 check_string_magic(string);
421 if (!times) return string;
423 newlength = string->length + times;
424 if (!realloc_string(string, newlength))
425 return NULL;
427 memset(string->source + string->length, character, times);
428 string->length = newlength;
429 string->source[newlength] = '\0';
431 return string;
434 /* Add printf-like format string to @string. */
435 struct string *
436 add_format_to_string(struct string *string, unsigned char *format, ...)
438 int newlength;
439 int width;
440 va_list ap;
441 va_list ap2;
443 assertm(string && format, "[add_format_to_string]");
444 if_assert_failed { return NULL; }
446 check_string_magic(string);
448 va_start(ap, format);
449 VA_COPY(ap2, ap);
451 width = vsnprintf(NULL, 0, format, ap2);
452 if (width <= 0) return NULL;
454 newlength = string->length + width;
455 if (!realloc_string(string, newlength))
456 return NULL;
458 vsnprintf(&string->source[string->length], width + 1, format, ap);
460 va_end(ap);
462 string->length = newlength;
463 string->source[newlength] = '\0';
465 return string;
468 struct string *
469 add_to_string_list(struct list_head *list, const unsigned char *source,
470 int length)
472 struct string_list_item *item;
473 struct string *string;
475 assertm(list && source, "[add_to_string_list]");
476 if_assert_failed return NULL;
478 item = mem_alloc(sizeof(*item));
479 if (!item) return NULL;
481 string = &item->string;
482 if (length < 0) length = strlen(source);
484 if (!init_string(string)
485 || !add_bytes_to_string(string, source, length)) {
486 done_string(string);
487 mem_free(item);
488 return NULL;
491 add_to_list_end(*list, item);
492 return string;
495 void
496 free_string_list(struct list_head *list)
498 assertm(list, "[free_string_list]");
499 if_assert_failed return;
501 while (!list_empty(*list)) {
502 struct string_list_item *item = list->next;
504 del_from_list(item);
505 done_string(&item->string);
506 mem_free(item);