[wasm] Fix eglib issues with WASM.
[mono.git] / mono / eglib / 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 #ifndef G_OS_WIN32
36 #include <pthread.h>
37 #endif
38
39 #include <errno.h>
40
41 /*
42  *  Linux knows two different versions of strerror_r () that can only be distinguished
43  *  by using feature test macros.  Please check the man pages for more details.
44  */
45 #if defined (_POSIX_C_SOURCE) && defined (_GNU_SOURCE)
46 #if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE
47 #define USE_STRERROR_R_XSI
48 #endif
49 #endif
50
51 /* 
52  * g_strndup and g_vasprintf need to allocate memory with g_malloc if 
53  * ENABLE_OVERRIDABLE_ALLOCATORS is defined so that it can be safely freed with g_free 
54  * rather than free.
55  */
56
57 /* This is not a macro, because I dont want to put _GNU_SOURCE in the glib.h header */
58 gchar *
59 g_strndup (const gchar *str, gsize n)
60 {
61 #if defined (HAVE_STRNDUP) && !defined (ENABLE_OVERRIDABLE_ALLOCATORS)
62         return strndup (str, n);
63 #else
64         if (str) {
65                 char *retval = g_malloc(n+1);
66                 if (retval) {
67                         strncpy(retval, str, n)[n] = 0;
68                 }
69                 return retval;
70         }
71         return NULL;
72 #endif
73 }
74
75 gint g_vasprintf (gchar **ret, const gchar *fmt, va_list ap)
76 {
77 #if defined (HAVE_VASPRINTF) && !defined (ENABLE_OVERRIDABLE_ALLOCATORS)
78   return vasprintf (ret, fmt, ap);
79 #else
80         char *buf;
81         int len;
82         size_t buflen;
83         va_list ap2;
84         
85 #if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
86         ap2 = ap;
87         len = _vscprintf(fmt, ap2); // NOTE MS specific extension ( :-( )
88 #else
89         va_copy(ap2, ap);
90         len = vsnprintf(NULL, 0, fmt, ap2);
91 #endif
92
93         if (len >= 0 && (buf = g_malloc ((buflen = (size_t) (len + 1)))) != NULL) {
94                 len = vsnprintf(buf, buflen, fmt, ap);
95                 *ret = buf;
96         } else {
97                 *ret = NULL;
98                 len = -1;
99         }
100
101         va_end(ap2);
102         return len;
103 #endif
104 }
105
106 void
107 g_strfreev (gchar **str_array)
108 {
109         gchar **orig = str_array;
110         if (str_array == NULL)
111                 return;
112         while (*str_array != NULL){
113                 g_free (*str_array);
114                 str_array++;
115         }
116         g_free (orig);
117 }
118
119 gchar **
120 g_strdupv (gchar **str_array)
121 {
122         guint length;
123         gchar **ret;
124         guint i;
125
126         if (!str_array)
127                 return NULL;
128
129         length = g_strv_length(str_array);
130         ret = g_new0(gchar *, length + 1);
131         for (i = 0; str_array[i]; i++) {
132                 ret[i] = g_strdup(str_array[i]);
133         }
134         ret[length] = NULL;
135         return ret;
136 }
137
138 guint
139 g_strv_length(gchar **str_array)
140 {
141         gint length = 0;
142         g_return_val_if_fail(str_array != NULL, 0);
143         for(length = 0; str_array[length] != NULL; length++);
144         return length;
145 }
146
147 gboolean
148 g_str_has_suffix(const gchar *str, const gchar *suffix)
149 {
150         size_t str_length;
151         size_t suffix_length;
152         
153         g_return_val_if_fail(str != NULL, FALSE);
154         g_return_val_if_fail(suffix != NULL, FALSE);
155
156         str_length = strlen(str);
157         suffix_length = strlen(suffix);
158
159         return suffix_length <= str_length ?
160                 strncmp(str + str_length - suffix_length, suffix, suffix_length) == 0 :
161                 FALSE;
162 }
163
164 gboolean
165 g_str_has_prefix(const gchar *str, const gchar *prefix)
166 {
167         size_t str_length;
168         size_t prefix_length;
169         
170         g_return_val_if_fail(str != NULL, FALSE);
171         g_return_val_if_fail(prefix != NULL, FALSE);
172
173         str_length = strlen(str);
174         prefix_length = strlen(prefix);
175
176         return prefix_length <= str_length ?
177                 strncmp(str, prefix, prefix_length) == 0 :
178                 FALSE;
179 }
180
181 gchar *
182 g_strdup_vprintf (const gchar *format, va_list args)
183 {
184         int n;
185         char *ret;
186         
187         n = g_vasprintf (&ret, format, args);
188         if (n == -1)
189                 return NULL;
190
191         return ret;
192 }
193
194 gchar *
195 g_strdup_printf (const gchar *format, ...)
196 {
197         gchar *ret;
198         va_list args;
199         int n;
200
201         va_start (args, format);
202         n = g_vasprintf (&ret, format, args);
203         va_end (args);
204         if (n == -1)
205                 return NULL;
206
207         return ret;
208 }
209
210
211 /*
212 Max error number we support. It's empirically found by looking at our target OS.
213
214 Last this was checked was June-2017.
215
216 Apple is at 106.
217 Android is at 133.
218 */
219 #define MONO_ERRNO_MAX 200
220 #define str(s) #s
221
222 #ifndef G_OS_WIN32
223 static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
224 #endif
225
226 static char *error_messages [MONO_ERRNO_MAX];
227
228 const gchar *
229 g_strerror (gint errnum)
230 {
231         if (errnum < 0)
232                 errnum = -errnum;
233         if (errnum >= MONO_ERRNO_MAX)
234                 return ("Error number higher than " str (MONO_ERRNO_MAX));
235
236         if (!error_messages [errnum]) {
237 #ifndef G_OS_WIN32
238                 pthread_mutex_lock (&strerror_lock);
239 #endif
240
241 #ifdef HAVE_STRERROR_R
242                 char tmp_buff [128]; //Quite arbitrary, should be large enough
243                 char *buff = tmp_buff;
244                 size_t buff_len = sizeof (tmp_buff);
245                 buff [0] = 0;
246
247 #ifdef USE_STRERROR_R_XSI
248                 int r;
249                 while ((r = strerror_r (errnum, buff, buff_len - 1))) {
250                         if (r != ERANGE) {
251                                 buff = g_strdup_printf ("Invalid Error code '%d'", errnum);
252                                 break;
253                         }
254                         if (buff == tmp_buff)
255                                 buff = g_malloc (buff_len * 2);
256                         else
257                                 buff = g_realloc (buff, buff_len * 2);
258                         buff_len *= 2;
259                  //Spec is not clean on whether size argument includes space for null terminator or not 
260                 }
261                 if (!error_messages [errnum])
262                         error_messages [errnum] = g_strdup (buff);
263                 if (buff != tmp_buff)
264                         g_free (buff);
265 #else /* USE_STRERROR_R_XSI */
266                 int r;
267                 r = strerror_r (errnum, buff, buff_len);
268                 if (!error_messages [errnum])
269                         error_messages [errnum] = g_strdup (buff);
270 #endif /* USE_STRERROR_R_XSI */
271
272 #else /* HAVE_STRERROR_R */
273                 if (!error_messages [errnum])
274                         error_messages [errnum] = g_strdup_printf ("Error code '%d'", errnum);
275 #endif /* HAVE_STRERROR_R */
276
277
278 #ifndef G_OS_WIN32
279                 pthread_mutex_unlock (&strerror_lock);
280 #endif
281
282         }
283         return error_messages [errnum];
284 }
285
286 gchar *
287 g_strconcat (const gchar *first, ...)
288 {
289         va_list args;
290         size_t total = 0;
291         char *s, *ret;
292         g_return_val_if_fail (first != NULL, NULL);
293
294         total += strlen (first);
295         va_start (args, first);
296         for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){
297                 total += strlen (s);
298         }
299         va_end (args);
300         
301         ret = g_malloc (total + 1);
302         if (ret == NULL)
303                 return NULL;
304
305         ret [total] = 0;
306         strcpy (ret, first);
307         va_start (args, first);
308         for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){
309                 strcat (ret, s);
310         }
311         va_end (args);
312
313         return ret;
314 }
315
316 static void
317 add_to_vector (gchar ***vector, int size, gchar *token)
318 {
319         *vector = *vector == NULL ? 
320                 (gchar **)g_malloc(2 * sizeof(*vector)) :
321                 (gchar **)g_realloc(*vector, (size + 1) * sizeof(*vector));
322                 
323         (*vector)[size - 1] = token;
324 }
325
326 gchar ** 
327 g_strsplit (const gchar *string, const gchar *delimiter, gint max_tokens)
328 {
329         const gchar *c;
330         gchar *token, **vector;
331         gint size = 1;
332         
333         g_return_val_if_fail (string != NULL, NULL);
334         g_return_val_if_fail (delimiter != NULL, NULL);
335         g_return_val_if_fail (delimiter[0] != 0, NULL);
336         
337         if (strncmp (string, delimiter, strlen (delimiter)) == 0) {
338                 vector = (gchar **)g_malloc (2 * sizeof(vector));
339                 vector[0] = g_strdup ("");
340                 size++;
341                 string += strlen (delimiter);
342         } else {
343                 vector = NULL;
344         }
345
346         while (*string && !(max_tokens > 0 && size >= max_tokens)) {
347                 c = string;
348                 if (strncmp (string, delimiter, strlen (delimiter)) == 0) {
349                         token = g_strdup ("");
350                         string += strlen (delimiter);
351                 } else {
352                         while (*string && strncmp (string, delimiter, strlen (delimiter)) != 0) {
353                                 string++;
354                         }
355
356                         if (*string) {
357                                 gsize toklen = (string - c);
358                                 token = g_strndup (c, toklen);
359
360                                 /* Need to leave a trailing empty
361                                  * token if the delimiter is the last
362                                  * part of the string
363                                  */
364                                 if (strcmp (string, delimiter) != 0) {
365                                         string += strlen (delimiter);
366                                 }
367                         } else {
368                                 token = g_strdup (c);
369                         }
370                 }
371                         
372                 add_to_vector (&vector, size, token);
373                 size++;
374         }
375
376         if (*string) {
377                 if (strcmp (string, delimiter) == 0)
378                         add_to_vector (&vector, size, g_strdup (""));
379                 else {
380                         /* Add the rest of the string as the last element */
381                         add_to_vector (&vector, size, g_strdup (string));
382                 }
383                 size++;
384         }
385         
386         if (vector == NULL) {
387                 vector = (gchar **) g_malloc (2 * sizeof (vector));
388                 vector [0] = NULL;
389         } else if (size > 0) {
390                 vector[size - 1] = NULL;
391         }
392         
393         return vector;
394 }
395
396 static gboolean
397 charcmp (gchar testchar, const gchar *compare)
398 {
399         while(*compare) {
400                 if (*compare == testchar) {
401                         return TRUE;
402                 }
403                 compare++;
404         }
405         
406         return FALSE;
407 }
408
409 gchar ** 
410 g_strsplit_set (const gchar *string, const gchar *delimiter, gint max_tokens)
411 {
412         const gchar *c;
413         gchar *token, **vector;
414         gint size = 1;
415         
416         g_return_val_if_fail (string != NULL, NULL);
417         g_return_val_if_fail (delimiter != NULL, NULL);
418         g_return_val_if_fail (delimiter[0] != 0, NULL);
419         
420         if (charcmp (*string, delimiter)) {
421                 vector = (gchar **)g_malloc (2 * sizeof(vector));
422                 vector[0] = g_strdup ("");
423                 size++;
424                 string++;
425         } else {
426                 vector = NULL;
427         }
428
429         c = string;
430         while (*string && !(max_tokens > 0 && size >= max_tokens)) {
431                 if (charcmp (*string, delimiter)) {
432                         gsize toklen = (string - c);
433                         if (toklen == 0) {
434                                 token = g_strdup ("");
435                         } else {
436                                 token = g_strndup (c, toklen);
437                         }
438                         
439                         c = string + 1;
440                         
441                         add_to_vector (&vector, size, token);
442                         size++;
443                 }
444
445                 string++;
446         }
447         
448         if (max_tokens > 0 && size >= max_tokens) {
449                 if (*string) {
450                         /* Add the rest of the string as the last element */
451                         add_to_vector (&vector, size, g_strdup (string));
452                         size++;
453                 }
454         } else {
455                 if (*c) {
456                         /* Fill in the trailing last token */
457                         add_to_vector (&vector, size, g_strdup (c));
458                         size++;
459                 } else {
460                         /* Need to leave a trailing empty token if the
461                          * delimiter is the last part of the string
462                          */
463                         add_to_vector (&vector, size, g_strdup (""));
464                         size++;
465                 }
466         }
467         
468         if (vector == NULL) {
469                 vector = (gchar **) g_malloc (2 * sizeof (vector));
470                 vector [0] = NULL;
471         } else if (size > 0) {
472                 vector[size - 1] = NULL;
473         }
474         
475         return vector;
476 }
477
478 gchar *
479 g_strreverse (gchar *str)
480 {
481         size_t i, j;
482         gchar c;
483
484         if (str == NULL)
485                 return NULL;
486
487         if (*str == 0)
488                 return str;
489
490         for (i = 0, j = strlen (str) - 1; i < j; i++, j--) {
491                 c = str [i];
492                 str [i] = str [j];
493                 str [j] = c;
494         }
495
496         return str;
497 }
498
499 gchar *
500 g_strjoin (const gchar *separator, ...)
501 {
502         va_list args;
503         char *res, *s, *r;
504         size_t len, slen;
505
506         if (separator != NULL)
507                 slen = strlen (separator);
508         else
509                 slen = 0;
510         
511         len = 0;
512         va_start (args, separator);
513         for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){
514                 len += strlen (s);
515                 len += slen;
516         }
517         va_end (args);
518
519         if (len == 0)
520                 return g_strdup ("");
521         
522         /* Remove the last separator */
523         if (slen > 0 && len > 0)
524                 len -= slen;
525
526         res = g_malloc (len + 1);
527         va_start (args, separator);
528         s = va_arg (args, char *);
529         r = g_stpcpy (res, s);
530         for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){
531                 if (separator != NULL)
532                         r = g_stpcpy (r, separator);
533                 r = g_stpcpy (r, s);
534         }
535         va_end (args);
536
537         return res;
538 }
539
540 gchar *
541 g_strjoinv (const gchar *separator, gchar **str_array)
542 {
543         char *res, *r;
544         size_t slen, len, i;
545         
546         if (separator != NULL)
547                 slen = strlen (separator);
548         else
549                 slen = 0;
550         
551         len = 0;
552         for (i = 0; str_array [i] != NULL; i++){
553                 len += strlen (str_array [i]);
554                 len += slen;
555         }
556
557         if (len == 0)
558                 return g_strdup ("");
559
560         if (slen > 0 && len > 0)
561                 len -= slen;
562
563         res = g_malloc (len + 1);
564         r = g_stpcpy (res, str_array [0]);
565         for (i = 1; str_array [i] != NULL; i++){
566                 if (separator != NULL)
567                         r = g_stpcpy (r, separator);
568                 r = g_stpcpy (r, str_array [i]);
569         }
570
571         return res;
572 }
573
574 gchar *
575 g_strchug (gchar *str)
576 {
577         size_t len;
578         gchar *tmp;
579
580         if (str == NULL)
581                 return NULL;
582
583         tmp = str;
584         while (*tmp && isspace (*tmp)) tmp++;
585         if (str != tmp) {
586                 len = strlen (str) - (tmp - str - 1);
587                 memmove (str, tmp, len);
588         }
589         return str;
590 }
591
592 gchar *
593 g_strchomp (gchar *str)
594 {
595         gchar *tmp;
596
597         if (str == NULL)
598                 return NULL;
599
600         tmp = str + strlen (str) - 1;
601         while (*tmp && isspace (*tmp)) tmp--;
602         *(tmp + 1) = '\0';
603         return str;
604 }
605
606 gint
607 g_printf(gchar const *format, ...)
608 {
609         va_list args;
610         gint ret;
611
612         va_start(args, format);
613         ret = vprintf(format, args);
614         va_end(args);
615
616         return ret;
617 }
618
619 gint
620 g_fprintf(FILE *file, gchar const *format, ...)
621 {
622         va_list args;
623         gint ret;
624
625         va_start(args, format);
626         ret = vfprintf(file, format, args);
627         va_end(args);
628
629         return ret;
630 }
631
632 gint
633 g_sprintf(gchar *string, gchar const *format, ...)
634 {
635         va_list args;
636         gint ret;
637
638         va_start(args, format);
639         ret = vsprintf(string, format, args);
640         va_end(args);
641
642         return ret;
643 }
644
645 gint
646 g_snprintf(gchar *string, gulong n, gchar const *format, ...)
647 {
648         va_list args;
649         gint ret;
650         
651         va_start(args, format);
652         ret = vsnprintf(string, n, format, args);
653         va_end(args);
654
655         return ret;
656 }
657
658 static const char hx [] = { '0', '1', '2', '3', '4', '5', '6', '7',
659                                   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
660
661 static gboolean
662 char_needs_encoding (char c)
663 {
664         if (((unsigned char)c) >= 0x80)
665                 return TRUE;
666         
667         if ((c >= '@' && c <= 'Z') ||
668             (c >= 'a' && c <= 'z') ||
669             (c >= '&' && c < 0x3b) ||
670             (c == '!') || (c == '$') || (c == '_') || (c == '=') || (c == '~'))
671                 return FALSE;
672         return TRUE;
673 }
674
675 gchar *
676 g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)
677 {
678         size_t n;
679         char *ret, *rp;
680         const char *p;
681 #ifdef G_OS_WIN32
682         const char *uriPrefix = "file:///";
683 #else
684         const char *uriPrefix = "file://";
685 #endif
686         
687         g_return_val_if_fail (filename != NULL, NULL);
688
689         if (hostname != NULL)
690                 g_warning ("%s", "eglib: g_filename_to_uri: hostname not handled");
691
692         if (!g_path_is_absolute (filename)){
693                 if (error != NULL)
694                         *error = g_error_new (NULL, 2, "Not an absolute filename");
695                 
696                 return NULL;
697         }
698         
699         n = strlen (uriPrefix) + 1;
700         for (p = filename; *p; p++){
701 #ifdef G_OS_WIN32
702                 if (*p == '\\') {
703                         n++;
704                         continue;
705                 }
706 #endif
707                 if (char_needs_encoding (*p))
708                         n += 3;
709                 else
710                         n++;
711         }
712         ret = g_malloc (n);
713         strcpy (ret, uriPrefix);
714         for (p = filename, rp = ret + strlen (ret); *p; p++){
715 #ifdef G_OS_WIN32
716                 if (*p == '\\') {
717                         *rp++ = '/';
718                         continue;
719                 }
720 #endif
721                 if (char_needs_encoding (*p)){
722                         *rp++ = '%';
723                         *rp++ = hx [((unsigned char)(*p)) >> 4];
724                         *rp++ = hx [((unsigned char)(*p)) & 0xf];
725                 } else
726                         *rp++ = *p;
727         }
728         *rp = 0;
729         return ret;
730 }
731
732 static int
733 decode (char p)
734 {
735         if (p >= '0' && p <= '9')
736                 return p - '0';
737         if (p >= 'A' && p <= 'F')
738                 return p - 'A';
739         if (p >= 'a' && p <= 'f')
740                 return p - 'a';
741         g_assert_not_reached ();
742         return 0;
743 }
744
745 gchar *
746 g_filename_from_uri (const gchar *uri, gchar **hostname, GError **error)
747 {
748         const char *p;
749         char *r, *result;
750         int flen = 0;
751         
752         g_return_val_if_fail (uri != NULL, NULL);
753
754         if (hostname != NULL)
755                 g_warning ("%s", "eglib: g_filename_from_uri: hostname not handled");
756
757         if (strncmp (uri, "file:///", 8) != 0){
758                 if (error != NULL)
759                         *error = g_error_new (NULL, 2, "URI does not start with the file: scheme");
760                 return NULL;
761         }
762
763         for (p = uri + 8; *p; p++){
764                 if (*p == '%'){
765                         if (p [1] && p [2] && isxdigit (p [1]) && isxdigit (p [2])){
766                                 p += 2;
767                         } else {
768                                 if (error != NULL)
769                                         *error = g_error_new (NULL, 2, "URI contains an invalid escape sequence");
770                                 return NULL;
771                         }
772                 } 
773                 flen++;
774         }
775 #ifndef G_OS_WIN32
776         flen++;
777 #endif
778
779         result = g_malloc (flen + 1);
780         result [flen] = 0;
781
782 #ifndef G_OS_WIN32
783         *result = '/';
784         r = result + 1;
785 #else
786         r = result;
787 #endif
788
789         for (p = uri + 8; *p; p++){
790                 if (*p == '%'){
791                         *r++ = (char)((decode (p [1]) << 4) | decode (p [2]));
792                         p += 2;
793                 } else
794                         *r++ = *p;
795                 flen++;
796         }
797         return result;
798 }
799
800 void
801 g_strdown (gchar *string)
802 {
803         g_return_if_fail (string != NULL);
804
805         while (*string){
806                 *string = (gchar)tolower (*string);
807                 string++;
808         }
809 }
810
811 gchar
812 g_ascii_tolower (gchar c)
813 {
814         return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
815 }
816
817 gchar *
818 g_ascii_strdown (const gchar *str, gssize len)
819 {
820         char *ret;
821         int i;
822         
823         g_return_val_if_fail  (str != NULL, NULL);
824
825         if (len == -1)
826                 len = strlen (str);
827         
828         ret = g_malloc (len + 1);
829         for (i = 0; i < len; i++)
830                 ret [i] = (guchar) g_ascii_tolower (str [i]);
831         ret [i] = 0;
832         
833         return ret;
834 }
835
836 gchar
837 g_ascii_toupper (gchar c)
838 {
839         return c >= 'a' && c <= 'z' ? c + ('A' - 'a') : c;
840 }
841
842 gchar *
843 g_ascii_strup (const gchar *str, gssize len)
844 {
845         char *ret;
846         int i;
847         
848         g_return_val_if_fail  (str != NULL, NULL);
849
850         if (len == -1)
851                 len = strlen (str);
852         
853         ret = g_malloc (len + 1);
854         for (i = 0; i < len; i++)
855                 ret [i] = (guchar) g_ascii_toupper (str [i]);
856         ret [i] = 0;
857         
858         return ret;
859 }
860
861 gint
862 g_ascii_strncasecmp (const gchar *s1, const gchar *s2, gsize n)
863 {
864         gsize i;
865         
866         g_return_val_if_fail (s1 != NULL, 0);
867         g_return_val_if_fail (s2 != NULL, 0);
868
869         for (i = 0; i < n; i++) {
870                 gchar c1 = g_ascii_tolower (*s1++);
871                 gchar c2 = g_ascii_tolower (*s2++);
872                 
873                 if (c1 != c2)
874                         return c1 - c2;
875         }
876         
877         return 0;
878 }
879
880 gint
881 g_ascii_strcasecmp (const gchar *s1, const gchar *s2)
882 {
883         const char *sp1 = s1;
884         const char *sp2 = s2;
885         
886         g_return_val_if_fail (s1 != NULL, 0);
887         g_return_val_if_fail (s2 != NULL, 0);
888         
889         while (*sp1 != '\0') {
890                 char c1 = g_ascii_tolower (*sp1++);
891                 char c2 = g_ascii_tolower (*sp2++);
892                 
893                 if (c1 != c2)
894                         return c1 - c2;
895         }
896         
897         return (*sp1) - (*sp2);
898 }
899
900 gchar *
901 g_strdelimit (gchar *string, const gchar *delimiters, gchar new_delimiter)
902 {
903         gchar *ptr;
904
905         g_return_val_if_fail (string != NULL, NULL);
906
907         if (delimiters == NULL)
908                 delimiters = G_STR_DELIMITERS;
909
910         for (ptr = string; *ptr; ptr++) {
911                 if (strchr (delimiters, *ptr))
912                         *ptr = new_delimiter;
913         }
914         
915         return string;
916 }
917
918 gsize 
919 g_strlcpy (gchar *dest, const gchar *src, gsize dest_size)
920 {
921 #ifdef HAVE_STRLCPY
922         return strlcpy (dest, src, dest_size);
923 #else
924         gchar *d;
925         const gchar *s;
926         gchar c;
927         gsize len;
928         
929         g_return_val_if_fail (src != NULL, 0);
930         g_return_val_if_fail (dest != NULL, 0);
931
932         len = dest_size;
933         if (len == 0)
934                 return 0;
935
936         s = src;
937         d = dest;
938         while (--len) {
939                 c = *s++;
940                 *d++ = c;
941                 if (c == '\0')
942                         return (dest_size - len - 1);
943         }
944
945         /* len is 0 i we get here */
946         *d = '\0';
947         /* we need to return the length of src here */
948         while (*s++) ; /* instead of a plain strlen, we use 's' */
949         return s - src - 1;
950 #endif
951 }
952
953 gchar *
954 g_stpcpy (gchar *dest, const char *src)
955 {
956         g_return_val_if_fail (dest != NULL, dest);
957         g_return_val_if_fail (src != NULL, dest);
958
959 #if HAVE_STPCPY
960         return stpcpy (dest, src);
961 #else
962         while (*src)
963                 *dest++ = *src++;
964         
965         *dest = '\0';
966         
967         return dest;
968 #endif
969 }
970
971 static const gchar escaped_dflt [256] = {
972         1, 1, 1, 1, 1, 1, 1, 1, 'b', 't', 'n', 1, 'f', 'r', 1, 1,
973         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
974         0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
975         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
976         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
977         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0,
978         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
979         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
980         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
981         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
982         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
983         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
984         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
985         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
986         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
987         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
988 };
989
990 gchar *
991 g_strescape (const gchar *source, const gchar *exceptions)
992 {
993         gchar escaped [256];
994         const gchar *ptr;
995         gchar c;
996         gchar op;
997         gchar *result;
998         gchar *res_ptr;
999
1000         g_return_val_if_fail (source != NULL, NULL);
1001
1002         memcpy (escaped, escaped_dflt, 256);
1003         if (exceptions != NULL) {
1004                 for (ptr = exceptions; *ptr; ptr++)
1005                         escaped [(int) *ptr] = 0;
1006         }
1007         result = g_malloc (strlen (source) * 4 + 1); /* Worst case: everything octal. */
1008         res_ptr = result;
1009         for (ptr = source; *ptr; ptr++) {
1010                 c = *ptr;
1011                 op = escaped [(int) c];
1012                 if (op == 0) {
1013                         *res_ptr++ = c;
1014                 } else {
1015                         *res_ptr++ = '\\';
1016                         if (op != 1) {
1017                                 *res_ptr++ = op;
1018                         } else {
1019                                 *res_ptr++ = '0' + ((c >> 6) & 3);
1020                                 *res_ptr++ = '0' + ((c >> 3) & 7);
1021                                 *res_ptr++ = '0' + (c & 7);
1022                         }
1023                 }
1024         }
1025         *res_ptr = '\0';
1026         return result;
1027 }
1028
1029 gint
1030 g_ascii_xdigit_value (gchar c)
1031 {
1032         return ((isxdigit (c) == 0) ? -1 :
1033                 ((c >= '0' && c <= '9') ? (c - '0') :
1034                  ((c >= 'a' && c <= 'f') ? (c - 'a' + 10) :
1035                   (c - 'A' + 10))));
1036 }
1037
1038 gchar *
1039 g_strnfill (gsize length, gchar fill_char)
1040 {
1041         gchar *ret = g_new (gchar, length + 1);
1042
1043         memset (ret, fill_char, length);
1044         ret [length] = 0;
1045         return ret;
1046 }