iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / util / string.c
blobc8ee7d77a14b3a261fbd3c380ba9d29cb5daae3c
1 /** String handling functions
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #ifndef _GNU_SOURCE
9 #define _GNU_SOURCE /* XXX: fseeko, ftello */
10 #endif
12 #include <ctype.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <limits.h>
19 #include "elinks.h"
21 #include "util/conv.h"
22 #include "util/error.h"
23 #include "util/memdebug.h"
24 #include "util/memory.h"
25 #include "util/string.h"
26 #include "util/snprintf.h"
29 /* This file looks to be slowly being overloaded by a lot of various stuff,
30 * like memory managment, stubs, tools, granular and non-granular strings,
31 * struct string object... Perhaps util/memory.* and util/stubs.* (stubs.h
32 * probably included in elinks.h, it's important enough) would be nice to
33 * have. --pasky */
36 #define string_assert(f, l, x, o) \
37 if ((assert_failed = !(x))) { \
38 errfile = f, errline = l, \
39 elinks_internal("[%s] assertion %s failed!", o, #x); \
42 #ifdef DEBUG_MEMLEAK
44 unsigned char *
45 debug_memacpy(const unsigned char *f, int l, const unsigned char *src, int len)
47 unsigned char *m;
49 string_assert(f, l, len >= 0, "memacpy");
50 if_assert_failed len = 0;
52 m = debug_mem_alloc(f, l, len + 1);
53 if (!m) return NULL;
55 if (src && len) memcpy(m, src, len);
56 m[len] = '\0';
58 return m;
61 unsigned char *
62 debug_stracpy(const unsigned char *f, int l, const unsigned char *src)
64 string_assert(f, l, src, "stracpy");
65 if_assert_failed return NULL;
67 return debug_memacpy(f, l, src, strlen(src));
70 #else /* DEBUG_MEMLEAK */
72 unsigned char *
73 memacpy(const unsigned char *src, int len)
75 unsigned char *m;
77 assertm(len >= 0, "[memacpy]");
78 if_assert_failed { len = 0; }
80 m = mem_alloc(len + 1);
81 if (!m) return NULL;
83 if (src && len) memcpy(m, src, len);
84 m[len] = 0;
86 return m;
89 unsigned char *
90 stracpy(const unsigned char *src)
92 assertm(src, "[stracpy]");
93 if_assert_failed return NULL;
95 return memacpy(src, strlen(src));
98 #endif /* DEBUG_MEMLEAK */
101 void
102 add_to_strn(unsigned char **dst, const unsigned char *src)
104 unsigned char *newdst;
105 int dstlen;
106 int srclen;
108 assertm(*dst && src, "[add_to_strn]");
109 if_assert_failed return;
111 dstlen = strlen(*dst);
112 srclen = strlen(src) + 1; /* Include the NUL char! */
113 newdst = mem_realloc(*dst, dstlen + srclen);
114 if (!newdst) return;
116 memcpy(newdst + dstlen, src, srclen);
117 *dst = newdst;
120 unsigned char *
121 insert_in_string(unsigned char **dst, int pos,
122 const unsigned char *seq, int seqlen)
124 int dstlen = strlen(*dst);
125 unsigned char *string = mem_realloc(*dst, dstlen + seqlen + 1);
127 if (!string) return NULL;
129 memmove(string + pos + seqlen, string + pos, dstlen - pos + 1);
130 memcpy(string + pos, seq, seqlen);
131 *dst = string;
133 return string;
136 unsigned char *
137 straconcat(const unsigned char *str, ...)
139 va_list ap;
140 const unsigned char *a;
141 unsigned char *s;
142 unsigned int len;
144 assertm(str != NULL, "[straconcat]");
145 if_assert_failed { return NULL; }
147 len = strlen(str);
148 s = mem_alloc(len + 1);
149 if (!s) return NULL;
151 if (len) memcpy(s, str, len);
153 va_start(ap, str);
154 while ((a = va_arg(ap, const unsigned char *))) {
155 unsigned int l = strlen(a);
156 unsigned char *ns;
158 if (!l) continue;
160 ns = mem_realloc(s, len + 1 + l);
161 if (!ns) {
162 mem_free(s);
163 va_end(ap);
164 return NULL;
167 s = ns;
168 memcpy(s + len, a, l);
169 len += l;
171 va_end(ap);
173 s[len] = '\0';
174 return s;
178 xstrcmp(const unsigned char *s1, const unsigned char *s2)
180 if (!s1) return -!!s2;
181 if (!s2) return 1;
182 return strcmp(s1, s2);
185 unsigned char *
186 safe_strncpy(unsigned char *dst, const unsigned char *src, size_t dst_size)
188 assertm(dst && src && dst_size > 0, "[safe_strncpy]");
189 if_assert_failed return NULL;
191 strncpy(dst, src, dst_size);
192 dst[dst_size - 1] = '\0';
194 return dst;
197 #define strlcmp_device(c,s1,n1,s2,n2,t1,t2) { \
198 size_t p; \
199 int d; \
201 /* XXX: The return value is inconsistent. Hrmpf. Making it consistent
202 * would make the @n1 != @n2 case significantly more expensive, though.
203 * So noone should better rely on the return value actually meaning
204 * anything quantitatively. --pasky */ \
206 if (!s1 || !s2) \
207 return 1; \
209 /* n1,n2 is unsigned, so don't assume -1 < 0 ! >:) */ \
211 /* TODO: Don't precompute strlen()s but rather make the loop smarter.
212 * --pasky */ \
213 if (n1 == -1) n1 = strlen(s1); \
214 if (n2 == -1) n2 = strlen(s2); \
216 string_assert(errfile, errline, n1 >= 0 && n2 >= 0, c); \
218 d = n1 - n2; \
219 if (d) return d; \
221 for (p = 0; p < n1 && s1[p] && s2[p]; p++) { \
222 d = t1 - t2; \
223 if (d) return d; \
225 return 0; \
229 elinks_strlcmp(const unsigned char *s1, size_t n1,
230 const unsigned char *s2, size_t n2)
232 strlcmp_device("strlcmp", s1, n1, s2, n2, s1[p], s2[p]);
236 elinks_strlcasecmp(const unsigned char *s1, size_t n1,
237 const unsigned char *s2, size_t n2,
238 const int locale_indep)
240 if (locale_indep) {
241 strlcmp_device("strlcasecmp", s1, n1, s2, n2, c_toupper(s1[p]), c_toupper(s2[p]));
243 else {
244 strlcmp_device("strlcasecmp", s1, n1, s2, n2, toupper(s1[p]), toupper(s2[p]));
248 /* strlcasestr - adapted from c_strcasestr */
249 char *
250 elinks_strlcasestr(const char *haystack, const int haystackl,
251 const char *needle, const int needlel)
253 size_t haystack_length = haystackl == -1 ? strlen(haystack) : haystackl;
254 size_t needle_length = needlel == -1 ? strlen(needle) : needlel;
255 int i;
257 if (haystack_length < needle_length)
258 return NULL;
260 for (i = haystack_length - needle_length + 1; i; i--) {
261 if (!c_strncasecmp(haystack, needle, needle_length))
262 return (char *) haystack;
263 haystack++;
266 return NULL;
270 c_strcasecmp(const char *s1, const char *s2)
272 for (;; s1++, s2++) {
273 unsigned char c1 = c_tolower(*(const unsigned char *) s1);
274 unsigned char c2 = c_tolower(*(const unsigned char *) s2);
276 if (c1 != c2)
277 return (c1 < c2) ? -1: +1;
278 if (c1 == '\0')
279 return 0;
283 int c_strncasecmp(const char *s1, const char *s2, size_t n)
285 for (; n > 0; n--, s1++, s2++) {
286 unsigned char c1 = c_tolower(*(const unsigned char *) s1);
287 unsigned char c2 = c_tolower(*(const unsigned char *) s2);
289 if (c1 != c2)
290 return (c1 < c2) ? -1: +1;
291 if (c1 == '\0')
292 return 0;
294 return 0;
297 /* c_strcasestr - adapted from src/osdep/stub.c */
298 char * c_strcasestr(const char *haystack, const char *needle)
300 size_t haystack_length = strlen(haystack);
301 size_t needle_length = strlen(needle);
302 int i;
304 if (haystack_length < needle_length)
305 return NULL;
307 for (i = haystack_length - needle_length + 1; i; i--) {
308 if (!c_strncasecmp(haystack, needle, needle_length))
309 return (char *) haystack;
310 haystack++;
313 return NULL;
316 /* The new string utilities: */
318 /* TODO Currently most of the functions use add_bytes_to_string() as a backend
319 * instead we should optimize each function. */
321 NONSTATIC_INLINE struct string *
322 #ifdef DEBUG_MEMLEAK
323 init_string__(const unsigned char *file, int line, struct string *string)
324 #else
325 init_string(struct string *string)
326 #endif
328 assertm(string != NULL, "[init_string]");
329 if_assert_failed { return NULL; }
331 string->length = 0;
332 #ifdef DEBUG_MEMLEAK
333 string->source = debug_mem_alloc(file, line, STRING_GRANULARITY + 1);
334 #else
335 string->source = mem_alloc(STRING_GRANULARITY + 1);
336 #endif
337 if (!string->source) return NULL;
339 *string->source = '\0';
341 set_string_magic(string);
343 return string;
346 NONSTATIC_INLINE void
347 done_string(struct string *string)
349 assertm(string != NULL, "[done_string]");
350 if_assert_failed { return; }
352 if (string->source) {
353 /* We only check the magic if we have to free anything so
354 * that done_string() can be called multiple times without
355 * blowing up something */
356 check_string_magic(string);
357 mem_free(string->source);
360 /* Blast everything including the magic */
361 memset(string, 0, sizeof(*string));
364 /** @relates string */
365 NONSTATIC_INLINE struct string *
366 add_to_string(struct string *string, const unsigned char *source)
368 assertm(string && source, "[add_to_string]");
369 if_assert_failed { return NULL; }
371 check_string_magic(string);
373 if (!*source) return string;
375 return add_bytes_to_string(string, source, strlen(source));
378 /** @relates string */
379 NONSTATIC_INLINE struct string *
380 add_crlf_to_string(struct string *string)
382 assertm(string != NULL, "[add_crlf_to_string]");
383 if_assert_failed { return NULL; }
385 check_string_magic(string);
387 if (!realloc_string(string, string->length + 2))
388 return NULL;
390 string->source[string->length++] = ASCII_CR;
391 string->source[string->length++] = ASCII_LF;
392 string->source[string->length] = '\0';
394 return string;
397 /** @relates string */
398 NONSTATIC_INLINE struct string *
399 add_string_to_string(struct string *string, const struct string *from)
401 assertm(string && from, "[add_string_to_string]");
402 if_assert_failed { return NULL; }
404 check_string_magic(string);
405 check_string_magic(from);
407 if (!from->length) return string; /* optimization only */
409 return add_bytes_to_string(string, from->source, from->length);
412 /** @relates string */
413 struct string *
414 add_file_to_string(struct string *string, const unsigned char *filename)
416 FILE *file;
417 off_t filelen;
418 int newlength;
420 assertm(string && filename, "[add_file_to_string]");
421 if_assert_failed { return NULL; }
423 check_string_magic(string);
425 file = fopen(filename, "rb");
426 if (!file) return NULL;
428 if (fseeko(file, 0, SEEK_END)) goto err;
430 filelen = ftello(file);
431 if (filelen == -1) goto err;
433 if (fseeko(file, 0, SEEK_SET)) goto err;
435 newlength = string->length + filelen;
436 if (!realloc_string(string, newlength)) goto err;
438 string->length += fread(string->source + string->length, 1,
439 (size_t) filelen, file);
440 string->source[string->length] = 0;
441 fclose(file);
443 if (string->length != newlength) goto err;
445 return string;
447 err:
448 fclose(file);
450 return NULL;
453 struct string *
454 string_concat(struct string *string, ...)
456 va_list ap;
457 const unsigned char *source;
459 assertm(string != NULL, "[string_concat]");
460 if_assert_failed { return NULL; }
462 check_string_magic(string);
464 va_start(ap, string);
465 while ((source = va_arg(ap, const unsigned char *)))
466 if (*source)
467 add_to_string(string, source);
469 va_end(ap);
471 return string;
474 /** @relates string */
475 NONSTATIC_INLINE struct string *
476 add_char_to_string(struct string *string, unsigned char character)
478 assertm(string && character, "[add_char_to_string]");
479 if_assert_failed { return NULL; }
481 check_string_magic(string);
483 if (!realloc_string(string, string->length + 1))
484 return NULL;
486 string->source[string->length++] = character;
487 string->source[string->length] = '\0';
489 return string;
492 NONSTATIC_INLINE struct string *
493 add_xchar_to_string(struct string *string, unsigned char character, int times)
495 int newlength;
497 assertm(string && character && times >= 0, "[add_xchar_to_string]");
498 if_assert_failed { return NULL; }
500 check_string_magic(string);
502 if (!times) return string;
504 newlength = string->length + times;
505 if (!realloc_string(string, newlength))
506 return NULL;
508 memset(string->source + string->length, character, times);
509 string->length = newlength;
510 string->source[newlength] = '\0';
512 return string;
515 /** Add printf()-style format string to @a string. */
516 struct string *
517 add_format_to_string(struct string *string, const unsigned char *format, ...)
519 int newlength;
520 int width;
521 va_list ap;
523 assertm(string && format, "[add_format_to_string]");
524 if_assert_failed { return NULL; }
526 check_string_magic(string);
528 va_start(ap, format);
529 width = vsnprintf(NULL, 0, format, ap);
530 va_end(ap);
531 if (width <= 0) return NULL;
533 newlength = string->length + width;
534 if (!realloc_string(string, newlength))
535 return NULL;
537 va_start(ap, format);
538 vsnprintf(&string->source[string->length], width + 1, format, ap);
539 va_end(ap);
541 string->length = newlength;
542 string->source[newlength] = '\0';
544 return string;
547 struct string *
548 add_to_string_list(LIST_OF(struct string_list_item) *list,
549 const unsigned char *source, int length)
551 struct string_list_item *item;
552 struct string *string;
554 assertm(list && source, "[add_to_string_list]");
555 if_assert_failed return NULL;
557 item = mem_alloc(sizeof(*item));
558 if (!item) return NULL;
560 string = &item->string;
561 if (length < 0) length = strlen(source);
563 if (!init_string(string)
564 || !add_bytes_to_string(string, source, length)) {
565 done_string(string);
566 mem_free(item);
567 return NULL;
570 add_to_list_end(*list, item);
571 return string;
574 /** @relates string_list_item */
575 void
576 free_string_list(LIST_OF(struct string_list_item) *list)
578 assertm(list != NULL, "[free_string_list]");
579 if_assert_failed return;
581 while (!list_empty(*list)) {
582 struct string_list_item *item = list->next;
584 del_from_list(item);
585 done_string(&item->string);
586 mem_free(item);