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