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