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