Distinguish between the XSI-compliant and the GNU-specific version of `strerror_r ()`
[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                 buff = strerror_r (errnum, buff, buff_len);
267                 if (!error_messages [errnum])
268                         error_messages [errnum] = g_strdup (buff);
269 #endif /* USE_STRERROR_R_XSI */
270
271 #else /* HAVE_STRERROR_R */
272                 if (!error_messages [errnum])
273                         error_messages [errnum] = g_strdup_printf ("Error code '%d'", errnum);
274 #endif /* HAVE_STRERROR_R */
275
276
277 #ifndef G_OS_WIN32
278                 pthread_mutex_unlock (&strerror_lock);
279 #endif
280
281         }
282         return error_messages [errnum];
283 }
284
285 gchar *
286 g_strconcat (const gchar *first, ...)
287 {
288         va_list args;
289         size_t total = 0;
290         char *s, *ret;
291         g_return_val_if_fail (first != NULL, NULL);
292
293         total += strlen (first);
294         va_start (args, first);
295         for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){
296                 total += strlen (s);
297         }
298         va_end (args);
299         
300         ret = g_malloc (total + 1);
301         if (ret == NULL)
302                 return NULL;
303
304         ret [total] = 0;
305         strcpy (ret, first);
306         va_start (args, first);
307         for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){
308                 strcat (ret, s);
309         }
310         va_end (args);
311
312         return ret;
313 }
314
315 static void
316 add_to_vector (gchar ***vector, int size, gchar *token)
317 {
318         *vector = *vector == NULL ? 
319                 (gchar **)g_malloc(2 * sizeof(*vector)) :
320                 (gchar **)g_realloc(*vector, (size + 1) * sizeof(*vector));
321                 
322         (*vector)[size - 1] = token;
323 }
324
325 gchar ** 
326 g_strsplit (const gchar *string, const gchar *delimiter, gint max_tokens)
327 {
328         const gchar *c;
329         gchar *token, **vector;
330         gint size = 1;
331         
332         g_return_val_if_fail (string != NULL, NULL);
333         g_return_val_if_fail (delimiter != NULL, NULL);
334         g_return_val_if_fail (delimiter[0] != 0, NULL);
335         
336         if (strncmp (string, delimiter, strlen (delimiter)) == 0) {
337                 vector = (gchar **)g_malloc (2 * sizeof(vector));
338                 vector[0] = g_strdup ("");
339                 size++;
340                 string += strlen (delimiter);
341         } else {
342                 vector = NULL;
343         }
344
345         while (*string && !(max_tokens > 0 && size >= max_tokens)) {
346                 c = string;
347                 if (strncmp (string, delimiter, strlen (delimiter)) == 0) {
348                         token = g_strdup ("");
349                         string += strlen (delimiter);
350                 } else {
351                         while (*string && strncmp (string, delimiter, strlen (delimiter)) != 0) {
352                                 string++;
353                         }
354
355                         if (*string) {
356                                 gsize toklen = (string - c);
357                                 token = g_strndup (c, toklen);
358
359                                 /* Need to leave a trailing empty
360                                  * token if the delimiter is the last
361                                  * part of the string
362                                  */
363                                 if (strcmp (string, delimiter) != 0) {
364                                         string += strlen (delimiter);
365                                 }
366                         } else {
367                                 token = g_strdup (c);
368                         }
369                 }
370                         
371                 add_to_vector (&vector, size, token);
372                 size++;
373         }
374
375         if (*string) {
376                 if (strcmp (string, delimiter) == 0)
377                         add_to_vector (&vector, size, g_strdup (""));
378                 else {
379                         /* Add the rest of the string as the last element */
380                         add_to_vector (&vector, size, g_strdup (string));
381                 }
382                 size++;
383         }
384         
385         if (vector == NULL) {
386                 vector = (gchar **) g_malloc (2 * sizeof (vector));
387                 vector [0] = NULL;
388         } else if (size > 0) {
389                 vector[size - 1] = NULL;
390         }
391         
392         return vector;
393 }
394
395 static gboolean
396 charcmp (gchar testchar, const gchar *compare)
397 {
398         while(*compare) {
399                 if (*compare == testchar) {
400                         return TRUE;
401                 }
402                 compare++;
403         }
404         
405         return FALSE;
406 }
407
408 gchar ** 
409 g_strsplit_set (const gchar *string, const gchar *delimiter, gint max_tokens)
410 {
411         const gchar *c;
412         gchar *token, **vector;
413         gint size = 1;
414         
415         g_return_val_if_fail (string != NULL, NULL);
416         g_return_val_if_fail (delimiter != NULL, NULL);
417         g_return_val_if_fail (delimiter[0] != 0, NULL);
418         
419         if (charcmp (*string, delimiter)) {
420                 vector = (gchar **)g_malloc (2 * sizeof(vector));
421                 vector[0] = g_strdup ("");
422                 size++;
423                 string++;
424         } else {
425                 vector = NULL;
426         }
427
428         c = string;
429         while (*string && !(max_tokens > 0 && size >= max_tokens)) {
430                 if (charcmp (*string, delimiter)) {
431                         gsize toklen = (string - c);
432                         if (toklen == 0) {
433                                 token = g_strdup ("");
434                         } else {
435                                 token = g_strndup (c, toklen);
436                         }
437                         
438                         c = string + 1;
439                         
440                         add_to_vector (&vector, size, token);
441                         size++;
442                 }
443
444                 string++;
445         }
446         
447         if (max_tokens > 0 && size >= max_tokens) {
448                 if (*string) {
449                         /* Add the rest of the string as the last element */
450                         add_to_vector (&vector, size, g_strdup (string));
451                         size++;
452                 }
453         } else {
454                 if (*c) {
455                         /* Fill in the trailing last token */
456                         add_to_vector (&vector, size, g_strdup (c));
457                         size++;
458                 } else {
459                         /* Need to leave a trailing empty token if the
460                          * delimiter is the last part of the string
461                          */
462                         add_to_vector (&vector, size, g_strdup (""));
463                         size++;
464                 }
465         }
466         
467         if (vector == NULL) {
468                 vector = (gchar **) g_malloc (2 * sizeof (vector));
469                 vector [0] = NULL;
470         } else if (size > 0) {
471                 vector[size - 1] = NULL;
472         }
473         
474         return vector;
475 }
476
477 gchar *
478 g_strreverse (gchar *str)
479 {
480         size_t i, j;
481         gchar c;
482
483         if (str == NULL)
484                 return NULL;
485
486         if (*str == 0)
487                 return str;
488
489         for (i = 0, j = strlen (str) - 1; i < j; i++, j--) {
490                 c = str [i];
491                 str [i] = str [j];
492                 str [j] = c;
493         }
494
495         return str;
496 }
497
498 gchar *
499 g_strjoin (const gchar *separator, ...)
500 {
501         va_list args;
502         char *res, *s, *r;
503         size_t len, slen;
504
505         if (separator != NULL)
506                 slen = strlen (separator);
507         else
508                 slen = 0;
509         
510         len = 0;
511         va_start (args, separator);
512         for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){
513                 len += strlen (s);
514                 len += slen;
515         }
516         va_end (args);
517
518         if (len == 0)
519                 return g_strdup ("");
520         
521         /* Remove the last separator */
522         if (slen > 0 && len > 0)
523                 len -= slen;
524
525         res = g_malloc (len + 1);
526         va_start (args, separator);
527         s = va_arg (args, char *);
528         r = g_stpcpy (res, s);
529         for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){
530                 if (separator != NULL)
531                         r = g_stpcpy (r, separator);
532                 r = g_stpcpy (r, s);
533         }
534         va_end (args);
535
536         return res;
537 }
538
539 gchar *
540 g_strjoinv (const gchar *separator, gchar **str_array)
541 {
542         char *res, *r;
543         size_t slen, len, i;
544         
545         if (separator != NULL)
546                 slen = strlen (separator);
547         else
548                 slen = 0;
549         
550         len = 0;
551         for (i = 0; str_array [i] != NULL; i++){
552                 len += strlen (str_array [i]);
553                 len += slen;
554         }
555
556         if (len == 0)
557                 return g_strdup ("");
558
559         if (slen > 0 && len > 0)
560                 len -= slen;
561
562         res = g_malloc (len + 1);
563         r = g_stpcpy (res, str_array [0]);
564         for (i = 1; str_array [i] != NULL; i++){
565                 if (separator != NULL)
566                         r = g_stpcpy (r, separator);
567                 r = g_stpcpy (r, str_array [i]);
568         }
569
570         return res;
571 }
572
573 gchar *
574 g_strchug (gchar *str)
575 {
576         size_t len;
577         gchar *tmp;
578
579         if (str == NULL)
580                 return NULL;
581
582         tmp = str;
583         while (*tmp && isspace (*tmp)) tmp++;
584         if (str != tmp) {
585                 len = strlen (str) - (tmp - str - 1);
586                 memmove (str, tmp, len);
587         }
588         return str;
589 }
590
591 gchar *
592 g_strchomp (gchar *str)
593 {
594         gchar *tmp;
595
596         if (str == NULL)
597                 return NULL;
598
599         tmp = str + strlen (str) - 1;
600         while (*tmp && isspace (*tmp)) tmp--;
601         *(tmp + 1) = '\0';
602         return str;
603 }
604
605 gint
606 g_printf(gchar const *format, ...)
607 {
608         va_list args;
609         gint ret;
610
611         va_start(args, format);
612         ret = vprintf(format, args);
613         va_end(args);
614
615         return ret;
616 }
617
618 gint
619 g_fprintf(FILE *file, gchar const *format, ...)
620 {
621         va_list args;
622         gint ret;
623
624         va_start(args, format);
625         ret = vfprintf(file, format, args);
626         va_end(args);
627
628         return ret;
629 }
630
631 gint
632 g_sprintf(gchar *string, gchar const *format, ...)
633 {
634         va_list args;
635         gint ret;
636
637         va_start(args, format);
638         ret = vsprintf(string, format, args);
639         va_end(args);
640
641         return ret;
642 }
643
644 gint
645 g_snprintf(gchar *string, gulong n, gchar const *format, ...)
646 {
647         va_list args;
648         gint ret;
649         
650         va_start(args, format);
651         ret = vsnprintf(string, n, format, args);
652         va_end(args);
653
654         return ret;
655 }
656
657 static const char hx [] = { '0', '1', '2', '3', '4', '5', '6', '7',
658                                   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
659
660 static gboolean
661 char_needs_encoding (char c)
662 {
663         if (((unsigned char)c) >= 0x80)
664                 return TRUE;
665         
666         if ((c >= '@' && c <= 'Z') ||
667             (c >= 'a' && c <= 'z') ||
668             (c >= '&' && c < 0x3b) ||
669             (c == '!') || (c == '$') || (c == '_') || (c == '=') || (c == '~'))
670                 return FALSE;
671         return TRUE;
672 }
673
674 gchar *
675 g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)
676 {
677         size_t n;
678         char *ret, *rp;
679         const char *p;
680 #ifdef G_OS_WIN32
681         const char *uriPrefix = "file:///";
682 #else
683         const char *uriPrefix = "file://";
684 #endif
685         
686         g_return_val_if_fail (filename != NULL, NULL);
687
688         if (hostname != NULL)
689                 g_warning ("%s", "eglib: g_filename_to_uri: hostname not handled");
690
691         if (!g_path_is_absolute (filename)){
692                 if (error != NULL)
693                         *error = g_error_new (NULL, 2, "Not an absolute filename");
694                 
695                 return NULL;
696         }
697         
698         n = strlen (uriPrefix) + 1;
699         for (p = filename; *p; p++){
700 #ifdef G_OS_WIN32
701                 if (*p == '\\') {
702                         n++;
703                         continue;
704                 }
705 #endif
706                 if (char_needs_encoding (*p))
707                         n += 3;
708                 else
709                         n++;
710         }
711         ret = g_malloc (n);
712         strcpy (ret, uriPrefix);
713         for (p = filename, rp = ret + strlen (ret); *p; p++){
714 #ifdef G_OS_WIN32
715                 if (*p == '\\') {
716                         *rp++ = '/';
717                         continue;
718                 }
719 #endif
720                 if (char_needs_encoding (*p)){
721                         *rp++ = '%';
722                         *rp++ = hx [((unsigned char)(*p)) >> 4];
723                         *rp++ = hx [((unsigned char)(*p)) & 0xf];
724                 } else
725                         *rp++ = *p;
726         }
727         *rp = 0;
728         return ret;
729 }
730
731 static int
732 decode (char p)
733 {
734         if (p >= '0' && p <= '9')
735                 return p - '0';
736         if (p >= 'A' && p <= 'F')
737                 return p - 'A';
738         if (p >= 'a' && p <= 'f')
739                 return p - 'a';
740         g_assert_not_reached ();
741         return 0;
742 }
743
744 gchar *
745 g_filename_from_uri (const gchar *uri, gchar **hostname, GError **error)
746 {
747         const char *p;
748         char *r, *result;
749         int flen = 0;
750         
751         g_return_val_if_fail (uri != NULL, NULL);
752
753         if (hostname != NULL)
754                 g_warning ("%s", "eglib: g_filename_from_uri: hostname not handled");
755
756         if (strncmp (uri, "file:///", 8) != 0){
757                 if (error != NULL)
758                         *error = g_error_new (NULL, 2, "URI does not start with the file: scheme");
759                 return NULL;
760         }
761
762         for (p = uri + 8; *p; p++){
763                 if (*p == '%'){
764                         if (p [1] && p [2] && isxdigit (p [1]) && isxdigit (p [2])){
765                                 p += 2;
766                         } else {
767                                 if (error != NULL)
768                                         *error = g_error_new (NULL, 2, "URI contains an invalid escape sequence");
769                                 return NULL;
770                         }
771                 } 
772                 flen++;
773         }
774 #ifndef G_OS_WIN32
775         flen++;
776 #endif
777
778         result = g_malloc (flen + 1);
779         result [flen] = 0;
780
781 #ifndef G_OS_WIN32
782         *result = '/';
783         r = result + 1;
784 #else
785         r = result;
786 #endif
787
788         for (p = uri + 8; *p; p++){
789                 if (*p == '%'){
790                         *r++ = (char)((decode (p [1]) << 4) | decode (p [2]));
791                         p += 2;
792                 } else
793                         *r++ = *p;
794                 flen++;
795         }
796         return result;
797 }
798
799 void
800 g_strdown (gchar *string)
801 {
802         g_return_if_fail (string != NULL);
803
804         while (*string){
805                 *string = (gchar)tolower (*string);
806                 string++;
807         }
808 }
809
810 gchar
811 g_ascii_tolower (gchar c)
812 {
813         return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
814 }
815
816 gchar *
817 g_ascii_strdown (const gchar *str, gssize len)
818 {
819         char *ret;
820         int i;
821         
822         g_return_val_if_fail  (str != NULL, NULL);
823
824         if (len == -1)
825                 len = strlen (str);
826         
827         ret = g_malloc (len + 1);
828         for (i = 0; i < len; i++)
829                 ret [i] = (guchar) g_ascii_tolower (str [i]);
830         ret [i] = 0;
831         
832         return ret;
833 }
834
835 gchar
836 g_ascii_toupper (gchar c)
837 {
838         return c >= 'a' && c <= 'z' ? c + ('A' - 'a') : c;
839 }
840
841 gchar *
842 g_ascii_strup (const gchar *str, gssize len)
843 {
844         char *ret;
845         int i;
846         
847         g_return_val_if_fail  (str != NULL, NULL);
848
849         if (len == -1)
850                 len = strlen (str);
851         
852         ret = g_malloc (len + 1);
853         for (i = 0; i < len; i++)
854                 ret [i] = (guchar) g_ascii_toupper (str [i]);
855         ret [i] = 0;
856         
857         return ret;
858 }
859
860 gint
861 g_ascii_strncasecmp (const gchar *s1, const gchar *s2, gsize n)
862 {
863         gsize i;
864         
865         g_return_val_if_fail (s1 != NULL, 0);
866         g_return_val_if_fail (s2 != NULL, 0);
867
868         for (i = 0; i < n; i++) {
869                 gchar c1 = g_ascii_tolower (*s1++);
870                 gchar c2 = g_ascii_tolower (*s2++);
871                 
872                 if (c1 != c2)
873                         return c1 - c2;
874         }
875         
876         return 0;
877 }
878
879 gint
880 g_ascii_strcasecmp (const gchar *s1, const gchar *s2)
881 {
882         const char *sp1 = s1;
883         const char *sp2 = s2;
884         
885         g_return_val_if_fail (s1 != NULL, 0);
886         g_return_val_if_fail (s2 != NULL, 0);
887         
888         while (*sp1 != '\0') {
889                 char c1 = g_ascii_tolower (*sp1++);
890                 char c2 = g_ascii_tolower (*sp2++);
891                 
892                 if (c1 != c2)
893                         return c1 - c2;
894         }
895         
896         return (*sp1) - (*sp2);
897 }
898
899 gchar *
900 g_strdelimit (gchar *string, const gchar *delimiters, gchar new_delimiter)
901 {
902         gchar *ptr;
903
904         g_return_val_if_fail (string != NULL, NULL);
905
906         if (delimiters == NULL)
907                 delimiters = G_STR_DELIMITERS;
908
909         for (ptr = string; *ptr; ptr++) {
910                 if (strchr (delimiters, *ptr))
911                         *ptr = new_delimiter;
912         }
913         
914         return string;
915 }
916
917 gsize 
918 g_strlcpy (gchar *dest, const gchar *src, gsize dest_size)
919 {
920 #ifdef HAVE_STRLCPY
921         return strlcpy (dest, src, dest_size);
922 #else
923         gchar *d;
924         const gchar *s;
925         gchar c;
926         gsize len;
927         
928         g_return_val_if_fail (src != NULL, 0);
929         g_return_val_if_fail (dest != NULL, 0);
930
931         len = dest_size;
932         if (len == 0)
933                 return 0;
934
935         s = src;
936         d = dest;
937         while (--len) {
938                 c = *s++;
939                 *d++ = c;
940                 if (c == '\0')
941                         return (dest_size - len - 1);
942         }
943
944         /* len is 0 i we get here */
945         *d = '\0';
946         /* we need to return the length of src here */
947         while (*s++) ; /* instead of a plain strlen, we use 's' */
948         return s - src - 1;
949 #endif
950 }
951
952 gchar *
953 g_stpcpy (gchar *dest, const char *src)
954 {
955         g_return_val_if_fail (dest != NULL, dest);
956         g_return_val_if_fail (src != NULL, dest);
957
958 #if HAVE_STPCPY
959         return stpcpy (dest, src);
960 #else
961         while (*src)
962                 *dest++ = *src++;
963         
964         *dest = '\0';
965         
966         return dest;
967 #endif
968 }
969
970 static const gchar escaped_dflt [256] = {
971         1, 1, 1, 1, 1, 1, 1, 1, 'b', 't', 'n', 1, 'f', 'r', 1, 1,
972         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
973         0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
974         0, 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,
977         0, 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         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
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 };
988
989 gchar *
990 g_strescape (const gchar *source, const gchar *exceptions)
991 {
992         gchar escaped [256];
993         const gchar *ptr;
994         gchar c;
995         gchar op;
996         gchar *result;
997         gchar *res_ptr;
998
999         g_return_val_if_fail (source != NULL, NULL);
1000
1001         memcpy (escaped, escaped_dflt, 256);
1002         if (exceptions != NULL) {
1003                 for (ptr = exceptions; *ptr; ptr++)
1004                         escaped [(int) *ptr] = 0;
1005         }
1006         result = g_malloc (strlen (source) * 4 + 1); /* Worst case: everything octal. */
1007         res_ptr = result;
1008         for (ptr = source; *ptr; ptr++) {
1009                 c = *ptr;
1010                 op = escaped [(int) c];
1011                 if (op == 0) {
1012                         *res_ptr++ = c;
1013                 } else {
1014                         *res_ptr++ = '\\';
1015                         if (op != 1) {
1016                                 *res_ptr++ = op;
1017                         } else {
1018                                 *res_ptr++ = '0' + ((c >> 6) & 3);
1019                                 *res_ptr++ = '0' + ((c >> 3) & 7);
1020                                 *res_ptr++ = '0' + (c & 7);
1021                         }
1022                 }
1023         }
1024         *res_ptr = '\0';
1025         return result;
1026 }
1027
1028 gint
1029 g_ascii_xdigit_value (gchar c)
1030 {
1031         return ((isxdigit (c) == 0) ? -1 :
1032                 ((c >= '0' && c <= '9') ? (c - '0') :
1033                  ((c >= 'a' && c <= 'f') ? (c - 'a' + 10) :
1034                   (c - 'A' + 10))));
1035 }
1036
1037 gchar *
1038 g_strnfill (gsize length, gchar fill_char)
1039 {
1040         gchar *ret = g_new (gchar, length + 1);
1041
1042         memset (ret, fill_char, length);
1043         ret [length] = 0;
1044         return ret;
1045 }