2006-10-05 Miguel de Icaza <miguel@novell.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 #define _GNU_SOURCE
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         return strndup (str, n);
40 }
41
42 void
43 g_strfreev (gchar **str_array)
44 {
45         gchar **orig = str_array;
46         if (str_array == NULL)
47                 return;
48         while (*str_array != NULL){
49                 g_free (*str_array);
50                 str_array++;
51         }
52         g_free (orig);
53 }
54
55 guint
56 g_strv_length(gchar **str_array)
57 {
58         gint length = 0;
59         g_return_val_if_fail(str_array != NULL, 0);
60         for(length = 0; str_array[length] != NULL; length++);
61         return length;
62 }
63
64 gboolean
65 g_str_has_suffix(const gchar *str, const gchar *suffix)
66 {
67         gint str_length;
68         gint suffix_length;
69         
70         g_return_val_if_fail(str != NULL, FALSE);
71         g_return_val_if_fail(suffix != NULL, FALSE);
72
73         str_length = strlen(str);
74         suffix_length = strlen(suffix);
75
76         return suffix_length <= str_length ?
77                 strncmp(str + str_length - suffix_length, suffix, suffix_length) == 0 :
78                 FALSE;
79 }
80
81 gboolean
82 g_str_has_prefix(const gchar *str, const gchar *prefix)
83 {
84         gint str_length;
85         gint prefix_length;
86         
87         g_return_val_if_fail(str != NULL, FALSE);
88         g_return_val_if_fail(prefix != NULL, FALSE);
89
90         str_length = strlen(str);
91         prefix_length = strlen(prefix);
92
93         return prefix_length <= str_length ?
94                 strncmp(str, prefix, prefix_length) == 0 :
95                 FALSE;
96 }
97
98 gchar *
99 g_strdup_vprintf (const gchar *format, va_list args)
100 {
101         int n;
102         char *ret;
103         
104         n = vasprintf (&ret, format, args);
105         if (n == -1)
106                 return NULL;
107
108         return ret;
109 }
110
111 gchar *
112 g_strdup_printf (const gchar *format, ...)
113 {
114         gchar *ret;
115         va_list args;
116         int n;
117
118         va_start (args, format);
119         n = vasprintf (&ret, format, args);
120         va_end (args);
121         if (n == -1)
122                 return NULL;
123
124         return ret;
125 }
126
127 const gchar *
128 g_strerror (gint errnum)
129 {
130         return strerror (errnum);
131 }
132
133 gchar *
134 g_strconcat (const gchar *first, ...)
135 {
136         g_return_val_if_fail (first != NULL, NULL);
137         va_list args;
138         int total = 0;
139         char *s, *ret;
140
141         total += strlen (first);
142         va_start (args, first);
143         for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){
144                 total += strlen (s);
145         }
146         va_end (args);
147         
148         ret = g_malloc (total + 1);
149         if (ret == NULL)
150                 return NULL;
151
152         ret [total] = 0;
153         strcpy (ret, first);
154         va_start (args, first);
155         for (s = va_arg (args, char *); s != NULL; s = va_arg(args, char *)){
156                 strcat (ret, s);
157         }
158         va_end (args);
159
160         return ret;
161 }
162
163 gchar ** 
164 g_strsplit (const gchar *string, const gchar *delimiter, gint max_tokens)
165 {
166         gchar *string_c;
167         gchar *strtok_save, **vector;
168         gchar *token, *token_c;
169         gint size = 1;
170         gint token_length;
171
172         g_return_val_if_fail(string != NULL, NULL);
173         g_return_val_if_fail(delimiter != NULL, NULL);
174         g_return_val_if_fail(delimiter[0] != 0, NULL);
175         
176         token_length = strlen(string);
177         string_c = (gchar *)g_malloc(token_length + 1);
178         memcpy(string_c, string, token_length);
179         string_c[token_length] = 0;
180         
181         vector = NULL;
182         token = (gchar *)strtok_r(string_c, delimiter, &strtok_save);
183
184         while(token != NULL) {
185                 token_length = strlen(token);
186                 token_c = (gchar *)g_malloc(token_length + 1);
187                 memcpy(token_c, token, token_length);
188                 token_c[token_length] = 0;
189
190                 vector = vector == NULL ? 
191                         (gchar **)g_malloc(2 * sizeof(vector)) :
192                         (gchar **)g_realloc(vector, (size + 1) * sizeof(vector));
193         
194                 vector[size - 1] = token_c;     
195                 size++;
196
197                 if(max_tokens > 0 && size >= max_tokens) {
198                         if(size > max_tokens) {
199                                 break;
200                         }
201
202                         token = strtok_save;
203                 } else {
204                         token = (gchar *)strtok_r(NULL, delimiter, &strtok_save);
205                 }
206         }
207
208         if(vector != NULL && size > 0) {
209                 vector[size - 1] = NULL;
210         }
211         
212         g_free(string_c);
213         string_c = NULL;
214
215         return vector;
216 }
217
218 gchar *
219 g_strreverse (gchar *str)
220 {
221         guint len, half;
222         gint i;
223         gchar c;
224
225         if (str == NULL)
226                 return NULL;
227
228         len = strlen (str);
229         half = len / 2;
230         len--;
231         for (i = 0; i < half; i++, len--) {
232                 c = str [i];
233                 str [i] = str [len];
234                 str [len] = c;
235         }
236         return str;
237 }
238
239 gchar *
240 g_strjoin (const gchar *separator, ...)
241 {
242         va_list args;
243         char *res, *s;
244         int len, slen;
245
246         if (separator != NULL)
247                 slen = strlen (separator);
248         else
249                 slen = 0;
250         len = 0;
251         va_start (args, separator);
252         for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){
253                 len += strlen (s);
254                 len += slen;
255         }
256         va_end (args);
257         if (len == 0)
258                 return g_strdup ("");
259         
260         /* Remove the last separator */
261         if (slen > 0 && len > 0)
262                 len -= slen;
263         len++;
264         res = g_malloc (len);
265         va_start (args, separator);
266         s = va_arg (args, char *);
267         strcpy (res, s);
268         for (s = va_arg (args, char *); s != NULL; s = va_arg (args, char *)){
269                 if (separator != NULL)
270                         strcat (res, separator);
271                 strcat (res, s);
272         }
273         va_end (args);
274
275         return res;
276 }
277
278 gchar *
279 g_strchug (gchar *str)
280 {
281         gint len;
282         gchar *tmp;
283
284         if (str == NULL)
285                 return NULL;
286
287         tmp = str;
288         while (*tmp && isspace (*tmp)) tmp++;
289         if (str != tmp) {
290                 len = strlen (str) - (tmp - str - 1);
291                 memmove (str, tmp, len);
292         }
293         return str;
294 }
295
296 gchar *
297 g_strchomp (gchar *str)
298 {
299         gchar *tmp;
300
301         if (str == NULL)
302                 return NULL;
303
304         tmp = str + strlen (str) - 1;
305         while (*tmp && isspace (*tmp)) tmp--;
306         *(tmp + 1) = '\0';
307         return str;
308 }
309
310 gint
311 g_printf(gchar const *format, ...)
312 {
313         va_list args;
314         gint ret;
315
316         va_start(args, format);
317         ret = vprintf(format, args);
318         va_end(args);
319
320         return ret;
321 }
322
323 gint
324 g_fprintf(FILE *file, gchar const *format, ...)
325 {
326         va_list args;
327         gint ret;
328
329         va_start(args, format);
330         ret = vfprintf(file, format, args);
331         va_end(args);
332
333         return ret;
334 }
335
336 gint
337 g_sprintf(gchar *string, gchar const *format, ...)
338 {
339         va_list args;
340         gint ret;
341
342         va_start(args, format);
343         ret = vsprintf(string, format, args);
344         va_end(args);
345
346         return ret;
347 }
348
349 gint
350 g_snprintf(gchar *string, gulong n, gchar const *format, ...)
351 {
352         va_list args;
353         gint ret;
354         
355         va_start(args, format);
356         ret = vsnprintf(string, n, format, args);
357         va_end(args);
358
359         return ret;
360 }
361
362 static const char const hx [] = { '0', '1', '2', '3', '4', '5', '6', '7',
363                                   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
364
365 static gboolean
366 char_needs_encoding (char c)
367 {
368         if (((unsigned char)c) >= 0x80)
369                 return TRUE;
370         
371         if ((c >= '@' && c <= 'Z') ||
372             (c >= 'a' && c <= 'z') ||
373             (c >= '&' && c < 0x3b) ||
374             (c == '!') || (c == '$') || (c == '_') || (c == '=') || (c == '~'))
375                 return FALSE;
376         return TRUE;
377 }
378
379 gchar *
380 g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)
381 {
382         int n;
383         char *ret, *rp;
384         const char *p;
385         
386         g_return_val_if_fail (filename != NULL, NULL);
387
388         if (hostname != NULL)
389                 g_warning ("eglib: g_filename_to_uri: hostname not handled");
390
391         if (*filename != '/'){
392                 if (error != NULL)
393                         *error = g_error_new (NULL, 2, "Not an absolute filename");
394                 
395                 return NULL;
396         }
397         
398         n = strlen ("file://") + 1;
399         for (p = filename; *p; p++){
400                 if (char_needs_encoding (*p))
401                         n += 3;
402                 else
403                         n++;
404         }
405         ret = g_malloc (n);
406         strcpy (ret, "file://");
407         for (p = filename, rp = ret + strlen (ret); *p; p++){
408                 if (char_needs_encoding (*p)){
409                         *rp++ = '%';
410                         *rp++ = hx [((unsigned char)(*p)) >> 4];
411                         *rp++ = hx [((unsigned char)(*p)) & 0xf];
412                 } else
413                         *rp++ = *p;
414         }
415         *rp = 0;
416         return ret;
417 }
418
419 static int
420 decode (char p)
421 {
422         if (p >= '0' && p <= '9')
423                 return p - '0';
424         if (p >= 'A' && p <= 'F')
425                 return p - 'A';
426         if (p >= 'a' && p <= 'f')
427                 return p - 'a';
428         g_assert_not_reached ();
429         return 0;
430 }
431
432 gchar *
433 g_filename_from_uri (const gchar *uri, gchar **hostname, GError **error)
434 {
435         const char *p;
436         char *r, *result;
437         int flen = 0;
438         
439         g_return_val_if_fail (uri != NULL, NULL);
440
441         if (hostname != NULL)
442                 g_warning ("eglib: g_filename_from_uri: hostname not handled");
443
444         if (strncmp (uri, "file:///", 8) != 0){
445                 if (error != NULL)
446                         *error = g_error_new (NULL, 2, "URI does not start with the file: scheme");
447                 return NULL;
448         }
449
450         for (p = uri + 8; *p; p++){
451                 if (*p == '%'){
452                         if (p [1] && p [2] && isxdigit (p [1]) && isxdigit (p [2])){
453                                 p += 2;
454                         } else {
455                                 if (error != NULL)
456                                         *error = g_error_new (NULL, 2, "URI contains an invalid escape sequence");
457                                 return NULL;
458                         }
459                 } 
460                 flen++;
461         }
462         flen++;
463         
464         result = g_malloc (flen + 1);
465         *result = '/';
466         result [flen] = 0;
467
468         for (p = uri + 8, r = result + 1; *p; p++){
469                 if (*p == '%'){
470                         *r++ = (decode (p [1]) << 4) | decode (p [2]);
471                         p += 2;
472                 } else
473                         *r++ = *p;
474                 flen++;
475         }
476         return result;
477 }