New test.
[mono.git] / eglib / src / gstr.c
1 /*
2  * gstr.c: String Utility Functions.
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@novell.com)
6  *   Aaron Bockover (abockover@novell.com)
7  *
8  * (C) 2006 Novell, Inc.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining
11  * a copy of this software and associated documentation files (the
12  * "Software"), to deal in the Software without restriction, including
13  * without limitation the rights to use, copy, modify, merge, publish,
14  * distribute, sublicense, and/or sell copies of the Software, and to
15  * permit persons to whom the Software is furnished to do so, subject to
16  * the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be
19  * included in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28  */
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <glib.h>
33
34 /* This is not a macro, because I dont want to put _GNU_SOURCE in the glib.h header */
35 gchar *
36 g_strndup (const gchar *str, gsize n)
37 {
38         return strndup (str, n);
39 }
40
41 void
42 g_strfreev (gchar **str_array)
43 {
44         gchar **orig = str_array;
45         if (str_array == NULL)
46                 return;
47         while (*str_array != NULL){
48                 g_free (*str_array);
49                 str_array++;
50         }
51         g_free (orig);
52 }
53
54 guint
55 g_strv_length(gchar **str_array)
56 {
57         gint length = 0;
58         g_return_val_if_fail(str_array != NULL, 0);
59         for(length = 0; str_array[length] != NULL; length++);
60         return length;
61 }
62
63 gboolean
64 g_str_has_suffix(const gchar *str, const gchar *suffix)
65 {
66         gint str_length;
67         gint suffix_length;
68         
69         g_return_val_if_fail(str != NULL, FALSE);
70         g_return_val_if_fail(suffix != NULL, FALSE);
71
72         str_length = strlen(str);
73         suffix_length = strlen(suffix);
74
75         return suffix_length <= str_length ?
76                 strncmp(str + str_length - suffix_length, suffix, suffix_length) == 0 :
77                 FALSE;
78 }
79
80 gboolean
81 g_str_has_prefix(const gchar *str, const gchar *prefix)
82 {
83         gint str_length;
84         gint prefix_length;
85         
86         g_return_val_if_fail(str != NULL, FALSE);
87         g_return_val_if_fail(prefix != NULL, FALSE);
88
89         str_length = strlen(str);
90         prefix_length = strlen(prefix);
91
92         return prefix_length <= str_length ?
93                 strncmp(str, prefix, prefix_length) == 0 :
94                 FALSE;
95 }
96
97 gchar *
98 g_strdup_vprintf (const gchar *format, va_list args)
99 {
100         int n;
101         char *ret;
102         
103         n = vasprintf (&ret, format, args);
104         if (n == -1)
105                 return NULL;
106
107         return ret;
108 }
109
110 gchar *
111 g_strdup_printf (const gchar *format, ...)
112 {
113         gchar *ret;
114         va_list args;
115         int n;
116
117         va_start (args, format);
118         n = vasprintf (&ret, format, args);
119         va_end (args);
120         if (n == -1)
121                 return NULL;
122
123         return ret;
124 }
125
126 const gchar *
127 g_strerror (gint errnum)
128 {
129         return strerror (errnum);
130 }
131
132 gchar *
133 g_strconcat (const gchar *first, ...)
134 {
135         g_return_val_if_fail (first != NULL, NULL);
136         va_list args;
137         int total = 0;
138         char *s, *ret;
139
140         total += strlen (first);
141         va_start (args, first);
142         for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){
143                 total += strlen (s);
144         }
145         va_end (args);
146         
147         ret = g_malloc (total + 1);
148         if (ret == NULL)
149                 return NULL;
150
151         ret [total] = 0;
152         strcpy (ret, first);
153         va_start (args, first);
154         for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){
155                 strcat (ret, s);
156         }
157         va_end (args);
158
159         return ret;
160 }
161
162 gchar ** 
163 g_strsplit (const gchar *string, const gchar *delimiter, gint max_tokens)
164 {
165         gchar *string_c;
166         gchar *strtok_save, **vector;
167         gchar *token, *token_c;
168         gint size = 1;
169         gint token_length;
170
171         g_return_val_if_fail(string != NULL, NULL);
172         g_return_val_if_fail(delimiter != NULL, NULL);
173         g_return_val_if_fail(delimiter[0] != 0, NULL);
174         
175         token_length = strlen(string);
176         string_c = (gchar *)g_malloc(token_length + 1);
177         memcpy(string_c, string, token_length);
178         string_c[token_length] = 0;
179
180         if (strncmp (string_c, delimiter, strlen (delimiter)) == 0){
181                 vector = (gchar **) g_malloc (2 * sizeof (vector));
182                 vector [0]  = g_strdup ("");
183                 size++;
184         } else
185                 vector = NULL;
186         token = (gchar *)strtok_r(string_c, delimiter, &strtok_save);
187
188         if (!(max_tokens > 0 && size >= max_tokens)){
189                 while(token != NULL) {
190                         token_length = strlen(token);
191                         token_c = (gchar *)g_malloc(token_length + 1);
192                         memcpy(token_c, token, token_length);
193                         token_c[token_length] = 0;
194                         
195                         vector = vector == NULL ? 
196                                 (gchar **)g_malloc(2 * sizeof(vector)) :
197                                 (gchar **)g_realloc(vector, (size + 1) * sizeof(vector));
198                         
199                         vector[size - 1] = token_c;     
200                         size++;
201                         
202                         if(max_tokens > 0 && size >= max_tokens) {
203                                 if(size > max_tokens) {
204                                         break;
205                                 }
206                                 
207                                 token = *strtok_save ? strtok_save : NULL;
208                         } else {
209                                 token = (gchar *)strtok_r(NULL, delimiter, &strtok_save);
210                         }
211                 }
212         }
213         
214         if (vector == NULL){
215                 vector = (gchar **) g_malloc (2 * sizeof (vector));
216                 vector [0] = NULL;
217         } else if (size > 0){
218                 vector[size - 1] = NULL;
219         }
220         
221         g_free(string_c);
222         string_c = NULL;
223
224         return vector;
225 }
226
227 gchar *
228 g_strreverse (gchar *str)
229 {
230         guint len, half;
231         gint i;
232         gchar c;
233
234         if (str == NULL)
235                 return NULL;
236
237         len = strlen (str);
238         half = len / 2;
239         len--;
240         for (i = 0; i < half; i++, len--) {
241                 c = str [i];
242                 str [i] = str [len];
243                 str [len] = c;
244         }
245         return str;
246 }
247
248 gchar *
249 g_strjoin (const gchar *separator, ...)
250 {
251         va_list args;
252         char *res, *s;
253         int len, slen;
254
255         if (separator != NULL)
256                 slen = strlen (separator);
257         else
258                 slen = 0;
259         len = 0;
260         va_start (args, separator);
261         for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){
262                 len += strlen (s);
263                 len += slen;
264         }
265         va_end (args);
266         if (len == 0)
267                 return g_strdup ("");
268         
269         /* Remove the last separator */
270         if (slen > 0 && len > 0)
271                 len -= slen;
272         len++;
273         res = g_malloc (len);
274         va_start (args, separator);
275         s = va_arg (args, char *);
276         strcpy (res, s);
277         for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){
278                 if (separator != NULL)
279                         strcat (res, separator);
280                 strcat (res, s);
281         }
282         va_end (args);
283
284         return res;
285 }
286
287 gchar *
288 g_strjoinv (const gchar *separator, gchar **str_array)
289 {
290         char *res;
291         int slen, len, i;
292         
293         if (separator != NULL)
294                 slen = strlen (separator);
295         else
296                 slen = 0;
297         
298         len = 0;
299         for (i = 0; str_array [i] != NULL; i++){
300                 len += strlen (str_array [i]);
301                 len += slen;
302         }
303         if (len == 0)
304                 return g_strdup ("");
305         if (slen > 0 && len > 0)
306                 len -= slen;
307         len++;
308         res = g_malloc (len);
309         strcpy (res, str_array [0]);
310         for (i = 1; str_array [i] != NULL; i++){
311                 if (separator != NULL)
312                         strcat (res, separator);
313                 strcat (res, str_array [i]);
314         }
315         return res;
316 }
317
318 gchar *
319 g_strchug (gchar *str)
320 {
321         gint len;
322         gchar *tmp;
323
324         if (str == NULL)
325                 return NULL;
326
327         tmp = str;
328         while (*tmp && isspace (*tmp)) tmp++;
329         if (str != tmp) {
330                 len = strlen (str) - (tmp - str - 1);
331                 memmove (str, tmp, len);
332         }
333         return str;
334 }
335
336 gchar *
337 g_strchomp (gchar *str)
338 {
339         gchar *tmp;
340
341         if (str == NULL)
342                 return NULL;
343
344         tmp = str + strlen (str) - 1;
345         while (*tmp && isspace (*tmp)) tmp--;
346         *(tmp + 1) = '\0';
347         return str;
348 }
349
350 gint
351 g_printf(gchar const *format, ...)
352 {
353         va_list args;
354         gint ret;
355
356         va_start(args, format);
357         ret = vprintf(format, args);
358         va_end(args);
359
360         return ret;
361 }
362
363 gint
364 g_fprintf(FILE *file, gchar const *format, ...)
365 {
366         va_list args;
367         gint ret;
368
369         va_start(args, format);
370         ret = vfprintf(file, format, args);
371         va_end(args);
372
373         return ret;
374 }
375
376 gint
377 g_sprintf(gchar *string, gchar const *format, ...)
378 {
379         va_list args;
380         gint ret;
381
382         va_start(args, format);
383         ret = vsprintf(string, format, args);
384         va_end(args);
385
386         return ret;
387 }
388
389 gint
390 g_snprintf(gchar *string, gulong n, gchar const *format, ...)
391 {
392         va_list args;
393         gint ret;
394         
395         va_start(args, format);
396         ret = vsnprintf(string, n, format, args);
397         va_end(args);
398
399         return ret;
400 }
401
402 static const char const hx [] = { '0', '1', '2', '3', '4', '5', '6', '7',
403                                   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
404
405 static gboolean
406 char_needs_encoding (char c)
407 {
408         if (((unsigned char)c) >= 0x80)
409                 return TRUE;
410         
411         if ((c >= '@' && c <= 'Z') ||
412             (c >= 'a' && c <= 'z') ||
413             (c >= '&' && c < 0x3b) ||
414             (c == '!') || (c == '$') || (c == '_') || (c == '=') || (c == '~'))
415                 return FALSE;
416         return TRUE;
417 }
418
419 gchar *
420 g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)
421 {
422         int n;
423         char *ret, *rp;
424         const char *p;
425         
426         g_return_val_if_fail (filename != NULL, NULL);
427
428         if (hostname != NULL)
429                 g_warning ("eglib: g_filename_to_uri: hostname not handled");
430
431         if (*filename != '/'){
432                 if (error != NULL)
433                         *error = g_error_new (NULL, 2, "Not an absolute filename");
434                 
435                 return NULL;
436         }
437         
438         n = strlen ("file://") + 1;
439         for (p = filename; *p; p++){
440                 if (char_needs_encoding (*p))
441                         n += 3;
442                 else
443                         n++;
444         }
445         ret = g_malloc (n);
446         strcpy (ret, "file://");
447         for (p = filename, rp = ret + strlen (ret); *p; p++){
448                 if (char_needs_encoding (*p)){
449                         *rp++ = '%';
450                         *rp++ = hx [((unsigned char)(*p)) >> 4];
451                         *rp++ = hx [((unsigned char)(*p)) & 0xf];
452                 } else
453                         *rp++ = *p;
454         }
455         *rp = 0;
456         return ret;
457 }
458
459 static int
460 decode (char p)
461 {
462         if (p >= '0' && p <= '9')
463                 return p - '0';
464         if (p >= 'A' && p <= 'F')
465                 return p - 'A';
466         if (p >= 'a' && p <= 'f')
467                 return p - 'a';
468         g_assert_not_reached ();
469         return 0;
470 }
471
472 gchar *
473 g_filename_from_uri (const gchar *uri, gchar **hostname, GError **error)
474 {
475         const char *p;
476         char *r, *result;
477         int flen = 0;
478         
479         g_return_val_if_fail (uri != NULL, NULL);
480
481         if (hostname != NULL)
482                 g_warning ("eglib: g_filename_from_uri: hostname not handled");
483
484         if (strncmp (uri, "file:///", 8) != 0){
485                 if (error != NULL)
486                         *error = g_error_new (NULL, 2, "URI does not start with the file: scheme");
487                 return NULL;
488         }
489
490         for (p = uri + 8; *p; p++){
491                 if (*p == '%'){
492                         if (p [1] && p [2] && isxdigit (p [1]) && isxdigit (p [2])){
493                                 p += 2;
494                         } else {
495                                 if (error != NULL)
496                                         *error = g_error_new (NULL, 2, "URI contains an invalid escape sequence");
497                                 return NULL;
498                         }
499                 } 
500                 flen++;
501         }
502         flen++;
503         
504         result = g_malloc (flen + 1);
505         *result = '/';
506         result [flen] = 0;
507
508         for (p = uri + 8, r = result + 1; *p; p++){
509                 if (*p == '%'){
510                         *r++ = (decode (p [1]) << 4) | decode (p [2]);
511                         p += 2;
512                 } else
513                         *r++ = *p;
514                 flen++;
515         }
516         return result;
517 }
518
519 void
520 g_strdown (gchar *string)
521 {
522         g_return_if_fail (string != NULL);
523
524         while (*string){
525                 *string = tolower (*string);
526                 string++;
527         }
528 }
529
530 gchar *
531 g_ascii_strdown (const gchar *str, gssize len)
532 {
533         char *ret;
534         int i;
535         
536         g_return_val_if_fail  (str != NULL, NULL);
537
538         if (len == -1)
539                 len = strlen (str);
540         
541         ret = g_malloc (len + 1);
542         for (i = 0; i < len; i++){
543                 guchar c = (guchar) str [i];
544                 if (c >= 'A' && c <= 'Z')
545                         c += 'a' - 'A';
546                 ret [i] = c;
547         }
548         ret [i] = 0;
549         
550         return ret;
551 }
552
553 gint
554 g_ascii_strncasecmp (const gchar *s1, const gchar *s2, gsize n)
555 {
556         int i;
557         
558         g_return_val_if_fail (s1 != NULL, 0);
559         g_return_val_if_fail (s2 != NULL, 0);
560
561         for (i = 0; i < n; i++){
562                 gchar c1 = *s1++;
563                 gchar c2 = *s2++;
564                 
565                 if (c1 == c2)
566                         continue;
567                 
568                 if (c1 == 0)
569                         return -1;
570                 if (c2 == 0)
571                         return 1;
572                 return c1-c2;
573         }
574         return 0;
575 }
576
577 gchar *
578 g_strdelimit (gchar *string, const gchar *delimiters, gchar new_delimiter)
579 {
580         gchar *ptr;
581
582         g_return_val_if_fail (string != NULL, NULL);
583
584         if (delimiters == NULL)
585                 delimiters = G_STR_DELIMITERS;
586
587         for (ptr = string; *ptr; ptr++) {
588                 if (strchr (delimiters, *ptr))
589                         *ptr = new_delimiter;
590         }
591         
592         return string;
593 }
594
595 #ifndef HAVE_STRLCPY
596 gsize 
597 g_strlcpy (gchar *dest, const gchar *src, gsize dest_size)
598 {
599         gchar *d;
600         const gchar *s;
601         gchar c;
602         gsize len;
603         
604         g_return_val_if_fail (src != NULL, 0);
605         g_return_val_if_fail (dest != NULL, 0);
606
607         len = dest_size;
608         if (len == 0)
609                 return 0;
610
611         s = src;
612         d = dest;
613         while (--len) {
614                 c = *s++;
615                 *d++ = c;
616                 if (c == '\0')
617                         return (dest_size - len - 1);
618         }
619
620         /* len is 0 i we get here */
621         *d = '\0';
622         /* we need to return the length of src here */
623         while (*s++) ; /* instead of a plain strlen, we use 's' */
624         return s - src - 1;
625 }
626 #endif
627
628 static const gchar escaped_dflt [256] = {
629         1, 1, 1, 1, 1, 1, 1, 1, 'b', 't', 'n', 1, 'f', 'r', 1, 1,
630         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
631         0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
632         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
633         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
634         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0,
635         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
636         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
637         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
638         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
639         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
640         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
641         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
642         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
643         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
644         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
645 };
646
647 gchar *
648 g_strescape (const gchar *source, const gchar *exceptions)
649 {
650         gchar escaped [256];
651         const gchar *ptr;
652         gchar c;
653         int op;
654         gchar *result;
655         gchar *res_ptr;
656
657         g_return_val_if_fail (source != NULL, NULL);
658
659         memcpy (escaped, escaped_dflt, 256);
660         if (exceptions != NULL) {
661                 for (ptr = exceptions; *ptr; ptr++)
662                         escaped [(int) *ptr] = 0;
663         }
664         result = g_malloc (strlen (source) * 4 + 1); /* Worst case: everything octal. */
665         res_ptr = result;
666         for (ptr = source; *ptr; ptr++) {
667                 c = *ptr;
668                 op = escaped [(int) c];
669                 if (op == 0) {
670                         *res_ptr++ = c;
671                 } else {
672                         *res_ptr++ = '\\';
673                         if (op != 1) {
674                                 *res_ptr++ = op;
675                         } else {
676                                 *res_ptr++ = '0' + ((c >> 6) & 3);
677                                 *res_ptr++ = '0' + ((c >> 3) & 7);
678                                 *res_ptr++ = '0' + (c & 7);
679                         }
680                 }
681         }
682         *res_ptr = '\0';
683         return result;
684 }
685
686 gchar *
687 g_strdup (const gchar *str)
688 {
689         if (str == NULL)
690                 return NULL;
691
692         return strdup (str);
693 }
694
695 gint
696 g_ascii_xdigit_value (gchar c)
697 {
698         return ((isxdigit (c) == 0) ? -1 :
699                 ((c >= '0' && c <= '9') ? (c - '0') :
700                  ((c >= 'a' && c <= 'f') ? (c - 'a' + 10) :
701                   (c - 'A' + 10))));
702 }
703
704