just spent waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay too long working on eina_str_s...
[framework/uifw/eina.git] / src / lib / eina_str.c
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_ICONV
32 # include <errno.h>
33 # include <iconv.h>
34 #endif
35
36 #include "eina_private.h"
37 #include "eina_str.h"
38
39 /*============================================================================*
40 *                                  Local                                     *
41 *============================================================================*/
42
43 /**
44  * @cond LOCAL
45  */
46
47 /*
48  * Internal helper function used by eina_str_has_suffix() and
49  * eina_str_has_extension()
50  */
51 static inline Eina_Bool
52 eina_str_has_suffix_helper(const char *str,
53                            const char *suffix,
54                            int (*cmp)(const char *, const char *))
55 {
56    size_t str_len;
57    size_t suffix_len;
58
59    if ((!str) || (!suffix)) return EINA_FALSE;
60    str_len = strlen(str);
61    suffix_len = eina_strlen_bounded(suffix, str_len);
62    if (suffix_len == (size_t)-1)
63       return EINA_FALSE;
64
65    return cmp(str + str_len - suffix_len, suffix) == 0;
66 }
67
68 static inline char **
69 eina_str_split_full_helper(const char *str,
70                            const char *delim,
71                            int max_tokens,
72                            unsigned int *elements)
73 {
74    char *s, *pos, **str_array;
75    const char *src;
76    size_t len, dlen;
77    unsigned int tokens = 0, x;
78    const char *idx[256] = {NULL};
79
80    if (max_tokens < 0) max_tokens = 0;
81    if (max_tokens == 1)
82      {
83         str_array = malloc(sizeof(char *) * 2);
84         if (!str_array)
85           {
86              if (elements)
87                 *elements = 0;
88
89              return NULL;
90           }
91
92         s = strdup(str);
93         if (!s)
94           {
95              free(str_array);
96              if (elements)
97                 *elements = 0;
98
99              return NULL;
100           }
101         if (elements)
102           *elements = 1;
103         str_array[0] = s;
104         return str_array;
105      }
106    dlen = strlen(delim);
107    if (dlen == 0)
108      {
109         if (elements)
110            *elements = 0;
111
112         return NULL;
113      }
114
115    src = str;
116    /* count tokens and check strlen(str) */
117    while (*src != '\0')
118      {
119         const char *d = delim, *d_end = d + dlen;
120         const char *tmp = src;
121         for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
122           {
123              if (EINA_LIKELY(*d != *tmp))
124                 break;
125           }
126         if (EINA_UNLIKELY(d == d_end))
127           {
128              src = tmp;
129              if (tokens < (sizeof(idx) / sizeof(idx[0])))
130                {
131                   idx[tokens] = tmp;
132                   //printf("token %d='%s'\n", tokens + 1, idx[tokens]);
133                }
134              tokens++;
135              if (tokens && (tokens == (unsigned int)max_tokens)) break;
136           }
137         else
138            src++;
139      }
140    len = src - str + strlen(src);
141
142    str_array = malloc(sizeof(char *) * (tokens + 2));
143    if (!str_array)
144      {
145         if (elements)
146            *elements = 0;
147
148         return NULL;
149      }
150
151    if (!tokens)
152      {
153         s = strdup(str);
154         if (!s)
155           {
156              free(str_array);
157              if (elements)
158                 *elements = 0;
159
160              return NULL;
161           }
162         str_array[0] = s;
163         str_array[1] = NULL;
164         if (elements)
165           *elements = 2;
166         return str_array;
167      }
168
169    s = malloc(len + 1);
170    if (!s)
171      {
172         free(str_array);
173         if (elements)
174            *elements = 0;
175
176         return NULL;
177      }
178
179    str_array[0] = s;
180
181    if (len == tokens * dlen)
182      {
183         /* someone's having a laugh somewhere */
184         memset(s, 0, len + 1);
185         for (x = 1; x < tokens + 1; x++)
186           str_array[x] = s + x;
187         str_array[x] = NULL;
188         if (elements)
189           *elements = x + 1;
190         return str_array;
191      }
192    /* copy tokens and string */
193    if (idx[0] - str - dlen > len)
194      {
195         /* FIXME: don't think this can happen but putting this here just in case */
196         abort();
197      }
198    pos = s;
199    for (x = 0; x < MIN(tokens, (sizeof(idx) / sizeof(idx[0]))); x++)
200      {
201         if (x + 1 < (sizeof(idx) / sizeof(idx[0])))
202           {
203              /* first one is special */
204              if (!x)
205                {
206                   eina_strlcpy(pos, str, idx[x] - str - dlen + 1);
207                   str_array[x] = pos;
208                   //printf("str_array[%d] = '%s'\n", x, str_array[x]);
209                   pos += idx[x] - str - dlen + 1;
210                   if ((tokens == 1) && (idx[0]))
211                     {
212                        eina_strlcpy(pos, idx[x], len + 1 - (pos - s));
213                        x++, tokens++;
214                        str_array[x] = pos;
215                     }
216                }
217              /* more tokens */
218              else if (idx[x + 1])
219                {
220                   eina_strlcpy(pos, idx[x - 1], idx[x] - idx[x - 1] - dlen + 1);
221                   str_array[x] = pos;
222                   //printf("str_array[%d] = '%s'\n", x, str_array[x]);
223                   pos += idx[x] - idx[x - 1] - dlen + 1;
224                }
225              /* last token */
226              else
227                {
228                   if (max_tokens && ((unsigned int)max_tokens < tokens + 1))
229                     eina_strlcpy(pos, idx[x - 1], len + 1 - (pos - s));
230                   else
231                     {
232                        //printf("diff: %d\n", len + 1 - (pos - s));
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                        x++, tokens++;
238                        eina_strlcpy(pos, idx[x - 1], len + 1 - (pos - s));
239                     }
240                   str_array[x] = pos;
241                   //printf("str_array[%d] = '%s'\n", x, str_array[x]);
242                }
243           }
244         /* no more tokens saved after this one */
245         else
246           {
247              eina_strlcpy(pos, idx[x - 1], idx[x] - idx[x - 1] - dlen + 1);
248              str_array[x] = pos;
249              //printf("str_array[%d] = '%s'\n", x, str_array[x]);
250              pos += idx[x] - idx[x - 1] - dlen + 1;
251              src = idx[x];
252              x++, tokens++;
253              str_array[x] = s = pos;
254              break;
255           }
256      }
257    if ((x != tokens) && ((!max_tokens) || (x < tokens)))
258      {
259         while (*src != '\0')
260           {
261              const char *d = delim, *d_end = d + dlen;
262              const char *tmp = src;
263              for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
264                {
265                   if (EINA_LIKELY(*d != *tmp))
266                      break;
267                }
268              if (((!max_tokens) || (((tokens == (unsigned int)max_tokens) || x < tokens - 2))) && (EINA_UNLIKELY(d == d_end)))
269                {
270                   src = tmp;
271                   *s = '\0';
272                   s++, x++;
273                   //printf("str_array[%d] = '%s'\n", x, str_array[x - 1]);
274                   str_array[x] = s;
275                }
276              else
277                {
278                   *s = *src;
279                   s++, src++;
280                }
281           }
282         *s = 0;
283      }
284    str_array[tokens] = NULL;
285    if (elements)
286      {
287         *elements = tokens;
288         if ((!max_tokens) || (tokens == (unsigned int)max_tokens))
289           (*elements)++;
290      }
291
292    return str_array;
293 }
294
295 /**
296  * @endcond
297  */
298
299 /*============================================================================*
300 *                                 Global                                     *
301 *============================================================================*/
302
303 /*============================================================================*
304 *                                   API                                      *
305 *============================================================================*/
306
307 EAPI size_t
308 eina_strlcpy(char *dst, const char *src, size_t siz)
309 {
310 #ifdef HAVE_STRLCPY
311    return strlcpy(dst, src, siz);
312 #else
313    char *d = dst;
314    const char *s = src;
315    size_t n = siz;
316
317    /* Copy as many bytes as will fit */
318    if (n != 0)
319       while (--n != 0)
320         {
321            if ((*d++ = *s++) == '\0')
322               break;
323         }
324
325    /* Not enough room in dst, add NUL and traverse rest of src */
326    if (n == 0)
327      {
328         if (siz != 0)
329            *d = '\0';  /* NUL-terminate dst */
330
331         while (*s++)
332            ;
333      }
334
335    return(s - src - 1); /* count does not include NUL */
336 #endif
337 }
338
339 EAPI size_t
340 eina_strlcat(char *dst, const char *src, size_t siz)
341 {
342    char *d = dst;
343    const char *s = src;
344    size_t n = siz;
345    size_t dlen;
346
347    /* Find the end of dst and adjust bytes left but don't go past end */
348    while (n-- != 0 && *d != '\0')
349       d++;
350    dlen = d - dst;
351    n = siz - dlen;
352
353    if (n == 0)
354       return(dlen + strlen(s));
355
356    while (*s != '\0') {
357         if (n != 1)
358           {
359              *d++ = *s;
360              n--;
361           }
362
363         s++;
364      }
365    *d = '\0';
366
367    return(dlen + (s - src)); /* count does not include NUL */
368 }
369
370 EAPI Eina_Bool
371 eina_str_has_prefix(const char *str, const char *prefix)
372 {
373    size_t str_len;
374    size_t prefix_len;
375
376    str_len = strlen(str);
377    prefix_len = eina_strlen_bounded(prefix, str_len);
378    if (prefix_len == (size_t)-1)
379       return EINA_FALSE;
380
381    return (strncmp(str, prefix, prefix_len) == 0);
382 }
383
384 EAPI Eina_Bool
385 eina_str_has_suffix(const char *str, const char *suffix)
386 {
387    return eina_str_has_suffix_helper(str, suffix, strcmp);
388 }
389
390 EAPI Eina_Bool
391 eina_str_has_extension(const char *str, const char *ext)
392 {
393    return eina_str_has_suffix_helper(str, ext, strcasecmp);
394 }
395
396 EAPI char **
397 eina_str_split_full(const char *str,
398                     const char *delim,
399                     int max_tokens,
400                     unsigned int *elements)
401 {
402    return eina_str_split_full_helper(str, delim, max_tokens, elements);
403 }
404
405
406 EAPI char **
407 eina_str_split(const char *str, const char *delim, int max_tokens)
408 {
409    return eina_str_split_full_helper(str, delim, max_tokens, NULL);
410 }
411
412 EAPI size_t
413 eina_str_join_len(char *dst,
414                   size_t size,
415                   char sep,
416                   const char *a,
417                   size_t a_len,
418                   const char *b,
419                   size_t b_len)
420 {
421    size_t ret = a_len + b_len + 1;
422    size_t off;
423
424    if (size < 1)
425       return ret;
426
427    if (size <= a_len)
428      {
429         memcpy(dst, a, size - 1);
430         dst[size - 1] = '\0';
431         return ret;
432      }
433
434         memcpy(dst, a, a_len);
435    off = a_len;
436
437    if (size <= off + 1)
438      {
439         dst[size - 1] = '\0';
440         return ret;
441      }
442
443    dst[off] = sep;
444    off++;
445
446    if (size <= off + b_len + 1)
447      {
448         memcpy(dst + off, b, size - off - 1);
449         dst[size - 1] = '\0';
450         return ret;
451      }
452
453         memcpy(dst + off, b, b_len);
454    dst[off + b_len] = '\0';
455    return ret;
456 }
457
458 #ifdef HAVE_ICONV
459 EAPI char *
460 eina_str_convert(const char *enc_from, const char *enc_to, const char *text)
461 {
462    iconv_t ic;
463    char *new_txt, *inp, *outp;
464    size_t inb, outb, outlen, tob, outalloc;
465
466    if (!text)
467       return NULL;
468
469    ic = iconv_open(enc_to, enc_from);
470    if (ic == (iconv_t)(-1))
471       return NULL;
472
473    new_txt = malloc(64);
474    inb = strlen(text);
475    outb = 64;
476    inp = (char *)text;
477    outp = new_txt;
478    outalloc = 64;
479    outlen = 0;
480
481    for (;; )
482      {
483         size_t count;
484
485         tob = outb;
486         count = iconv(ic, &inp, &inb, &outp, &outb);
487         outlen += tob - outb;
488         if (count == (size_t)(-1))
489           {
490              if (errno == E2BIG)
491                {
492                   new_txt = realloc(new_txt, outalloc + 64);
493                   outp = new_txt + outlen;
494                   outalloc += 64;
495                   outb += 64;
496                }
497              else if (errno == EILSEQ)
498                {
499                   if (new_txt)
500                      free(new_txt);
501
502                   new_txt = NULL;
503                   break;
504                }
505              else if (errno == EINVAL)
506                {
507                   if (new_txt)
508                      free(new_txt);
509
510                   new_txt = NULL;
511                   break;
512                }
513              else
514                {
515                   if (new_txt)
516                      free(new_txt);
517
518                   new_txt = NULL;
519                   break;
520                }
521           }
522
523         if (inb == 0)
524           {
525              if (outalloc == outlen)
526                 new_txt = realloc(new_txt, outalloc + 1);
527
528              new_txt[outlen] = 0;
529              break;
530           }
531      }
532    iconv_close(ic);
533    return new_txt;
534 }
535 #else
536 EAPI char *
537 eina_str_convert(const char *enc_from __UNUSED__,
538                  const char *enc_to __UNUSED__,
539                  const char *text __UNUSED__)
540 {
541    return NULL;
542 }
543 #endif
544
545 EAPI char *
546 eina_str_escape(const char *str)
547 {
548    char *s2, *d;
549    const char *s;
550
551    s2 = malloc((strlen(str) * 2) + 1);
552    if (!s2)
553       return NULL;
554
555    for (s = str, d = s2; *s != 0; s++, d++)
556      {
557         if ((*s == ' ') || (*s == '\\') || (*s == '\''))
558           {
559              *d = '\\';
560              d++;
561           }
562
563         *d = *s;
564      }
565    *d = 0;
566    return s2;
567 }
568
569 EAPI void
570 eina_str_tolower(char **str)
571 {
572    char *p;
573    if ((!str) || (!(*str)))
574       return;
575
576    for (p = *str; (*p); p++)
577       *p = tolower((unsigned char )(*p));
578 }
579
580 EAPI void
581 eina_str_toupper(char **str)
582 {
583    char *p;
584    if ((!str) || (!(*str)))
585       return;
586
587    for (p = *str; (*p); p++)
588       *p = toupper((unsigned char)(*p));
589 }