1 /** String handling functions
9 #define _GNU_SOURCE /* XXX: fseeko, ftello */
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
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); \
45 debug_memacpy(const unsigned char *f
, int l
, const unsigned char *src
, int len
)
49 string_assert(f
, l
, len
>= 0, "memacpy");
50 if_assert_failed len
= 0;
52 m
= debug_mem_alloc(f
, l
, len
+ 1);
55 if (src
&& len
) memcpy(m
, src
, len
);
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 */
73 memacpy(const unsigned char *src
, int len
)
77 assertm(len
>= 0, "[memacpy]");
78 if_assert_failed
{ len
= 0; }
80 m
= mem_alloc(len
+ 1);
83 if (src
&& len
) memcpy(m
, src
, len
);
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 */
102 add_to_strn(unsigned char **dst
, const unsigned char *src
)
104 unsigned char *newdst
;
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
);
116 memcpy(newdst
+ dstlen
, src
, srclen
);
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
);
137 straconcat(const unsigned char *str
, ...)
140 const unsigned char *a
;
144 assertm(str
!= NULL
, "[straconcat]");
145 if_assert_failed
{ return NULL
; }
148 s
= mem_alloc(len
+ 1);
151 if (len
) memcpy(s
, str
, len
);
154 while ((a
= va_arg(ap
, const unsigned char *))) {
155 unsigned int l
= strlen(a
);
160 ns
= mem_realloc(s
, len
+ 1 + l
);
168 memcpy(s
+ len
, a
, l
);
178 xstrcmp(const unsigned char *s1
, const unsigned char *s2
)
180 if (!s1
) return -!!s2
;
182 return strcmp(s1
, s2
);
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';
197 #define strlcmp_device(c,s1,n1,s2,n2,t1,t2) { \
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 */ \
209 /* n1,n2 is unsigned, so don't assume -1 < 0 ! >:) */ \
211 /* TODO: Don't precompute strlen()s but rather make the loop smarter.
213 if (n1 == -1) n1 = strlen(s1); \
214 if (n2 == -1) n2 = strlen(s2); \
216 string_assert(errfile, errline, n1 >= 0 && n2 >= 0, c); \
221 for (p = 0; p < n1 && s1[p] && s2[p]; p++) { \
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
)
241 strlcmp_device("strlcasecmp", s1
, n1
, s2
, n2
, c_toupper(s1
[p
]), c_toupper(s2
[p
]));
244 strlcmp_device("strlcasecmp", s1
, n1
, s2
, n2
, toupper(s1
[p
]), toupper(s2
[p
]));
248 /* strlcasestr - adapted from c_strcasestr */
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
;
257 if (haystack_length
< needle_length
)
260 for (i
= haystack_length
- needle_length
+ 1; i
; i
--) {
261 if (!c_strncasecmp(haystack
, needle
, needle_length
))
262 return (char *) haystack
;
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
);
277 return (c1
< c2
) ? -1: +1;
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
);
290 return (c1
< c2
) ? -1: +1;
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
);
304 if (haystack_length
< needle_length
)
307 for (i
= haystack_length
- needle_length
+ 1; i
; i
--) {
308 if (!c_strncasecmp(haystack
, needle
, needle_length
))
309 return (char *) haystack
;
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
*
323 init_string__(const unsigned char *file
, int line
, struct string
*string
)
325 init_string(struct string
*string
)
328 assertm(string
!= NULL
, "[init_string]");
329 if_assert_failed
{ return NULL
; }
333 string
->source
= debug_mem_alloc(file
, line
, STRING_GRANULARITY
+ 1);
335 string
->source
= mem_alloc(STRING_GRANULARITY
+ 1);
337 if (!string
->source
) return NULL
;
339 *string
->source
= '\0';
341 set_string_magic(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))
390 string
->source
[string
->length
++] = ASCII_CR
;
391 string
->source
[string
->length
++] = ASCII_LF
;
392 string
->source
[string
->length
] = '\0';
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 */
414 add_file_to_string(struct string
*string
, const unsigned char *filename
)
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;
443 if (string
->length
!= newlength
) goto err
;
454 string_concat(struct string
*string
, ...)
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 *)))
467 add_to_string(string
, source
);
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))
486 string
->source
[string
->length
++] = character
;
487 string
->source
[string
->length
] = '\0';
492 NONSTATIC_INLINE
struct string
*
493 add_xchar_to_string(struct string
*string
, unsigned char character
, int times
)
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
))
508 memset(string
->source
+ string
->length
, character
, times
);
509 string
->length
= newlength
;
510 string
->source
[newlength
] = '\0';
515 /** Add printf()-style format string to @a string. */
517 add_format_to_string(struct string
*string
, const unsigned char *format
, ...)
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
);
531 if (width
<= 0) return NULL
;
533 newlength
= string
->length
+ width
;
534 if (!realloc_string(string
, newlength
))
537 va_start(ap
, format
);
538 vsnprintf(&string
->source
[string
->length
], width
+ 1, format
, ap
);
541 string
->length
= newlength
;
542 string
->source
[newlength
] = '\0';
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
)) {
570 add_to_list_end(*list
, item
);
574 /** @relates string_list_item */
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
;
585 done_string(&item
->string
);