1 /* String handling functions */
8 #define _GNU_SOURCE /* XXX: fseeko, ftello */
19 #include "util/conv.h"
20 #include "util/error.h"
21 #include "util/memdebug.h"
22 #include "util/memory.h"
23 #include "util/string.h"
24 #include "util/snprintf.h"
27 /* This file looks to be slowly being overloaded by a lot of various stuff,
28 * like memory managment, stubs, tools, granular and non-granular strings,
29 * struct string object... Perhaps util/memory.* and util/stubs.* (stubs.h
30 * probably included in elinks.h, it's important enough) would be nice to
34 #define string_assert(f, l, x, o) \
35 if ((assert_failed = !(x))) { \
36 errfile = f, errline = l, \
37 elinks_internal("[" o "] assertion " #x " failed!"); \
43 debug_memacpy(unsigned char *f
, int l
, unsigned char *src
, int len
)
47 string_assert(f
, l
, len
>= 0, "memacpy");
48 if_assert_failed len
= 0;
50 m
= debug_mem_alloc(f
, l
, len
+ 1);
53 if (src
&& len
) memcpy(m
, src
, len
);
60 debug_stracpy(unsigned char *f
, int l
, unsigned char *src
)
62 string_assert(f
, l
, src
, "stracpy");
63 if_assert_failed
return NULL
;
65 return debug_memacpy(f
, l
, src
, strlen(src
));
68 #else /* DEBUG_MEMLEAK */
71 memacpy(unsigned char *src
, int len
)
75 assertm(len
>= 0, "[memacpy]");
76 if_assert_failed
{ len
= 0; }
78 m
= mem_alloc(len
+ 1);
81 if (src
&& len
) memcpy(m
, src
, len
);
88 stracpy(unsigned char *src
)
90 assertm(src
, "[stracpy]");
91 if_assert_failed
return NULL
;
93 return memacpy(src
, strlen(src
));
96 #endif /* DEBUG_MEMLEAK */
100 add_to_strn(unsigned char **dst
, unsigned char *src
)
102 unsigned char *newdst
;
106 assertm(*dst
&& src
, "[add_to_strn]");
107 if_assert_failed
return;
109 dstlen
= strlen(*dst
);
110 srclen
= strlen(src
) + 1; /* Include the NUL char! */
111 newdst
= mem_realloc(*dst
, dstlen
+ srclen
);
114 memcpy(newdst
+ dstlen
, src
, srclen
);
119 insert_in_string(unsigned char **dst
, int pos
, unsigned char *seq
, int seqlen
)
121 int dstlen
= strlen(*dst
);
122 unsigned char *string
= mem_realloc(*dst
, dstlen
+ seqlen
+ 1);
124 if (!string
) return NULL
;
126 memmove(string
+ pos
+ seqlen
, string
+ pos
, dstlen
- pos
+ 1);
127 memcpy(string
+ pos
, seq
, seqlen
);
134 straconcat(unsigned char *str
, ...)
141 assertm(str
, "[straconcat]");
142 if_assert_failed
{ return NULL
; }
145 s
= mem_alloc(len
+ 1);
148 if (len
) memcpy(s
, str
, len
);
151 while ((a
= va_arg(ap
, unsigned char *))) {
152 unsigned int l
= strlen(a
);
157 ns
= mem_realloc(s
, len
+ 1 + l
);
165 memcpy(s
+ len
, a
, l
);
175 xstrcmp(unsigned char *s1
, unsigned char *s2
)
177 if (!s1
) return -!!s2
;
179 return strcmp(s1
, s2
);
183 safe_strncpy(unsigned char *dst
, const unsigned char *src
, size_t dst_size
)
185 assertm(dst
&& src
&& dst_size
> 0, "[safe_strncpy]");
186 if_assert_failed
return NULL
;
188 strncpy(dst
, src
, dst_size
);
189 dst
[dst_size
- 1] = '\0';
194 #define strlcmp_device(c,s1,n1,s2,n2,t1,t2) { \
198 /* XXX: The return value is inconsistent. Hrmpf. Making it consistent
199 * would make the @n1 != @n2 case significantly more expensive, though.
200 * So noone should better rely on the return value actually meaning
201 * anything quantitatively. --pasky */ \
206 /* n1,n2 is unsigned, so don't assume -1 < 0 ! >:) */ \
208 /* TODO: Don't precompute strlen()s but rather make the loop smarter.
210 if (n1 == -1) n1 = strlen(s1); \
211 if (n2 == -1) n2 = strlen(s2); \
213 string_assert(errfile, errline, n1 >= 0 && n2 >= 0, c); \
218 for (p = 0; p < n1 && s1[p] && s2[p]; p++) { \
226 elinks_strlcmp(const unsigned char *s1
, size_t n1
,
227 const unsigned char *s2
, size_t n2
)
229 strlcmp_device("strlcmp", s1
, n1
, s2
, n2
, s1
[p
], s2
[p
]);
233 elinks_strlcasecmp(const unsigned char *s1
, size_t n1
,
234 const unsigned char *s2
, size_t n2
)
236 strlcmp_device("strlcasecmp", s1
, n1
, s2
, n2
, toupper(s1
[p
]), toupper(s2
[p
]));
240 /* The new string utilities: */
242 /* TODO Currently most of the functions use add_bytes_to_string() as a backend
243 * instead we should optimize each function. */
245 inline struct string
*
247 init_string__(unsigned char *file
, int line
, struct string
*string
)
249 init_string(struct string
*string
)
252 assertm(string
, "[init_string]");
253 if_assert_failed
{ return NULL
; }
257 string
->source
= debug_mem_alloc(file
, line
, STRING_GRANULARITY
+ 1);
259 string
->source
= mem_alloc(STRING_GRANULARITY
+ 1);
261 if (!string
->source
) return NULL
;
263 *string
->source
= '\0';
265 set_string_magic(string
);
271 done_string(struct string
*string
)
273 assertm(string
, "[done_string]");
274 if_assert_failed
{ return; }
276 if (string
->source
) {
277 /* We only check the magic if we have to free anything so
278 * that done_string() can be called multiple times without
279 * blowing up something */
280 check_string_magic(string
);
281 mem_free(string
->source
);
284 /* Blast everything including the magic */
285 memset(string
, 0, sizeof(*string
));
288 inline struct string
*
289 add_to_string(struct string
*string
, const unsigned char *source
)
291 assertm(string
&& source
, "[add_to_string]");
292 if_assert_failed
{ return NULL
; }
294 check_string_magic(string
);
296 if (!*source
) return string
;
298 return add_bytes_to_string(string
, source
, strlen(source
));
301 inline struct string
*
302 add_crlf_to_string(struct string
*string
)
304 assertm(string
, "[add_crlf_to_string]");
305 if_assert_failed
{ return NULL
; }
307 check_string_magic(string
);
309 if (!realloc_string(string
, string
->length
+ 2))
312 string
->source
[string
->length
++] = ASCII_CR
;
313 string
->source
[string
->length
++] = ASCII_LF
;
314 string
->source
[string
->length
] = '\0';
319 inline struct string
*
320 add_string_to_string(struct string
*string
, struct string
*from
)
322 assertm(string
&& from
, "[add_string_to_string]");
323 if_assert_failed
{ return NULL
; }
325 check_string_magic(string
);
326 check_string_magic(from
);
328 if (!*from
->source
) return NULL
;
330 return add_bytes_to_string(string
, from
->source
, from
->length
);
334 add_file_to_string(struct string
*string
, unsigned char *filename
)
340 assertm(string
&& filename
, "[add_file_to_string]");
341 if_assert_failed
{ return NULL
; }
343 check_string_magic(string
);
345 file
= fopen(filename
, "rb");
346 if (!file
) return NULL
;
348 if (fseeko(file
, 0, SEEK_END
)) goto err
;
350 filelen
= ftello(file
);
351 if (filelen
== -1) goto err
;
353 if (fseeko(file
, 0, SEEK_SET
)) goto err
;
355 newlength
= string
->length
+ filelen
;
356 if (!realloc_string(string
, newlength
)) goto err
;
358 string
->length
+= fread(string
->source
+ string
->length
, 1,
359 (size_t) filelen
, file
);
360 string
->source
[string
->length
] = 0;
363 if (string
->length
!= newlength
) goto err
;
374 string_concat(struct string
*string
, ...)
377 unsigned char *source
;
379 assertm(string
, "[string_concat]");
380 if_assert_failed
{ return NULL
; }
382 check_string_magic(string
);
384 va_start(ap
, string
);
385 while ((source
= va_arg(ap
, unsigned char *)))
387 add_to_string(string
, source
);
394 inline struct string
*
395 add_char_to_string(struct string
*string
, unsigned char character
)
397 assertm(string
&& character
, "[add_char_to_string]");
398 if_assert_failed
{ return NULL
; }
400 check_string_magic(string
);
402 if (!realloc_string(string
, string
->length
+ 1))
405 string
->source
[string
->length
++] = character
;
406 string
->source
[string
->length
] = '\0';
411 inline struct string
*
412 add_xchar_to_string(struct string
*string
, unsigned char character
, int times
)
416 assertm(string
&& character
&& times
>= 0, "[add_xchar_to_string]");
417 if_assert_failed
{ return NULL
; }
419 check_string_magic(string
);
421 if (!times
) return string
;
423 newlength
= string
->length
+ times
;
424 if (!realloc_string(string
, newlength
))
427 memset(string
->source
+ string
->length
, character
, times
);
428 string
->length
= newlength
;
429 string
->source
[newlength
] = '\0';
434 /* Add printf-like format string to @string. */
436 add_format_to_string(struct string
*string
, unsigned char *format
, ...)
443 assertm(string
&& format
, "[add_format_to_string]");
444 if_assert_failed
{ return NULL
; }
446 check_string_magic(string
);
448 va_start(ap
, format
);
451 width
= vsnprintf(NULL
, 0, format
, ap2
);
452 if (width
<= 0) return NULL
;
454 newlength
= string
->length
+ width
;
455 if (!realloc_string(string
, newlength
))
458 vsnprintf(&string
->source
[string
->length
], width
+ 1, format
, ap
);
462 string
->length
= newlength
;
463 string
->source
[newlength
] = '\0';
469 add_to_string_list(struct list_head
*list
, const unsigned char *source
,
472 struct string_list_item
*item
;
473 struct string
*string
;
475 assertm(list
&& source
, "[add_to_string_list]");
476 if_assert_failed
return NULL
;
478 item
= mem_alloc(sizeof(*item
));
479 if (!item
) return NULL
;
481 string
= &item
->string
;
482 if (length
< 0) length
= strlen(source
);
484 if (!init_string(string
)
485 || !add_bytes_to_string(string
, source
, length
)) {
491 add_to_list_end(*list
, item
);
496 free_string_list(struct list_head
*list
)
498 assertm(list
, "[free_string_list]");
499 if_assert_failed
return;
501 while (!list_empty(*list
)) {
502 struct string_list_item
*item
= list
->next
;
505 done_string(&item
->string
);