New action: move-cursor-line-start.
[elinks/kon.git] / src / util / string.c
blob076c491b886d9f5b2943aa5eb4880d899e5188db
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>
18 #include "elinks.h"
20 #include "util/conv.h"
21 #include "util/error.h"
22 #include "util/memdebug.h"
23 #include "util/memory.h"
24 #include "util/string.h"
25 #include "util/snprintf.h"
28 /* This file looks to be slowly being overloaded by a lot of various stuff,
29 * like memory managment, stubs, tools, granular and non-granular strings,
30 * struct string object... Perhaps util/memory.* and util/stubs.* (stubs.h
31 * probably included in elinks.h, it's important enough) would be nice to
32 * have. --pasky */
35 #define string_assert(f, l, x, o) \
36 if ((assert_failed = !(x))) { \
37 errfile = f, errline = l, \
38 elinks_internal("[%s] assertion %s failed!", o, #x); \
41 #ifdef DEBUG_MEMLEAK
43 unsigned char *
44 debug_memacpy(const unsigned char *f, int l, const unsigned char *src, int len)
46 unsigned char *m;
48 string_assert(f, l, len >= 0, "memacpy");
49 if_assert_failed len = 0;
51 m = debug_mem_alloc(f, l, len + 1);
52 if (!m) return NULL;
54 if (src && len) memcpy(m, src, len);
55 m[len] = '\0';
57 return m;
60 unsigned char *
61 debug_stracpy(const unsigned char *f, int l, const unsigned char *src)
63 string_assert(f, l, src, "stracpy");
64 if_assert_failed return NULL;
66 return debug_memacpy(f, l, src, strlen(src));
69 #else /* DEBUG_MEMLEAK */
71 unsigned char *
72 memacpy(const unsigned char *src, int len)
74 unsigned char *m;
76 assertm(len >= 0, "[memacpy]");
77 if_assert_failed { len = 0; }
79 m = mem_alloc(len + 1);
80 if (!m) return NULL;
82 if (src && len) memcpy(m, src, len);
83 m[len] = 0;
85 return m;
88 unsigned char *
89 stracpy(const unsigned char *src)
91 assertm(src, "[stracpy]");
92 if_assert_failed return NULL;
94 return memacpy(src, strlen(src));
97 #endif /* DEBUG_MEMLEAK */
100 void
101 add_to_strn(unsigned char **dst, const unsigned char *src)
103 unsigned char *newdst;
104 int dstlen;
105 int srclen;
107 assertm(*dst && src, "[add_to_strn]");
108 if_assert_failed return;
110 dstlen = strlen(*dst);
111 srclen = strlen(src) + 1; /* Include the NUL char! */
112 newdst = mem_realloc(*dst, dstlen + srclen);
113 if (!newdst) return;
115 memcpy(newdst + dstlen, src, srclen);
116 *dst = newdst;
119 unsigned char *
120 insert_in_string(unsigned char **dst, int pos,
121 const unsigned char *seq, int seqlen)
123 int dstlen = strlen(*dst);
124 unsigned char *string = mem_realloc(*dst, dstlen + seqlen + 1);
126 if (!string) return NULL;
128 memmove(string + pos + seqlen, string + pos, dstlen - pos + 1);
129 memcpy(string + pos, seq, seqlen);
130 *dst = string;
132 return string;
135 unsigned char *
136 straconcat(const unsigned char *str, ...)
138 va_list ap;
139 const unsigned char *a;
140 unsigned char *s;
141 unsigned int len;
143 assertm(str != NULL, "[straconcat]");
144 if_assert_failed { return NULL; }
146 len = strlen(str);
147 s = mem_alloc(len + 1);
148 if (!s) return NULL;
150 if (len) memcpy(s, str, len);
152 va_start(ap, str);
153 while ((a = va_arg(ap, const unsigned char *))) {
154 unsigned int l = strlen(a);
155 unsigned char *ns;
157 if (!l) continue;
159 ns = mem_realloc(s, len + 1 + l);
160 if (!ns) {
161 mem_free(s);
162 va_end(ap);
163 return NULL;
166 s = ns;
167 memcpy(s + len, a, l);
168 len += l;
170 va_end(ap);
172 s[len] = '\0';
173 return s;
177 xstrcmp(const unsigned char *s1, const unsigned char *s2)
179 if (!s1) return -!!s2;
180 if (!s2) return 1;
181 return strcmp(s1, s2);
184 unsigned char *
185 safe_strncpy(unsigned char *dst, const unsigned char *src, size_t dst_size)
187 assertm(dst && src && dst_size > 0, "[safe_strncpy]");
188 if_assert_failed return NULL;
190 strncpy(dst, src, dst_size);
191 dst[dst_size - 1] = '\0';
193 return dst;
196 #define strlcmp_device(c,s1,n1,s2,n2,t1,t2) { \
197 size_t p; \
198 int d; \
200 /* XXX: The return value is inconsistent. Hrmpf. Making it consistent
201 * would make the @n1 != @n2 case significantly more expensive, though.
202 * So noone should better rely on the return value actually meaning
203 * anything quantitatively. --pasky */ \
205 if (!s1 || !s2) \
206 return 1; \
208 /* n1,n2 is unsigned, so don't assume -1 < 0 ! >:) */ \
210 /* TODO: Don't precompute strlen()s but rather make the loop smarter.
211 * --pasky */ \
212 if (n1 == -1) n1 = strlen(s1); \
213 if (n2 == -1) n2 = strlen(s2); \
215 string_assert(errfile, errline, n1 >= 0 && n2 >= 0, c); \
217 d = n1 - n2; \
218 if (d) return d; \
220 for (p = 0; p < n1 && s1[p] && s2[p]; p++) { \
221 d = t1 - t2; \
222 if (d) return d; \
224 return 0; \
228 elinks_strlcmp(const unsigned char *s1, size_t n1,
229 const unsigned char *s2, size_t n2)
231 strlcmp_device("strlcmp", s1, n1, s2, n2, s1[p], s2[p]);
235 elinks_strlcasecmp(const unsigned char *s1, size_t n1,
236 const unsigned char *s2, size_t n2)
238 strlcmp_device("strlcasecmp", s1, n1, s2, n2, toupper(s1[p]), toupper(s2[p]));
242 /* The new string utilities: */
244 /* TODO Currently most of the functions use add_bytes_to_string() as a backend
245 * instead we should optimize each function. */
247 inline struct string *
248 #ifdef DEBUG_MEMLEAK
249 init_string__(const unsigned char *file, int line, struct string *string)
250 #else
251 init_string(struct string *string)
252 #endif
254 assertm(string != NULL, "[init_string]");
255 if_assert_failed { return NULL; }
257 string->length = 0;
258 #ifdef DEBUG_MEMLEAK
259 string->source = debug_mem_alloc(file, line, STRING_GRANULARITY + 1);
260 #else
261 string->source = mem_alloc(STRING_GRANULARITY + 1);
262 #endif
263 if (!string->source) return NULL;
265 *string->source = '\0';
267 set_string_magic(string);
269 return string;
272 inline void
273 done_string(struct string *string)
275 assertm(string != NULL, "[done_string]");
276 if_assert_failed { return; }
278 if (string->source) {
279 /* We only check the magic if we have to free anything so
280 * that done_string() can be called multiple times without
281 * blowing up something */
282 check_string_magic(string);
283 mem_free(string->source);
286 /* Blast everything including the magic */
287 memset(string, 0, sizeof(*string));
290 /** @relates string */
291 inline struct string *
292 add_to_string(struct string *string, const unsigned char *source)
294 assertm(string && source, "[add_to_string]");
295 if_assert_failed { return NULL; }
297 check_string_magic(string);
299 if (!*source) return string;
301 return add_bytes_to_string(string, source, strlen(source));
304 /** @relates string */
305 inline struct string *
306 add_crlf_to_string(struct string *string)
308 assertm(string != NULL, "[add_crlf_to_string]");
309 if_assert_failed { return NULL; }
311 check_string_magic(string);
313 if (!realloc_string(string, string->length + 2))
314 return NULL;
316 string->source[string->length++] = ASCII_CR;
317 string->source[string->length++] = ASCII_LF;
318 string->source[string->length] = '\0';
320 return string;
323 /** @relates string */
324 inline struct string *
325 add_string_to_string(struct string *string, const struct string *from)
327 assertm(string && from, "[add_string_to_string]");
328 if_assert_failed { return NULL; }
330 check_string_magic(string);
331 check_string_magic(from);
333 if (!from->length) return string; /* optimization only */
335 return add_bytes_to_string(string, from->source, from->length);
338 /** @relates string */
339 struct string *
340 add_file_to_string(struct string *string, const unsigned char *filename)
342 FILE *file;
343 off_t filelen;
344 int newlength;
346 assertm(string && filename, "[add_file_to_string]");
347 if_assert_failed { return NULL; }
349 check_string_magic(string);
351 file = fopen(filename, "rb");
352 if (!file) return NULL;
354 if (fseeko(file, 0, SEEK_END)) goto err;
356 filelen = ftello(file);
357 if (filelen == -1) goto err;
359 if (fseeko(file, 0, SEEK_SET)) goto err;
361 newlength = string->length + filelen;
362 if (!realloc_string(string, newlength)) goto err;
364 string->length += fread(string->source + string->length, 1,
365 (size_t) filelen, file);
366 string->source[string->length] = 0;
367 fclose(file);
369 if (string->length != newlength) goto err;
371 return string;
373 err:
374 fclose(file);
376 return NULL;
379 struct string *
380 string_concat(struct string *string, ...)
382 va_list ap;
383 const unsigned char *source;
385 assertm(string != NULL, "[string_concat]");
386 if_assert_failed { return NULL; }
388 check_string_magic(string);
390 va_start(ap, string);
391 while ((source = va_arg(ap, const unsigned char *)))
392 if (*source)
393 add_to_string(string, source);
395 va_end(ap);
397 return string;
400 /** @relates string */
401 inline struct string *
402 add_char_to_string(struct string *string, unsigned char character)
404 assertm(string && character, "[add_char_to_string]");
405 if_assert_failed { return NULL; }
407 check_string_magic(string);
409 if (!realloc_string(string, string->length + 1))
410 return NULL;
412 string->source[string->length++] = character;
413 string->source[string->length] = '\0';
415 return string;
418 inline struct string *
419 add_xchar_to_string(struct string *string, unsigned char character, int times)
421 int newlength;
423 assertm(string && character && times >= 0, "[add_xchar_to_string]");
424 if_assert_failed { return NULL; }
426 check_string_magic(string);
428 if (!times) return string;
430 newlength = string->length + times;
431 if (!realloc_string(string, newlength))
432 return NULL;
434 memset(string->source + string->length, character, times);
435 string->length = newlength;
436 string->source[newlength] = '\0';
438 return string;
441 /** Add printf()-style format string to @a string. */
442 struct string *
443 add_format_to_string(struct string *string, const unsigned char *format, ...)
445 int newlength;
446 int width;
447 va_list ap;
449 assertm(string && format, "[add_format_to_string]");
450 if_assert_failed { return NULL; }
452 check_string_magic(string);
454 va_start(ap, format);
455 width = vsnprintf(NULL, 0, format, ap);
456 va_end(ap);
457 if (width <= 0) return NULL;
459 newlength = string->length + width;
460 if (!realloc_string(string, newlength))
461 return NULL;
463 va_start(ap, format);
464 vsnprintf(&string->source[string->length], width + 1, format, ap);
465 va_end(ap);
467 string->length = newlength;
468 string->source[newlength] = '\0';
470 return string;
473 struct string *
474 add_to_string_list(LIST_OF(struct string_list_item) *list,
475 const unsigned char *source, int length)
477 struct string_list_item *item;
478 struct string *string;
480 assertm(list && source, "[add_to_string_list]");
481 if_assert_failed return NULL;
483 item = mem_alloc(sizeof(*item));
484 if (!item) return NULL;
486 string = &item->string;
487 if (length < 0) length = strlen(source);
489 if (!init_string(string)
490 || !add_bytes_to_string(string, source, length)) {
491 done_string(string);
492 mem_free(item);
493 return NULL;
496 add_to_list_end(*list, item);
497 return string;
500 /** @relates string_list_item */
501 void
502 free_string_list(LIST_OF(struct string_list_item) *list)
504 assertm(list != NULL, "[free_string_list]");
505 if_assert_failed return;
507 while (!list_empty(*list)) {
508 struct string_list_item *item = list->next;
510 del_from_list(item);
511 done_string(&item->string);
512 mem_free(item);