1 /* Leave the OpenBSD version below so we can track upstream fixes */
2 /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
3
4 /*
5 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <limits.h>
29 #include <ctype.h>
30
31 #ifdef HAVE_BSD_STRING_H
32 # include <bsd/string.h>
33 #endif
34
35 #ifdef HAVE_ICONV
36 # include <errno.h>
37 # include <iconv.h>
38 #endif
39
40 #include "eina_private.h"
41 #include "eina_str.h"
42 #include "eina_cpu.h"
43
44 /*============================================================================*
45 * Local *
46 *============================================================================*/
47
48 /**
49 * @cond LOCAL
50 */
51
52 /*
53 * Internal helper function used by eina_str_has_suffix() and
54 * eina_str_has_extension()
55 */
56 static inline Eina_Bool
eina_str_has_suffix_helper(const char * str,const char * suffix,int (* cmp)(const char *,const char *))57 eina_str_has_suffix_helper(const char *str,
58 const char *suffix,
59 int (*cmp)(const char *, const char *))
60 {
61 size_t str_len;
62 size_t suffix_len;
63
64 if ((!str) || (!suffix)) return EINA_FALSE;
65 str_len = strlen(str);
66 suffix_len = eina_strlen_bounded(suffix, str_len);
67 if (suffix_len == (size_t)-1)
68 return EINA_FALSE;
69
70 return cmp(str + str_len - suffix_len, suffix) == 0;
71 }
72
73 static inline char **
eina_str_split_full_helper(const char * str,const char * delim,int max_tokens,unsigned int * elements)74 eina_str_split_full_helper(const char *str,
75 const char *delim,
76 int max_tokens,
77 unsigned int *elements)
78 {
79 char *s, *pos, **str_array;
80 const char *src;
81 size_t len, dlen;
82 unsigned int tokens = 0, x;
83 const char *idx[256] = {NULL};
84
85 if ((!str) || (!delim))
86 {
87 if (elements)
88 *elements = 0;
89
90 return NULL;
91 }
92 if (max_tokens < 0) max_tokens = 0;
93 if (max_tokens == 1)
94 {
95 str_array = malloc(sizeof(char *) * 2);
96 if (!str_array)
97 {
98 if (elements)
99 *elements = 0;
100
101 return NULL;
102 }
103
104 s = strdup(str);
105 if (!s)
106 {
107 free(str_array);
108 if (elements)
109 *elements = 0;
110
111 return NULL;
112 }
113 if (elements)
114 *elements = 1;
115 str_array[0] = s;
116 str_array[1] = NULL;
117 return str_array;
118 }
119 dlen = strlen(delim);
120 if (dlen == 0)
121 {
122 if (elements)
123 *elements = 0;
124
125 return NULL;
126 }
127
128 src = str;
129 /* count tokens and check strlen(str) */
130 while (*src != '\0')
131 {
132 const char *d = delim, *d_end = d + dlen;
133 const char *tmp = src;
134 for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
135 {
136 if (EINA_LIKELY(*d != *tmp))
137 break;
138 }
139 if (EINA_UNLIKELY(d == d_end))
140 {
141 src = tmp;
142 if (tokens < (sizeof(idx) / sizeof(idx[0])))
143 {
144 idx[tokens] = tmp;
145 //printf("token %d='%s'\n", tokens + 1, idx[tokens]);
146 }
147 tokens++;
148 if (tokens && (tokens == (unsigned int)max_tokens)) break;
149 }
150 else
151 src++;
152 }
153 len = src - str + strlen(src);
154
155 str_array = malloc(sizeof(char *) * (tokens + 2));
156 if (!str_array)
157 {
158 if (elements)
159 *elements = 0;
160
161 return NULL;
162 }
163
164 if (!tokens)
165 {
166 s = strdup(str);
167 if (!s)
168 {
169 free(str_array);
170 if (elements)
171 *elements = 0;
172
173 return NULL;
174 }
175 str_array[0] = s;
176 str_array[1] = NULL;
177 if (elements)
178 *elements = 1;
179 return str_array;
180 }
181
182 s = malloc(len + 1);
183 if (!s)
184 {
185 free(str_array);
186 if (elements)
187 *elements = 0;
188
189 return NULL;
190 }
191
192 str_array[0] = s;
193
194 if (len == tokens * dlen)
195 {
196 /* someone's having a laugh somewhere */
197 memset(s, 0, len + 1);
198 for (x = 1; x < tokens + 1; x++)
199 str_array[x] = s + x;
200 str_array[x] = NULL;
201 if (elements)
202 *elements = x;
203 return str_array;
204 }
205 /* copy tokens and string */
206 if (idx[0] - str - dlen > len)
207 {
208 /* FIXME: don't think this can happen but putting this here just in case */
209 abort();
210 }
211 pos = s;
212 for (x = 0; x < MIN(tokens, (sizeof(idx) / sizeof(idx[0]))); x++)
213 {
214 if (x + 1 < (sizeof(idx) / sizeof(idx[0])))
215 {
216 /* first one is special */
217 if (!x)
218 {
219 eina_strlcpy(pos, str, idx[x] - str - dlen + 1);
220 str_array[x] = pos;
221 //printf("str_array[%d] = '%s'\n", x, str_array[x]);
222 pos += idx[x] - str - dlen + 1;
223 if ((tokens == 1) && (idx[0]))
224 {
225 eina_strlcpy(pos, idx[x], len + 1 - (pos - s));
226 x++, tokens++;
227 str_array[x] = pos;
228 }
229 }
230 /* more tokens */
231 else if (idx[x + 1])
232 {
233 eina_strlcpy(pos, idx[x - 1], idx[x] - idx[x - 1] - dlen + 1);
234 str_array[x] = pos;
235 //printf("str_array[%d] = '%s'\n", x, str_array[x]);
236 pos += idx[x] - idx[x - 1] - dlen + 1;
237 }
238 /* last token */
239 else
240 {
241 if (max_tokens && ((unsigned int)max_tokens < tokens + 1))
242 eina_strlcpy(pos, idx[x - 1], len + 1 - (pos - s));
243 else
244 {
245 //printf("diff: %d\n", len + 1 - (pos - s));
246 eina_strlcpy(pos, idx[x - 1], idx[x] - idx[x - 1] - dlen + 1);
247 str_array[x] = pos;
248 //printf("str_array[%d] = '%s'\n", x, str_array[x]);
249 pos += idx[x] - idx[x - 1] - dlen + 1;
250 x++, tokens++;
251 eina_strlcpy(pos, idx[x - 1], len + 1 - (pos - s));
252 }
253 str_array[x] = pos;
254 //printf("str_array[%d] = '%s'\n", x, str_array[x]);
255 }
256 }
257 /* no more tokens saved after this one */
258 else
259 {
260 eina_strlcpy(pos, idx[x - 1], idx[x] - idx[x - 1] - dlen + 1);
261 str_array[x] = pos;
262 //printf("str_array[%d] = '%s'\n", x, str_array[x]);
263 pos += idx[x] - idx[x - 1] - dlen + 1;
264 src = idx[x];
265 x++, tokens++;
266 str_array[x] = s = pos;
267 break;
268 }
269 }
270 if ((x != tokens) && ((!max_tokens) || (x < tokens)))
271 {
272 while (*src != '\0')
273 {
274 const char *d = delim, *d_end = d + dlen;
275 const char *tmp = src;
276 for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
277 {
278 if (EINA_LIKELY(*d != *tmp))
279 break;
280 }
281 if (((!max_tokens) || (((tokens == (unsigned int)max_tokens) || x < tokens - 2))) && (EINA_UNLIKELY(d == d_end)))
282 {
283 src = tmp;
284 *s = '\0';
285 s++, x++;
286 //printf("str_array[%d] = '%s'\n", x, str_array[x - 1]);
287 str_array[x] = s;
288 }
289 else
290 {
291 *s = *src;
292 s++, src++;
293 }
294 }
295 *s = 0;
296 }
297 str_array[tokens] = NULL;
298 if (elements)
299 *elements = tokens;
300
301 return str_array;
302 }
303
304 /**
305 * @endcond
306 */
307
308 /*============================================================================*
309 * Global *
310 *============================================================================*/
311
312 /*============================================================================*
313 * API *
314 *============================================================================*/
315
316 EAPI size_t
eina_strlcpy(char * dst,const char * src,size_t siz)317 eina_strlcpy(char *dst, const char *src, size_t siz)
318 {
319 #ifdef HAVE_STRLCPY
320 return strlcpy(dst, src, siz);
321 #else
322 char *d = dst;
323 const char *s = src;
324 size_t n = siz;
325
326 /* Copy as many bytes as will fit */
327 if (n != 0)
328 while (--n != 0)
329 {
330 if ((*d++ = *s++) == '\0')
331 break;
332 }
333
334 /* Not enough room in dst, add NUL and traverse rest of src */
335 if (n == 0)
336 {
337 if (siz != 0)
338 *d = '\0'; /* NUL-terminate dst */
339
340 while (*s++)
341 ;
342 }
343
344 return(s - src - 1); /* count does not include NUL */
345 #endif
346 }
347
348 EAPI size_t
eina_strlcat(char * dst,const char * src,size_t siz)349 eina_strlcat(char *dst, const char *src, size_t siz)
350 {
351 char *d = dst;
352 const char *s = src;
353 size_t n = siz;
354 size_t dlen;
355
356 /* Find the end of dst and adjust bytes left but don't go past end */
357 while (n-- != 0 && *d != '\0')
358 d++;
359 dlen = d - dst;
360 n = siz - dlen;
361
362 if (n == 0)
363 return(dlen + (s ? strlen(s) : 0));
364
365 if (s != NULL)
366 {
367 while (*s != '\0') {
368 if (n != 1)
369 {
370 *d++ = *s;
371 n--;
372 }
373
374 s++;
375 }
376 }
377 *d = '\0';
378
379 return(dlen + (s - src)); /* count does not include NUL */
380 }
381
382 EAPI char *
eina_strftime(const char * format,const struct tm * tm)383 eina_strftime(const char *format, const struct tm *tm)
384 {
385 const size_t flen = strlen(format);
386 size_t buflen = 16; // An arbitrary starting size
387 char *buf = NULL;
388
389 do {
390 char *tmp;
391 size_t len;
392
393 tmp = realloc(buf, buflen * sizeof(char));
394 if (!tmp) goto on_error;
395 buf = tmp;
396
397 len = strftime(buf, buflen, format, tm);
398 // Check if we have the expected result and return it.
399 if ((len > 0 && len < buflen) || (len == 0 && flen == 0))
400 {
401 tmp = realloc(buf, ((len + 1) * sizeof(char)));
402 buf = tmp;
403 return buf;
404 }
405
406 /* Possibly buf overflowed - try again with a bigger buffer */
407 buflen <<= 1; // multiply buffer size by 2
408 } while (buflen < 128 * flen);
409
410 on_error:
411 free(buf);
412 return NULL;
413 }
414
415 EAPI Eina_Bool
eina_str_has_prefix(const char * str,const char * prefix)416 eina_str_has_prefix(const char *str, const char *prefix)
417 {
418 size_t str_len;
419 size_t prefix_len;
420
421 str_len = strlen(str);
422 prefix_len = eina_strlen_bounded(prefix, str_len);
423 if (prefix_len == (size_t)-1)
424 return EINA_FALSE;
425
426 return (strncmp(str, prefix, prefix_len) == 0);
427 }
428
429 EAPI Eina_Bool
eina_str_has_suffix(const char * str,const char * suffix)430 eina_str_has_suffix(const char *str, const char *suffix)
431 {
432 return eina_str_has_suffix_helper(str, suffix, strcmp);
433 }
434
435 EAPI Eina_Bool
eina_str_has_extension(const char * str,const char * ext)436 eina_str_has_extension(const char *str, const char *ext)
437 {
438 return eina_str_has_suffix_helper(str, ext, strcasecmp);
439 }
440
441 EAPI char **
eina_str_split_full(const char * str,const char * delim,int max_tokens,unsigned int * elements)442 eina_str_split_full(const char *str,
443 const char *delim,
444 int max_tokens,
445 unsigned int *elements)
446 {
447 return eina_str_split_full_helper(str, delim, max_tokens, elements);
448 }
449
450
451 EAPI char **
eina_str_split(const char * str,const char * delim,int max_tokens)452 eina_str_split(const char *str, const char *delim, int max_tokens)
453 {
454 return eina_str_split_full_helper(str, delim, max_tokens, NULL);
455 }
456
457 EAPI size_t
eina_str_join_len(char * dst,size_t size,char sep,const char * a,size_t a_len,const char * b,size_t b_len)458 eina_str_join_len(char *dst,
459 size_t size,
460 char sep,
461 const char *a,
462 size_t a_len,
463 const char *b,
464 size_t b_len)
465 {
466 size_t ret = a_len + b_len + 1;
467 size_t off;
468
469 if (size < 1)
470 return ret;
471
472 if (size <= a_len)
473 {
474 memcpy(dst, a, size - 1);
475 dst[size - 1] = '\0';
476 return ret;
477 }
478
479 memcpy(dst, a, a_len);
480 off = a_len;
481
482 if (size <= off + 1)
483 {
484 dst[size - 1] = '\0';
485 return ret;
486 }
487
488 dst[off] = sep;
489 off++;
490
491 if (size <= off + b_len + 1)
492 {
493 memcpy(dst + off, b, size - off - 1);
494 dst[size - 1] = '\0';
495 return ret;
496 }
497
498 memcpy(dst + off, b, b_len);
499 dst[off + b_len] = '\0';
500 return ret;
501 }
502
503 #ifdef HAVE_ICONV
504 EAPI char *
eina_str_convert(const char * enc_from,const char * enc_to,const char * text)505 eina_str_convert(const char *enc_from, const char *enc_to, const char *text)
506 {
507 iconv_t ic;
508 char *new_txt, *outp;
509 const char *inp;
510 size_t inb, outb, outlen, tob, outalloc;
511
512 if (!text)
513 return NULL;
514
515 ic = iconv_open(enc_to, enc_from);
516 if (ic == (iconv_t)(-1))
517 return NULL;
518
519 new_txt = malloc(64);
520 inb = strlen(text);
521 outb = 64;
522 inp = text;
523 outp = new_txt;
524 outalloc = 64;
525 outlen = 0;
526
527 for (;; )
528 {
529 size_t count;
530
531 tob = outb;
532 count = iconv(ic, (char **)&inp, &inb, &outp, &outb);
533 outlen += tob - outb;
534 if (count == (size_t)(-1))
535 {
536 if (errno == E2BIG)
537 {
538 new_txt = realloc(new_txt, outalloc + 64);
539 outp = new_txt + outlen;
540 outalloc += 64;
541 outb += 64;
542 }
543 else
544 {
545 if (new_txt)
546 free(new_txt);
547
548 new_txt = NULL;
549 break;
550 }
551 }
552
553 if (inb == 0)
554 {
555 if (outalloc == outlen)
556 new_txt = realloc(new_txt, outalloc + 1);
557
558 new_txt[outlen] = 0;
559 break;
560 }
561 }
562 iconv_close(ic);
563 return new_txt;
564 }
565 #else
566 EAPI char *
eina_str_convert(const char * enc_from EINA_UNUSED,const char * enc_to EINA_UNUSED,const char * text EINA_UNUSED)567 eina_str_convert(const char *enc_from EINA_UNUSED,
568 const char *enc_to EINA_UNUSED,
569 const char *text EINA_UNUSED)
570 {
571 return NULL;
572 }
573 #endif
574
575 #ifdef HAVE_ICONV
576 EAPI char *
eina_str_convert_len(const char * enc_from,const char * enc_to,const char * text,size_t len,size_t * retlen)577 eina_str_convert_len(const char *enc_from, const char *enc_to, const char *text, size_t len, size_t *retlen)
578 {
579 iconv_t ic;
580 char *new_txt, *outp;
581 const char *inp;
582 size_t inb, outb, outlen, tob, outalloc;
583
584 if (retlen) *retlen = 0;
585 if (!text) return NULL;
586
587 ic = iconv_open(enc_to, enc_from);
588 if (ic == (iconv_t)(-1))
589 return NULL;
590
591 new_txt = malloc(64);
592 inb = len;
593 outb = 64;
594 inp = text;
595 outp = new_txt;
596 outalloc = 64;
597 outlen = 0;
598
599 for (;; )
600 {
601 size_t count;
602
603 tob = outb;
604 count = iconv(ic, (char **)&inp, &inb, &outp, &outb);
605 outlen += tob - outb;
606 if (count == (size_t)(-1))
607 {
608 if (errno == E2BIG)
609 {
610 new_txt = realloc(new_txt, outalloc + 64);
611 outp = new_txt + outlen;
612 outalloc += 64;
613 outb += 64;
614 }
615 else
616 {
617 if (new_txt)
618 free(new_txt);
619
620 new_txt = NULL;
621 break;
622 }
623 }
624
625 if (inb == 0)
626 {
627 if (outalloc == outlen)
628 new_txt = realloc(new_txt, outalloc + 1);
629
630 new_txt[outlen] = 0;
631 break;
632 }
633 }
634 iconv_close(ic);
635 if (retlen) *retlen = outlen;
636 return new_txt;
637 }
638 #else
639 EAPI char *
eina_str_convert_len(const char * enc_from EINA_UNUSED,const char * enc_to EINA_UNUSED,const char * text EINA_UNUSED,size_t len EINA_UNUSED,size_t * retlen)640 eina_str_convert_len(const char *enc_from EINA_UNUSED, const char *enc_to EINA_UNUSED, const char *text EINA_UNUSED, size_t len EINA_UNUSED, size_t *retlen)
641 {
642 if (retlen) *retlen = 0;
643 return NULL;
644 }
645 #endif
646
647 EAPI char *
eina_str_escape(const char * str)648 eina_str_escape(const char *str)
649 {
650 char *s2, *d;
651 const char *s;
652
653 if (!str)
654 return NULL;
655
656 s2 = malloc((strlen(str) * 2) + 1);
657 if (!s2)
658 return NULL;
659
660 for (s = str, d = s2; *s != 0; s++, d++)
661 {
662 switch (*s)
663 {
664 case ' ':
665 case '\\':
666 case '\'':
667 case '\"':
668 {
669 *d = '\\';
670 d++;
671 *d = *s;
672 break;
673 }
674 case '\n':
675 {
676 *d = '\\'; d++;
677 *d = 'n';
678 break;
679 }
680 case '\t':
681 {
682 *d = '\\'; d++;
683 *d = 't';
684 break;
685 }
686 default:
687 {
688 *d = *s;
689 break;
690 }
691 }
692 }
693 *d = 0;
694 return s2;
695 }
696
697 EAPI void
eina_str_tolower(char ** str)698 eina_str_tolower(char **str)
699 {
700 char *p;
701 if ((!str) || (!(*str)))
702 return;
703
704 for (p = *str; (*p); p++)
705 *p = tolower((unsigned char )(*p));
706 }
707
708 EAPI void
eina_str_toupper(char ** str)709 eina_str_toupper(char **str)
710 {
711 char *p;
712 if ((!str) || (!(*str)))
713 return;
714
715 for (p = *str; (*p); p++)
716 *p = toupper((unsigned char)(*p));
717 }
718
719 EAPI unsigned char *
eina_memdup(unsigned char * mem,size_t size,Eina_Bool terminate)720 eina_memdup(unsigned char *mem, size_t size, Eina_Bool terminate)
721 {
722 unsigned char *ret;
723
724 if (!mem) return NULL;
725
726 terminate = !!terminate;
727 ret = malloc(size + terminate);
728 if (!ret) return NULL;
729
730 memcpy(ret, mem, size);
731 if (terminate)
732 ret[size] = 0;
733 return ret;
734 }
735