1 /** String handling functions
9 #define _GNU_SOURCE /* XXX: fseeko, ftello */
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
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); \
44 debug_memacpy(const unsigned char *f
, int l
, const unsigned char *src
, int len
)
48 string_assert(f
, l
, len
>= 0, "memacpy");
49 if_assert_failed len
= 0;
51 m
= debug_mem_alloc(f
, l
, len
+ 1);
54 if (src
&& len
) memcpy(m
, src
, len
);
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 */
72 memacpy(const unsigned char *src
, int len
)
76 assertm(len
>= 0, "[memacpy]");
77 if_assert_failed
{ len
= 0; }
79 m
= mem_alloc(len
+ 1);
82 if (src
&& len
) memcpy(m
, src
, len
);
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 */
101 add_to_strn(unsigned char **dst
, const unsigned char *src
)
103 unsigned char *newdst
;
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
);
115 memcpy(newdst
+ dstlen
, src
, srclen
);
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
);
136 straconcat(const unsigned char *str
, ...)
139 const unsigned char *a
;
143 assertm(str
!= NULL
, "[straconcat]");
144 if_assert_failed
{ return NULL
; }
147 s
= mem_alloc(len
+ 1);
150 if (len
) memcpy(s
, str
, len
);
153 while ((a
= va_arg(ap
, const unsigned char *))) {
154 unsigned int l
= strlen(a
);
159 ns
= mem_realloc(s
, len
+ 1 + l
);
167 memcpy(s
+ len
, a
, l
);
177 xstrcmp(const unsigned char *s1
, const unsigned char *s2
)
179 if (!s1
) return -!!s2
;
181 return strcmp(s1
, s2
);
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';
196 #define strlcmp_device(c,s1,n1,s2,n2,t1,t2) { \
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 */ \
208 /* n1,n2 is unsigned, so don't assume -1 < 0 ! >:) */ \
210 /* TODO: Don't precompute strlen()s but rather make the loop smarter.
212 if (n1 == -1) n1 = strlen(s1); \
213 if (n2 == -1) n2 = strlen(s2); \
215 string_assert(errfile, errline, n1 >= 0 && n2 >= 0, c); \
220 for (p = 0; p < n1 && s1[p] && s2[p]; p++) { \
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
*
249 init_string__(const unsigned char *file
, int line
, struct string
*string
)
251 init_string(struct string
*string
)
254 assertm(string
!= NULL
, "[init_string]");
255 if_assert_failed
{ return NULL
; }
259 string
->source
= debug_mem_alloc(file
, line
, STRING_GRANULARITY
+ 1);
261 string
->source
= mem_alloc(STRING_GRANULARITY
+ 1);
263 if (!string
->source
) return NULL
;
265 *string
->source
= '\0';
267 set_string_magic(string
);
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))
316 string
->source
[string
->length
++] = ASCII_CR
;
317 string
->source
[string
->length
++] = ASCII_LF
;
318 string
->source
[string
->length
] = '\0';
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 */
340 add_file_to_string(struct string
*string
, const unsigned char *filename
)
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;
369 if (string
->length
!= newlength
) goto err
;
380 string_concat(struct string
*string
, ...)
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 *)))
393 add_to_string(string
, source
);
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))
412 string
->source
[string
->length
++] = character
;
413 string
->source
[string
->length
] = '\0';
418 inline struct string
*
419 add_xchar_to_string(struct string
*string
, unsigned char character
, int times
)
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
))
434 memset(string
->source
+ string
->length
, character
, times
);
435 string
->length
= newlength
;
436 string
->source
[newlength
] = '\0';
441 /** Add printf()-style format string to @a string. */
443 add_format_to_string(struct string
*string
, const unsigned char *format
, ...)
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
);
457 if (width
<= 0) return NULL
;
459 newlength
= string
->length
+ width
;
460 if (!realloc_string(string
, newlength
))
463 va_start(ap
, format
);
464 vsnprintf(&string
->source
[string
->length
], width
+ 1, format
, ap
);
467 string
->length
= newlength
;
468 string
->source
[newlength
] = '\0';
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
)) {
496 add_to_list_end(*list
, item
);
500 /** @relates string_list_item */
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
;
511 done_string(&item
->string
);