1 /* String handling functions */
2
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6
7 #ifndef _GNU_SOURCE
8 #define _GNU_SOURCE /* XXX: fseeko, ftello */
9 #endif
10
11 #include <ctype.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <limits.h>
17
18 #include "elinks.h"
19
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"
26
27
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 */
33
34
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); \
39 }
40
41 #ifdef DEBUG_MEMLEAK
42
43 unsigned char *
debug_memacpy(const unsigned char * f,int l,const unsigned char * src,int len)44 debug_memacpy(const unsigned char *f, int l, const unsigned char *src, int len)
45 {
46 unsigned char *m;
47
48 string_assert(f, l, len >= 0, "memacpy");
49 if_assert_failed len = 0;
50
51 m = debug_mem_alloc(f, l, len + 1);
52 if (!m) return NULL;
53
54 if (src && len) memcpy(m, src, len);
55 m[len] = '\0';
56
57 return m;
58 }
59
60 unsigned char *
debug_stracpy(const unsigned char * f,int l,const unsigned char * src)61 debug_stracpy(const unsigned char *f, int l, const unsigned char *src)
62 {
63 string_assert(f, l, src, "stracpy");
64 if_assert_failed return NULL;
65
66 return debug_memacpy(f, l, src, strlen(src));
67 }
68
69 #else /* DEBUG_MEMLEAK */
70
71 unsigned char *
memacpy(const unsigned char * src,int len)72 memacpy(const unsigned char *src, int len)
73 {
74 unsigned char *m;
75
76 assertm(len >= 0, "[memacpy]");
77 if_assert_failed { len = 0; }
78
79 m = mem_alloc(len + 1);
80 if (!m) return NULL;
81
82 if (src && len) memcpy(m, src, len);
83 m[len] = 0;
84
85 return m;
86 }
87
88 unsigned char *
stracpy(const unsigned char * src)89 stracpy(const unsigned char *src)
90 {
91 assertm(src, "[stracpy]");
92 if_assert_failed return NULL;
93
94 return memacpy(src, strlen(src));
95 }
96
97 #endif /* DEBUG_MEMLEAK */
98
99
100 void
add_to_strn(unsigned char ** dst,unsigned char * src)101 add_to_strn(unsigned char **dst, unsigned char *src)
102 {
103 unsigned char *newdst;
104 int dstlen;
105 int srclen;
106
107 assertm(*dst && src, "[add_to_strn]");
108 if_assert_failed return;
109
110 dstlen = strlen(*dst);
111 srclen = strlen(src) + 1; /* Include the NUL char! */
112 newdst = mem_realloc(*dst, dstlen + srclen);
113 if (!newdst) return;
114
115 memcpy(newdst + dstlen, src, srclen);
116 *dst = newdst;
117 }
118
119 unsigned char *
insert_in_string(unsigned char ** dst,int pos,unsigned char * seq,int seqlen)120 insert_in_string(unsigned char **dst, int pos, unsigned char *seq, int seqlen)
121 {
122 int dstlen = strlen(*dst);
123 unsigned char *string = mem_realloc(*dst, dstlen + seqlen + 1);
124
125 if (!string) return NULL;
126
127 memmove(string + pos + seqlen, string + pos, dstlen - pos + 1);
128 memcpy(string + pos, seq, seqlen);
129 *dst = string;
130
131 return string;
132 }
133
134 unsigned char *
straconcat(unsigned char * str,...)135 straconcat(unsigned char *str, ...)
136 {
137 va_list ap;
138 unsigned char *a;
139 unsigned char *s;
140 unsigned int len;
141
142 assertm(str, "[straconcat]");
143 if_assert_failed { return NULL; }
144
145 len = strlen(str);
146 s = mem_alloc(len + 1);
147 if (!s) return NULL;
148
149 if (len) memcpy(s, str, len);
150
151 va_start(ap, str);
152 while ((a = va_arg(ap, unsigned char *))) {
153 unsigned int l = strlen(a);
154 unsigned char *ns;
155
156 if (!l) continue;
157
158 ns = mem_realloc(s, len + 1 + l);
159 if (!ns) {
160 mem_free(s);
161 va_end(ap);
162 return NULL;
163 }
164
165 s = ns;
166 memcpy(s + len, a, l);
167 len += l;
168 }
169 va_end(ap);
170
171 s[len] = '\0';
172 return s;
173 }
174
175 int
xstrcmp(unsigned char * s1,unsigned char * s2)176 xstrcmp(unsigned char *s1, unsigned char *s2)
177 {
178 if (!s1) return -!!s2;
179 if (!s2) return 1;
180 return strcmp(s1, s2);
181 }
182
183 unsigned char *
safe_strncpy(unsigned char * dst,const unsigned char * src,size_t dst_size)184 safe_strncpy(unsigned char *dst, const unsigned char *src, size_t dst_size)
185 {
186 assertm(dst && src && dst_size > 0, "[safe_strncpy]");
187 if_assert_failed return NULL;
188
189 strncpy(dst, src, dst_size);
190 dst[dst_size - 1] = '\0';
191
192 return dst;
193 }
194
195 #define strlcmp_device(c,s1,n1,s2,n2,t1,t2) { \
196 size_t p; \
197 int d; \
198 \
199 /* XXX: The return value is inconsistent. Hrmpf. Making it consistent
200 * would make the @n1 != @n2 case significantly more expensive, though.
201 * So noone should better rely on the return value actually meaning
202 * anything quantitatively. --pasky */ \
203 \
204 if (!s1 || !s2) \
205 return 1; \
206 \
207 /* n1,n2 is unsigned, so don't assume -1 < 0 ! >:) */ \
208 \
209 /* TODO: Don't precompute strlen()s but rather make the loop smarter.
210 * --pasky */ \
211 if (n1 == -1) n1 = strlen(s1); \
212 if (n2 == -1) n2 = strlen(s2); \
213 \
214 string_assert(errfile, errline, n1 >= 0 && n2 >= 0, c); \
215 \
216 d = n1 - n2; \
217 if (d) return d; \
218 \
219 for (p = 0; p < n1 && s1[p] && s2[p]; p++) { \
220 d = t1 - t2; \
221 if (d) return d; \
222 } \
223 return 0; \
224 }
225
226 int
elinks_strlcmp(const unsigned char * s1,size_t n1,const unsigned char * s2,size_t n2)227 elinks_strlcmp(const unsigned char *s1, size_t n1,
228 const unsigned char *s2, size_t n2)
229 {
230 strlcmp_device("strlcmp", s1, n1, s2, n2, s1[p], s2[p]);
231 }
232
233 int
elinks_strlcasecmp(const unsigned char * s1,size_t n1,const unsigned char * s2,size_t n2,const int locale_indep)234 elinks_strlcasecmp(const unsigned char *s1, size_t n1,
235 const unsigned char *s2, size_t n2,
236 const int locale_indep)
237 {
238 if (locale_indep) {
239 strlcmp_device("strlcasecmp", s1, n1, s2, n2, c_toupper(s1[p]), c_toupper(s2[p]));
240 }
241 else {
242 strlcmp_device("strlcasecmp", s1, n1, s2, n2, toupper(s1[p]), toupper(s2[p]));
243 }
244 }
245
246 int
c_strcasecmp(const char * s1,const char * s2)247 c_strcasecmp(const char *s1, const char *s2)
248 {
249 for (;; s1++, s2++) {
250 unsigned char c1 = c_tolower(*(const unsigned char *) s1);
251 unsigned char c2 = c_tolower(*(const unsigned char *) s2);
252
253 if (c1 != c2)
254 return (c1 < c2) ? -1: +1;
255 if (c1 == '\0')
256 return 0;
257 }
258 }
259
c_strncasecmp(const char * s1,const char * s2,size_t n)260 int c_strncasecmp(const char *s1, const char *s2, size_t n)
261 {
262 for (; n > 0; n--, s1++, s2++) {
263 unsigned char c1 = c_tolower(*(const unsigned char *) s1);
264 unsigned char c2 = c_tolower(*(const unsigned char *) s2);
265
266 if (c1 != c2)
267 return (c1 < c2) ? -1: +1;
268 if (c1 == '\0')
269 return 0;
270 }
271 return 0;
272 }
273
274 /* c_strcasestr - adapted from src/osdep/stub.c */
c_strcasestr(const char * haystack,const char * needle)275 char * c_strcasestr(const char *haystack, const char *needle)
276 {
277 size_t haystack_length = strlen(haystack);
278 size_t needle_length = strlen(needle);
279 int i;
280
281 if (haystack_length < needle_length)
282 return NULL;
283
284 for (i = haystack_length - needle_length + 1; i; i--) {
285 if (!c_strncasecmp(haystack, needle, needle_length))
286 return (char *) haystack;
287 haystack++;
288 }
289
290 return NULL;
291 }
292
293 /* The new string utilities: */
294
295 /* TODO Currently most of the functions use add_bytes_to_string() as a backend
296 * instead we should optimize each function. */
297
298 inline struct string *
299 #ifdef DEBUG_MEMLEAK
init_string__(unsigned char * file,int line,struct string * string)300 init_string__(unsigned char *file, int line, struct string *string)
301 #else
302 init_string(struct string *string)
303 #endif
304 {
305 assertm(string, "[init_string]");
306 if_assert_failed { return NULL; }
307
308 string->length = 0;
309 #ifdef DEBUG_MEMLEAK
310 string->source = debug_mem_alloc(file, line, STRING_GRANULARITY + 1);
311 #else
312 string->source = mem_alloc(STRING_GRANULARITY + 1);
313 #endif
314 if (!string->source) return NULL;
315
316 *string->source = '\0';
317
318 set_string_magic(string);
319
320 return string;
321 }
322
323 inline void
done_string(struct string * string)324 done_string(struct string *string)
325 {
326 assertm(string, "[done_string]");
327 if_assert_failed { return; }
328
329 if (string->source) {
330 /* We only check the magic if we have to free anything so
331 * that done_string() can be called multiple times without
332 * blowing up something */
333 check_string_magic(string);
334 mem_free(string->source);
335 }
336
337 /* Blast everything including the magic */
338 memset(string, 0, sizeof(*string));
339 }
340
341 inline struct string *
add_to_string(struct string * string,const unsigned char * source)342 add_to_string(struct string *string, const unsigned char *source)
343 {
344 assertm(string && source, "[add_to_string]");
345 if_assert_failed { return NULL; }
346
347 check_string_magic(string);
348
349 if (!*source) return string;
350
351 return add_bytes_to_string(string, source, strlen(source));
352 }
353
354 inline struct string *
add_crlf_to_string(struct string * string)355 add_crlf_to_string(struct string *string)
356 {
357 assertm(string, "[add_crlf_to_string]");
358 if_assert_failed { return NULL; }
359
360 check_string_magic(string);
361
362 if (!realloc_string(string, string->length + 2))
363 return NULL;
364
365 string->source[string->length++] = ASCII_CR;
366 string->source[string->length++] = ASCII_LF;
367 string->source[string->length] = '\0';
368
369 return string;
370 }
371
372 inline struct string *
add_string_to_string(struct string * string,struct string * from)373 add_string_to_string(struct string *string, struct string *from)
374 {
375 assertm(string && from, "[add_string_to_string]");
376 if_assert_failed { return NULL; }
377
378 check_string_magic(string);
379 check_string_magic(from);
380
381 if (!from->length) return string; /* optimization only */
382
383 return add_bytes_to_string(string, from->source, from->length);
384 }
385
386 struct string *
add_file_to_string(struct string * string,unsigned char * filename)387 add_file_to_string(struct string *string, unsigned char *filename)
388 {
389 FILE *file;
390 off_t filelen;
391 int newlength;
392
393 assertm(string && filename, "[add_file_to_string]");
394 if_assert_failed { return NULL; }
395
396 check_string_magic(string);
397
398 file = fopen(filename, "rb");
399 if (!file) return NULL;
400
401 if (fseeko(file, 0, SEEK_END)) goto err;
402
403 filelen = ftello(file);
404 if (filelen == -1) goto err;
405
406 if (fseeko(file, 0, SEEK_SET)) goto err;
407
408 newlength = string->length + filelen;
409 if (!realloc_string(string, newlength)) goto err;
410
411 string->length += fread(string->source + string->length, 1,
412 (size_t) filelen, file);
413 string->source[string->length] = 0;
414 fclose(file);
415
416 if (string->length != newlength) goto err;
417
418 return string;
419
420 err:
421 fclose(file);
422
423 return NULL;
424 }
425
426 struct string *
string_concat(struct string * string,...)427 string_concat(struct string *string, ...)
428 {
429 va_list ap;
430 unsigned char *source;
431
432 assertm(string, "[string_concat]");
433 if_assert_failed { return NULL; }
434
435 check_string_magic(string);
436
437 va_start(ap, string);
438 while ((source = va_arg(ap, unsigned char *)))
439 if (*source)
440 add_to_string(string, source);
441
442 va_end(ap);
443
444 return string;
445 }
446
447 inline struct string *
add_char_to_string(struct string * string,unsigned char character)448 add_char_to_string(struct string *string, unsigned char character)
449 {
450 assertm(string && character, "[add_char_to_string]");
451 if_assert_failed { return NULL; }
452
453 check_string_magic(string);
454
455 if (!realloc_string(string, string->length + 1))
456 return NULL;
457
458 string->source[string->length++] = character;
459 string->source[string->length] = '\0';
460
461 return string;
462 }
463
464 inline struct string *
add_xchar_to_string(struct string * string,unsigned char character,int times)465 add_xchar_to_string(struct string *string, unsigned char character, int times)
466 {
467 int newlength;
468
469 assertm(string && character && times >= 0, "[add_xchar_to_string]");
470 if_assert_failed { return NULL; }
471
472 check_string_magic(string);
473
474 if (!times) return string;
475
476 newlength = string->length + times;
477 if (!realloc_string(string, newlength))
478 return NULL;
479
480 memset(string->source + string->length, character, times);
481 string->length = newlength;
482 string->source[newlength] = '\0';
483
484 return string;
485 }
486
487 /* Add printf-like format string to @string. */
488 struct string *
add_format_to_string(struct string * string,unsigned char * format,...)489 add_format_to_string(struct string *string, unsigned char *format, ...)
490 {
491 int newlength;
492 int width;
493 va_list ap;
494 va_list ap2;
495
496 assertm(string && format, "[add_format_to_string]");
497 if_assert_failed { return NULL; }
498
499 check_string_magic(string);
500
501 va_start(ap, format);
502 VA_COPY(ap2, ap);
503
504 width = vsnprintf(NULL, 0, format, ap2);
505 if (width <= 0) return NULL;
506
507 newlength = string->length + width;
508 if (!realloc_string(string, newlength))
509 return NULL;
510
511 vsnprintf(&string->source[string->length], width + 1, format, ap);
512
513 va_end(ap);
514
515 string->length = newlength;
516 string->source[newlength] = '\0';
517
518 return string;
519 }
520
521 struct string *
add_to_string_list(struct list_head * list,const unsigned char * source,int length)522 add_to_string_list(struct list_head *list, const unsigned char *source,
523 int length)
524 {
525 struct string_list_item *item;
526 struct string *string;
527
528 assertm(list && source, "[add_to_string_list]");
529 if_assert_failed return NULL;
530
531 item = mem_alloc(sizeof(*item));
532 if (!item) return NULL;
533
534 string = &item->string;
535 if (length < 0) length = strlen(source);
536
537 if (!init_string(string)
538 || !add_bytes_to_string(string, source, length)) {
539 done_string(string);
540 mem_free(item);
541 return NULL;
542 }
543
544 add_to_list_end(*list, item);
545 return string;
546 }
547
548 void
free_string_list(struct list_head * list)549 free_string_list(struct list_head *list)
550 {
551 assertm(list, "[free_string_list]");
552 if_assert_failed return;
553
554 while (!list_empty(*list)) {
555 struct string_list_item *item = list->next;
556
557 del_from_list(item);
558 done_string(&item->string);
559 mem_free(item);
560 }
561 }
562