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