add a check for stupids
[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, **str_array;
75    const char *src;
76    size_t len, dlen;
77    unsigned int tokens;
78
79    EINA_SAFETY_ON_TRUE_RETURN_VAL(max_tokens < 1, NULL);
80    dlen = strlen(delim);
81    if (dlen == 0)
82      {
83         if (elements)
84            *elements = 0;
85
86         return NULL;
87      }
88
89    tokens = 0;
90    src = str;
91    /* count tokens and check strlen(str) */
92    while (*src != '\0')
93      {
94         const char *d = delim, *d_end = d + dlen;
95         const char *tmp = src;
96         for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
97           {
98              if (EINA_LIKELY(*d != *tmp))
99                 break;
100           }
101         if (EINA_UNLIKELY(d == d_end))
102           {
103              src = tmp;
104              tokens++;
105           }
106         else
107            src++;
108      }
109    len = src - str;
110
111    if ((max_tokens > 0) && (tokens > (unsigned int)max_tokens))
112       tokens = max_tokens;
113
114    str_array = malloc(sizeof(char *) * (tokens + 2));
115    if (!str_array)
116      {
117         if (elements)
118            *elements = 0;
119
120         return NULL;
121      }
122
123    s = malloc(len + 1);
124    if (!s)
125      {
126         free(str_array);
127         if (elements)
128            *elements = 0;
129
130         return NULL;
131      }
132
133    /* copy tokens and string */
134    tokens = 0;
135    str_array[0] = s;
136    src = str;
137    while (*src != '\0')
138      {
139         const char *d = delim, *d_end = d + dlen;
140         const char *tmp = src;
141         for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
142           {
143              if (EINA_LIKELY(*d != *tmp))
144                 break;
145           }
146         if (EINA_UNLIKELY(d == d_end))
147           {
148              src = tmp;
149              *s = '\0';
150              s += dlen;
151              tokens++;
152              str_array[tokens] = s;
153           }
154         else
155           {
156              *s = *src;
157              s++;
158              src++;
159           }
160      }
161    *s = '\0';
162    str_array[tokens + 1] = NULL;
163    if (elements)
164       *elements = (tokens + 1);
165
166    return str_array;
167 }
168
169 /**
170  * @endcond
171  */
172
173 /*============================================================================*
174 *                                 Global                                     *
175 *============================================================================*/
176
177 /*============================================================================*
178 *                                   API                                      *
179 *============================================================================*/
180
181 EAPI size_t
182 eina_strlcpy(char *dst, const char *src, size_t siz)
183 {
184 #ifdef HAVE_STRLCPY
185    return strlcpy(dst, src, siz);
186 #else
187    char *d = dst;
188    const char *s = src;
189    size_t n = siz;
190
191    /* Copy as many bytes as will fit */
192    if (n != 0)
193       while (--n != 0)
194         {
195            if ((*d++ = *s++) == '\0')
196               break;
197         }
198
199    /* Not enough room in dst, add NUL and traverse rest of src */
200    if (n == 0)
201      {
202         if (siz != 0)
203            *d = '\0';  /* NUL-terminate dst */
204
205         while (*s++)
206            ;
207      }
208
209    return(s - src - 1); /* count does not include NUL */
210 #endif
211 }
212
213 EAPI size_t
214 eina_strlcat(char *dst, const char *src, size_t siz)
215 {
216    char *d = dst;
217    const char *s = src;
218    size_t n = siz;
219    size_t dlen;
220
221    /* Find the end of dst and adjust bytes left but don't go past end */
222    while (n-- != 0 && *d != '\0')
223       d++;
224    dlen = d - dst;
225    n = siz - dlen;
226
227    if (n == 0)
228       return(dlen + strlen(s));
229
230    while (*s != '\0') {
231         if (n != 1)
232           {
233              *d++ = *s;
234              n--;
235           }
236
237         s++;
238      }
239    *d = '\0';
240
241    return(dlen + (s - src)); /* count does not include NUL */
242 }
243
244 EAPI Eina_Bool
245 eina_str_has_prefix(const char *str, const char *prefix)
246 {
247    size_t str_len;
248    size_t prefix_len;
249
250    str_len = strlen(str);
251    prefix_len = eina_strlen_bounded(prefix, str_len);
252    if (prefix_len == (size_t)-1)
253       return EINA_FALSE;
254
255    return (strncmp(str, prefix, prefix_len) == 0);
256 }
257
258 EAPI Eina_Bool
259 eina_str_has_suffix(const char *str, const char *suffix)
260 {
261    return eina_str_has_suffix_helper(str, suffix, strcmp);
262 }
263
264 EAPI Eina_Bool
265 eina_str_has_extension(const char *str, const char *ext)
266 {
267    return eina_str_has_suffix_helper(str, ext, strcasecmp);
268 }
269
270 EAPI char **
271 eina_str_split_full(const char *str,
272                     const char *delim,
273                     int max_tokens,
274                     unsigned int *elements)
275 {
276    return eina_str_split_full_helper(str, delim, max_tokens, elements);
277 }
278
279
280 EAPI char **
281 eina_str_split(const char *str, const char *delim, int max_tokens)
282 {
283    return eina_str_split_full_helper(str, delim, max_tokens, NULL);
284 }
285
286 EAPI size_t
287 eina_str_join_len(char *dst,
288                   size_t size,
289                   char sep,
290                   const char *a,
291                   size_t a_len,
292                   const char *b,
293                   size_t b_len)
294 {
295    size_t ret = a_len + b_len + 1;
296    size_t off;
297
298    if (size < 1)
299       return ret;
300
301    if (size <= a_len)
302      {
303         memcpy(dst, a, size - 1);
304         dst[size - 1] = '\0';
305         return ret;
306      }
307
308         memcpy(dst, a, a_len);
309    off = a_len;
310
311    if (size <= off + 1)
312      {
313         dst[size - 1] = '\0';
314         return ret;
315      }
316
317    dst[off] = sep;
318    off++;
319
320    if (size <= off + b_len + 1)
321      {
322         memcpy(dst + off, b, size - off - 1);
323         dst[size - 1] = '\0';
324         return ret;
325      }
326
327         memcpy(dst + off, b, b_len);
328    dst[off + b_len] = '\0';
329    return ret;
330 }
331
332 #ifdef HAVE_ICONV
333 EAPI char *
334 eina_str_convert(const char *enc_from, const char *enc_to, const char *text)
335 {
336    iconv_t ic;
337    char *new_txt, *inp, *outp;
338    size_t inb, outb, outlen, tob, outalloc;
339
340    if (!text)
341       return NULL;
342
343    ic = iconv_open(enc_to, enc_from);
344    if (ic == (iconv_t)(-1))
345       return NULL;
346
347    new_txt = malloc(64);
348    inb = strlen(text);
349    outb = 64;
350    inp = (char *)text;
351    outp = new_txt;
352    outalloc = 64;
353    outlen = 0;
354
355    for (;; )
356      {
357         size_t count;
358
359         tob = outb;
360         count = iconv(ic, &inp, &inb, &outp, &outb);
361         outlen += tob - outb;
362         if (count == (size_t)(-1))
363           {
364              if (errno == E2BIG)
365                {
366                   new_txt = realloc(new_txt, outalloc + 64);
367                   outp = new_txt + outlen;
368                   outalloc += 64;
369                   outb += 64;
370                }
371              else if (errno == EILSEQ)
372                {
373                   if (new_txt)
374                      free(new_txt);
375
376                   new_txt = NULL;
377                   break;
378                }
379              else if (errno == EINVAL)
380                {
381                   if (new_txt)
382                      free(new_txt);
383
384                   new_txt = NULL;
385                   break;
386                }
387              else
388                {
389                   if (new_txt)
390                      free(new_txt);
391
392                   new_txt = NULL;
393                   break;
394                }
395           }
396
397         if (inb == 0)
398           {
399              if (outalloc == outlen)
400                 new_txt = realloc(new_txt, outalloc + 1);
401
402              new_txt[outlen] = 0;
403              break;
404           }
405      }
406    iconv_close(ic);
407    return new_txt;
408 }
409 #else
410 EAPI char *
411 eina_str_convert(const char *enc_from __UNUSED__,
412                  const char *enc_to __UNUSED__,
413                  const char *text __UNUSED__)
414 {
415    return NULL;
416 }
417 #endif
418
419 EAPI char *
420 eina_str_escape(const char *str)
421 {
422    char *s2, *d;
423    const char *s;
424
425    s2 = malloc((strlen(str) * 2) + 1);
426    if (!s2)
427       return NULL;
428
429    for (s = str, d = s2; *s != 0; s++, d++)
430      {
431         if ((*s == ' ') || (*s == '\\') || (*s == '\''))
432           {
433              *d = '\\';
434              d++;
435           }
436
437         *d = *s;
438      }
439    *d = 0;
440    return s2;
441 }
442
443 EAPI void
444 eina_str_tolower(char **str)
445 {
446    char *p;
447    if ((!str) || (!(*str)))
448       return;
449
450    for (p = *str; (*p); p++)
451       *p = tolower((unsigned char )(*p));
452 }
453
454 EAPI void
455 eina_str_toupper(char **str)
456 {
457    char *p;
458    if ((!str) || (!(*str)))
459       return;
460
461    for (p = *str; (*p); p++)
462       *p = toupper((unsigned char)(*p));
463 }