Add new error codes for when compilation fails and make compilation error
[platform/upstream/glib.git] / gio / gdummyfile.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include <config.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32
33 #include "gdummyfile.h"
34
35 #include "gioalias.h"
36
37 static void g_dummy_file_file_iface_init (GFileIface *iface);
38
39 typedef struct {
40   char *scheme;
41   char *userinfo;
42   char *host;
43   int port; /* -1 => not in uri */
44   char *path;
45   char *query;
46   char *fragment;
47 } GDecodedUri;
48
49 struct _GDummyFile
50 {
51   GObject parent_instance;
52
53   GDecodedUri *decoded_uri;
54   char *text_uri;
55 };
56
57 #define g_dummy_file_get_type _g_dummy_file_get_type
58 G_DEFINE_TYPE_WITH_CODE (GDummyFile, g_dummy_file, G_TYPE_OBJECT,
59                          G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
60                                                 g_dummy_file_file_iface_init))
61
62 #define SUB_DELIM_CHARS  "!$&'()*+,;="
63
64 static char *       _g_encode_uri       (GDecodedUri *decoded);
65 static void         _g_decoded_uri_free (GDecodedUri *decoded);
66 static GDecodedUri *_g_decode_uri       (const char  *uri);
67 static GDecodedUri *_g_decoded_uri_new  (void);
68
69 static char * unescape_string (const gchar *escaped_string,
70                                const gchar *escaped_string_end,
71                                const gchar *illegal_characters);
72
73 static void g_string_append_encoded (GString    *string, 
74                                      const char *encoded,
75                                      const char *reserved_chars_allowed);
76
77 static void
78 g_dummy_file_finalize (GObject *object)
79 {
80   GDummyFile *dummy;
81
82   dummy = G_DUMMY_FILE (object);
83
84   if (dummy->decoded_uri)
85     _g_decoded_uri_free (dummy->decoded_uri);
86   
87   g_free (dummy->text_uri);
88   
89   if (G_OBJECT_CLASS (g_dummy_file_parent_class)->finalize)
90     (*G_OBJECT_CLASS (g_dummy_file_parent_class)->finalize) (object);
91 }
92
93 static void
94 g_dummy_file_class_init (GDummyFileClass *klass)
95 {
96   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
97
98   gobject_class->finalize = g_dummy_file_finalize;
99 }
100
101 static void
102 g_dummy_file_init (GDummyFile *dummy)
103 {
104 }
105
106 /**
107  * g_dummy_file_new:
108  * @uri: Universal Resource Identifier for the dummy file object.
109  * 
110  * Returns: a new #GFile. 
111  **/
112 GFile *
113 _g_dummy_file_new (const char *uri)
114 {
115   GDummyFile *dummy;
116
117   g_return_val_if_fail (uri != NULL, NULL);
118
119   dummy = g_object_new (G_TYPE_DUMMY_FILE, NULL);
120   dummy->text_uri = g_strdup (uri);
121   dummy->decoded_uri = _g_decode_uri (uri);
122   
123   return G_FILE (dummy);
124 }
125
126 static gboolean
127 g_dummy_file_is_native (GFile *file)
128 {
129   return FALSE;
130 }
131
132 static char *
133 g_dummy_file_get_basename (GFile *file)
134 {
135   GDummyFile *dummy = G_DUMMY_FILE (file);
136   
137   if (dummy->decoded_uri)
138     return g_path_get_basename (dummy->decoded_uri->path);
139   return g_strdup (dummy->text_uri);
140 }
141
142 static char *
143 g_dummy_file_get_path (GFile *file)
144 {
145   GDummyFile *dummy = G_DUMMY_FILE (file);
146
147   if (dummy->decoded_uri)
148     return g_strdup (dummy->decoded_uri->path);
149   return NULL;
150 }
151
152 static char *
153 g_dummy_file_get_uri (GFile *file)
154 {
155   return g_strdup (G_DUMMY_FILE (file)->text_uri);
156 }
157
158 static char *
159 g_dummy_file_get_parse_name (GFile *file)
160 {
161   return g_strdup (G_DUMMY_FILE (file)->text_uri);
162 }
163
164 static GFile *
165 g_dummy_file_get_parent (GFile *file)
166 {
167   GDummyFile *dummy = G_DUMMY_FILE (file);
168   GFile *parent;
169   char *dirname;
170   char *uri;
171   GDecodedUri new_decoded_uri;
172
173   if (dummy->decoded_uri == NULL)
174     return NULL;
175
176   dirname = g_path_get_dirname (dummy->decoded_uri->path);
177   
178   if (strcmp (dirname, ".") == 0)
179     {
180       g_free (dirname);
181       return NULL;
182     }
183   
184   new_decoded_uri = *dummy->decoded_uri;
185   new_decoded_uri.path = dirname;
186   uri = _g_encode_uri (&new_decoded_uri);
187   g_free (dirname);
188   
189   parent = _g_dummy_file_new (uri);
190   g_free (uri);
191   
192   return parent;
193 }
194
195 static GFile *
196 g_dummy_file_dup (GFile *file)
197 {
198   GDummyFile *dummy = G_DUMMY_FILE (file);
199
200   return _g_dummy_file_new (dummy->text_uri);
201 }
202
203 static guint
204 g_dummy_file_hash (GFile *file)
205 {
206   GDummyFile *dummy = G_DUMMY_FILE (file);
207   
208   return g_str_hash (dummy->text_uri);
209 }
210
211 static gboolean
212 g_dummy_file_equal (GFile *file1,
213                     GFile *file2)
214 {
215   GDummyFile *dummy1 = G_DUMMY_FILE (file1);
216   GDummyFile *dummy2 = G_DUMMY_FILE (file2);
217
218   return g_str_equal (dummy1->text_uri, dummy2->text_uri);
219 }
220
221 static int
222 safe_strcmp (const char *a, 
223              const char *b)
224 {
225   if (a == NULL)
226     a = "";
227   if (b == NULL)
228     b = "";
229
230   return strcmp (a, b);
231 }
232
233 static gboolean
234 uri_same_except_path (GDecodedUri *a,
235                       GDecodedUri *b)
236 {
237   if (safe_strcmp (a->scheme, b->scheme) != 0)
238     return FALSE;
239   if (safe_strcmp (a->userinfo, b->userinfo) != 0)
240     return FALSE;
241   if (safe_strcmp (a->host, b->host) != 0)
242     return FALSE;
243   if (a->port != b->port)
244     return FALSE;
245
246   return TRUE;
247 }
248
249 static const char *
250 match_prefix (const char *path, 
251               const char *prefix)
252 {
253   int prefix_len;
254
255   prefix_len = strlen (prefix);
256   if (strncmp (path, prefix, prefix_len) != 0)
257     return NULL;
258   return path + prefix_len;
259 }
260
261 static gboolean
262 g_dummy_file_contains_file (GFile *parent,
263                             GFile *descendant)
264 {
265   GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
266   GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
267   const char *remainder;
268
269   if (parent_dummy->decoded_uri != NULL &&
270       descendant_dummy->decoded_uri != NULL)
271     {
272       if (uri_same_except_path (parent_dummy->decoded_uri,
273                                 descendant_dummy->decoded_uri)) 
274         {
275           remainder = match_prefix (descendant_dummy->decoded_uri->path,
276                                     parent_dummy->decoded_uri->path);
277           if (remainder != NULL && *remainder == '/')
278             {
279               while (*remainder == '/')
280                 remainder++;
281               if (*remainder != 0)
282                 return TRUE;
283             }
284         }
285     }
286   else
287     {
288       remainder = match_prefix (descendant_dummy->text_uri,
289                                 parent_dummy->text_uri);
290       if (remainder != NULL && *remainder == '/')
291           {
292             while (*remainder == '/')
293               remainder++;
294             if (*remainder != 0)
295               return TRUE;
296           }
297     }
298   
299   return FALSE;
300 }
301
302 static char *
303 g_dummy_file_get_relative_path (GFile *parent,
304                                 GFile *descendant)
305 {
306   GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
307   GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
308   const char *remainder;
309
310   if (parent_dummy->decoded_uri != NULL &&
311       descendant_dummy->decoded_uri != NULL)
312     {
313       if (uri_same_except_path (parent_dummy->decoded_uri,
314                                 descendant_dummy->decoded_uri)) 
315         {
316           remainder = match_prefix (descendant_dummy->decoded_uri->path,
317                                     parent_dummy->decoded_uri->path);
318           if (remainder != NULL && *remainder == '/')
319             {
320               while (*remainder == '/')
321                 remainder++;
322               if (*remainder != 0)
323                 return g_strdup (remainder);
324             }
325         }
326     }
327   else
328     {
329       remainder = match_prefix (descendant_dummy->text_uri,
330                                 parent_dummy->text_uri);
331       if (remainder != NULL && *remainder == '/')
332           {
333             while (*remainder == '/')
334               remainder++;
335             if (*remainder != 0)
336               return unescape_string (remainder, NULL, "/");
337           }
338     }
339   
340   return NULL;
341 }
342
343
344 static GFile *
345 g_dummy_file_resolve_relative_path (GFile      *file,
346                                     const char *relative_path)
347 {
348   GDummyFile *dummy = G_DUMMY_FILE (file);
349   GFile *child;
350   char *uri;
351   GDecodedUri new_decoded_uri;
352   GString *str;
353
354   if (dummy->decoded_uri == NULL)
355     {
356       str = g_string_new (dummy->text_uri);
357       g_string_append (str, "/");
358       g_string_append_encoded (str, relative_path, SUB_DELIM_CHARS ":@/");
359       child = _g_dummy_file_new (str->str);
360       g_string_free (str, TRUE);
361     }
362   else
363     {
364       new_decoded_uri = *dummy->decoded_uri;
365       
366       if (g_path_is_absolute (relative_path))
367         new_decoded_uri.path = g_strdup (relative_path);
368       else
369         new_decoded_uri.path = g_build_filename (new_decoded_uri.path, relative_path, NULL);
370       
371       uri = _g_encode_uri (&new_decoded_uri);
372       g_free (new_decoded_uri.path);
373       
374       child = _g_dummy_file_new (uri);
375       g_free (uri);
376     }
377
378   return child;
379 }
380
381 static GFile *
382 g_dummy_file_get_child_for_display_name (GFile        *file,
383                                          const char   *display_name,
384                                          GError      **error)
385 {
386   return g_file_get_child (file, display_name);
387 }
388
389 static gboolean
390 g_dummy_file_has_uri_scheme (GFile *file,
391                              const char *uri_scheme)
392 {
393   GDummyFile *dummy = G_DUMMY_FILE (file);
394   
395   if (dummy->decoded_uri)
396     return g_ascii_strcasecmp (uri_scheme, dummy->decoded_uri->scheme) == 0;
397   return FALSE;
398 }
399
400 static char *
401 g_dummy_file_get_uri_scheme (GFile *file)
402 {
403   GDummyFile *dummy = G_DUMMY_FILE (file);
404
405   if (dummy->decoded_uri)
406     return g_strdup (dummy->decoded_uri->scheme);
407     
408   return NULL;
409 }
410
411
412 static void
413 g_dummy_file_file_iface_init (GFileIface *iface)
414 {
415   iface->dup = g_dummy_file_dup;
416   iface->hash = g_dummy_file_hash;
417   iface->equal = g_dummy_file_equal;
418   iface->is_native = g_dummy_file_is_native;
419   iface->has_uri_scheme = g_dummy_file_has_uri_scheme;
420   iface->get_uri_scheme = g_dummy_file_get_uri_scheme;
421   iface->get_basename = g_dummy_file_get_basename;
422   iface->get_path = g_dummy_file_get_path;
423   iface->get_uri = g_dummy_file_get_uri;
424   iface->get_parse_name = g_dummy_file_get_parse_name;
425   iface->get_parent = g_dummy_file_get_parent;
426   iface->contains_file = g_dummy_file_contains_file;
427   iface->get_relative_path = g_dummy_file_get_relative_path;
428   iface->resolve_relative_path = g_dummy_file_resolve_relative_path;
429   iface->get_child_for_display_name = g_dummy_file_get_child_for_display_name;
430 }
431
432 /* Uri handling helper functions: */
433
434 static int
435 unescape_character (const char *scanner)
436 {
437   int first_digit;
438   int second_digit;
439   
440   first_digit = g_ascii_xdigit_value (*scanner++);
441   if (first_digit < 0)
442     return -1;
443
444   second_digit = g_ascii_xdigit_value (*scanner++);
445   if (second_digit < 0)
446     return -1;
447
448   return (first_digit << 4) | second_digit;
449 }
450
451 static char *
452 unescape_string (const gchar *escaped_string,
453                  const gchar *escaped_string_end,
454                  const gchar *illegal_characters)
455 {
456   const gchar *in;
457   gchar *out, *result;
458   gint character;
459   
460   if (escaped_string == NULL)
461     return NULL;
462
463   if (escaped_string_end == NULL)
464     escaped_string_end = escaped_string + strlen (escaped_string);
465   
466   result = g_malloc (escaped_string_end - escaped_string + 1);
467         
468   out = result;
469   for (in = escaped_string; in < escaped_string_end; in++) 
470     {
471       character = *in;
472       if (*in == '%') 
473         {
474           in++;
475           if (escaped_string_end - in < 2)
476             {
477               g_free (result);
478               return NULL;
479             }
480       
481           character = unescape_character (in);
482       
483           /* Check for an illegal character. We consider '\0' illegal here. */
484           if (character <= 0 ||
485               (illegal_characters != NULL &&
486                strchr (illegal_characters, (char)character) != NULL))
487             {
488               g_free (result);
489               return NULL;
490             }
491           in++; /* The other char will be eaten in the loop header */
492         }
493       *out++ = (char)character;
494     }
495   
496   *out = '\0';
497   g_assert (out - result <= strlen (escaped_string));
498   return result;
499 }
500
501 void
502 _g_decoded_uri_free (GDecodedUri *decoded)
503 {
504   if (decoded == NULL)
505     return;
506
507   g_free (decoded->scheme);
508   g_free (decoded->query);
509   g_free (decoded->fragment);
510   g_free (decoded->userinfo);
511   g_free (decoded->host);
512   g_free (decoded->path);
513   g_free (decoded);
514 }
515
516 GDecodedUri *
517 _g_decoded_uri_new (void)
518 {
519   GDecodedUri *uri;
520
521   uri = g_new0 (GDecodedUri, 1);
522   uri->port = -1;
523
524   return uri;
525 }
526
527 GDecodedUri *
528 _g_decode_uri (const char *uri)
529 {
530   GDecodedUri *decoded;
531   const char *p, *in, *hier_part_start, *hier_part_end, *query_start, *fragment_start;
532   char *out;
533   char c;
534
535   /* From RFC 3986 Decodes:
536    * URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
537    */ 
538
539   p = uri;
540   
541   /* Decode scheme:
542      scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
543   */
544
545   if (!g_ascii_isalpha (*p))
546     return NULL;
547
548   while (1)
549     {
550       c = *p++;
551
552       if (c == ':')
553         break;
554       
555       if (!(g_ascii_isalnum(c) ||
556             c == '+' ||
557             c == '-' ||
558             c == '.'))
559         return NULL;
560     }
561
562   decoded = _g_decoded_uri_new ();
563   
564   decoded->scheme = g_malloc (p - uri);
565   out = decoded->scheme;
566   for (in = uri; in < p - 1; in++)
567     *out++ = g_ascii_tolower (*in);
568   *out = 0;
569
570   hier_part_start = p;
571
572   query_start = strchr (p, '?');
573   if (query_start)
574     {
575       hier_part_end = query_start++;
576       fragment_start = strchr (query_start, '#');
577       if (fragment_start)
578         {
579           decoded->query = g_strndup (query_start, fragment_start - query_start);
580           decoded->fragment = g_strdup (fragment_start+1);
581         }
582       else
583         {
584           decoded->query = g_strdup (query_start);
585           decoded->fragment = NULL;
586         }
587     }
588   else
589     {
590       /* No query */
591       decoded->query = NULL;
592       fragment_start = strchr (p, '#');
593       if (fragment_start)
594         {
595           hier_part_end = fragment_start++;
596           decoded->fragment = g_strdup (fragment_start);
597         }
598       else
599         {
600           hier_part_end = p + strlen (p);
601           decoded->fragment = NULL;
602         }
603     }
604
605   /*  3:
606       hier-part   = "//" authority path-abempty
607                   / path-absolute
608                   / path-rootless
609                   / path-empty
610
611   */
612
613   if (hier_part_start[0] == '/' &&
614       hier_part_start[1] == '/')
615     {
616       const char *authority_start, *authority_end;
617       const char *userinfo_start, *userinfo_end;
618       const char *host_start, *host_end;
619       const char *port_start;
620       
621       authority_start = hier_part_start + 2;
622       /* authority is always followed by / or nothing */
623       authority_end = memchr (authority_start, '/', hier_part_end - authority_start);
624       if (authority_end == NULL)
625         authority_end = hier_part_end;
626
627       /* 3.2:
628               authority   = [ userinfo "@" ] host [ ":" port ]
629       */
630
631       userinfo_end = memchr (authority_start, '@', authority_end - authority_start);
632       if (userinfo_end)
633         {
634           userinfo_start = authority_start;
635           decoded->userinfo = unescape_string (userinfo_start, userinfo_end, NULL);
636           if (decoded->userinfo == NULL)
637             {
638               _g_decoded_uri_free (decoded);
639               return NULL;
640             }
641           host_start = userinfo_end + 1;
642         }
643       else
644         host_start = authority_start;
645
646       port_start = memchr (host_start, ':', authority_end - host_start);
647       if (port_start)
648         {
649           host_end = port_start++;
650
651           decoded->port = atoi(port_start);
652         }
653       else
654         {
655           host_end = authority_end;
656           decoded->port = -1;
657         }
658
659       decoded->host = g_strndup (host_start, host_end - host_start);
660
661       hier_part_start = authority_end;
662     }
663
664   decoded->path = unescape_string (hier_part_start, hier_part_end, "/");
665
666   if (decoded->path == NULL)
667     {
668       _g_decoded_uri_free (decoded);
669       return NULL;
670     }
671   
672   return decoded;
673 }
674
675 static gboolean
676 is_valid (char c, const char *reserved_chars_allowed)
677 {
678   if (g_ascii_isalnum (c) ||
679       c == '-' ||
680       c == '.' ||
681       c == '_' ||
682       c == '~')
683     return TRUE;
684
685   if (reserved_chars_allowed &&
686       strchr (reserved_chars_allowed, c) != NULL)
687     return TRUE;
688   
689   return FALSE;
690 }
691
692 static void
693 g_string_append_encoded (GString    *string, 
694                          const char *encoded,
695                          const char *reserved_chars_allowed)
696 {
697   unsigned char c;
698   const char *end;
699   static const gchar hex[16] = "0123456789ABCDEF";
700
701   end = encoded + strlen (encoded);
702   
703   while ((c = *encoded) != 0)
704     {
705       if (is_valid (c, reserved_chars_allowed))
706         {
707           g_string_append_c (string, c);
708           encoded++;
709         }
710       else
711         {
712           g_string_append_c (string, '%');
713           g_string_append_c (string, hex[((guchar)c) >> 4]);
714           g_string_append_c (string, hex[((guchar)c) & 0xf]);
715           encoded++;
716         }
717     }
718 }
719
720 static char *
721 _g_encode_uri (GDecodedUri *decoded)
722 {
723   GString *uri;
724
725   uri = g_string_new (NULL);
726
727   g_string_append (uri, decoded->scheme);
728   g_string_append (uri, "://");
729
730   if (decoded->host != NULL)
731     {
732       if (decoded->userinfo)
733         {
734           /* userinfo    = *( unreserved / pct-encoded / sub-delims / ":" ) */
735           g_string_append_encoded (uri, decoded->userinfo, SUB_DELIM_CHARS ":");
736           g_string_append_c (uri, '@');
737         }
738       
739       g_string_append (uri, decoded->host);
740       
741       if (decoded->port != -1)
742         {
743           g_string_append_c (uri, ':');
744           g_string_append_printf (uri, "%d", decoded->port);
745         }
746     }
747
748   g_string_append_encoded (uri, decoded->path, SUB_DELIM_CHARS ":@/");
749   
750   if (decoded->query)
751     {
752       g_string_append_c (uri, '?');
753       g_string_append (uri, decoded->query);
754     }
755     
756   if (decoded->fragment)
757     {
758       g_string_append_c (uri, '#');
759       g_string_append (uri, decoded->fragment);
760     }
761
762   return g_string_free (uri, FALSE);
763 }