Fix a lot of clang complaints
[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 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33 #include <stdlib.h>
34
35 #include "gdummyfile.h"
36 #include "gfile.h"
37
38 #include "gioalias.h"
39
40 static void g_dummy_file_file_iface_init (GFileIface *iface);
41
42 typedef struct {
43   char *scheme;
44   char *userinfo;
45   char *host;
46   int port; /* -1 => not in uri */
47   char *path;
48   char *query;
49   char *fragment;
50 } GDecodedUri;
51
52 struct _GDummyFile
53 {
54   GObject parent_instance;
55
56   GDecodedUri *decoded_uri;
57   char *text_uri;
58 };
59
60 #define g_dummy_file_get_type _g_dummy_file_get_type
61 G_DEFINE_TYPE_WITH_CODE (GDummyFile, g_dummy_file, G_TYPE_OBJECT,
62                          G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
63                                                 g_dummy_file_file_iface_init))
64
65 #define SUB_DELIM_CHARS  "!$&'()*+,;="
66
67 static char *       _g_encode_uri       (GDecodedUri *decoded);
68 static void         _g_decoded_uri_free (GDecodedUri *decoded);
69 static GDecodedUri *_g_decode_uri       (const char  *uri);
70 static GDecodedUri *_g_decoded_uri_new  (void);
71
72 static char * unescape_string (const gchar *escaped_string,
73                                const gchar *escaped_string_end,
74                                const gchar *illegal_characters);
75
76 static void g_string_append_encoded (GString    *string, 
77                                      const char *encoded,
78                                      const char *reserved_chars_allowed);
79
80 static void
81 g_dummy_file_finalize (GObject *object)
82 {
83   GDummyFile *dummy;
84
85   dummy = G_DUMMY_FILE (object);
86
87   if (dummy->decoded_uri)
88     _g_decoded_uri_free (dummy->decoded_uri);
89   
90   g_free (dummy->text_uri);
91
92   G_OBJECT_CLASS (g_dummy_file_parent_class)->finalize (object);
93 }
94
95 static void
96 g_dummy_file_class_init (GDummyFileClass *klass)
97 {
98   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
99
100   gobject_class->finalize = g_dummy_file_finalize;
101 }
102
103 static void
104 g_dummy_file_init (GDummyFile *dummy)
105 {
106 }
107
108 /**
109  * g_dummy_file_new:
110  * @uri: Universal Resource Identifier for the dummy file object.
111  * 
112  * Returns: a new #GFile. 
113  **/
114 GFile *
115 _g_dummy_file_new (const char *uri)
116 {
117   GDummyFile *dummy;
118
119   g_return_val_if_fail (uri != NULL, NULL);
120
121   dummy = g_object_new (G_TYPE_DUMMY_FILE, NULL);
122   dummy->text_uri = g_strdup (uri);
123   dummy->decoded_uri = _g_decode_uri (uri);
124   
125   return G_FILE (dummy);
126 }
127
128 static gboolean
129 g_dummy_file_is_native (GFile *file)
130 {
131   return FALSE;
132 }
133
134 static char *
135 g_dummy_file_get_basename (GFile *file)
136 {
137   GDummyFile *dummy = G_DUMMY_FILE (file);
138   
139   if (dummy->decoded_uri)
140     return g_path_get_basename (dummy->decoded_uri->path);
141   return g_strdup (dummy->text_uri);
142 }
143
144 static char *
145 g_dummy_file_get_path (GFile *file)
146 {
147   return NULL;
148 }
149
150 static char *
151 g_dummy_file_get_uri (GFile *file)
152 {
153   return g_strdup (G_DUMMY_FILE (file)->text_uri);
154 }
155
156 static char *
157 g_dummy_file_get_parse_name (GFile *file)
158 {
159   return g_strdup (G_DUMMY_FILE (file)->text_uri);
160 }
161
162 static GFile *
163 g_dummy_file_get_parent (GFile *file)
164 {
165   GDummyFile *dummy = G_DUMMY_FILE (file);
166   GFile *parent;
167   char *dirname;
168   char *uri;
169   GDecodedUri new_decoded_uri;
170
171   if (dummy->decoded_uri == NULL ||
172       g_strcmp0 (dummy->decoded_uri->path, "/") == 0)
173     return NULL;
174
175   dirname = g_path_get_dirname (dummy->decoded_uri->path);
176   
177   if (strcmp (dirname, ".") == 0)
178     {
179       g_free (dirname);
180       return NULL;
181     }
182   
183   new_decoded_uri = *dummy->decoded_uri;
184   new_decoded_uri.path = dirname;
185   uri = _g_encode_uri (&new_decoded_uri);
186   g_free (dirname);
187   
188   parent = _g_dummy_file_new (uri);
189   g_free (uri);
190   
191   return parent;
192 }
193
194 static GFile *
195 g_dummy_file_dup (GFile *file)
196 {
197   GDummyFile *dummy = G_DUMMY_FILE (file);
198
199   return _g_dummy_file_new (dummy->text_uri);
200 }
201
202 static guint
203 g_dummy_file_hash (GFile *file)
204 {
205   GDummyFile *dummy = G_DUMMY_FILE (file);
206   
207   return g_str_hash (dummy->text_uri);
208 }
209
210 static gboolean
211 g_dummy_file_equal (GFile *file1,
212                     GFile *file2)
213 {
214   GDummyFile *dummy1 = G_DUMMY_FILE (file1);
215   GDummyFile *dummy2 = G_DUMMY_FILE (file2);
216
217   return g_str_equal (dummy1->text_uri, dummy2->text_uri);
218 }
219
220 static int
221 safe_strcmp (const char *a, 
222              const char *b)
223 {
224   if (a == NULL)
225     a = "";
226   if (b == NULL)
227     b = "";
228
229   return strcmp (a, b);
230 }
231
232 static gboolean
233 uri_same_except_path (GDecodedUri *a,
234                       GDecodedUri *b)
235 {
236   if (safe_strcmp (a->scheme, b->scheme) != 0)
237     return FALSE;
238   if (safe_strcmp (a->userinfo, b->userinfo) != 0)
239     return FALSE;
240   if (safe_strcmp (a->host, b->host) != 0)
241     return FALSE;
242   if (a->port != b->port)
243     return FALSE;
244
245   return TRUE;
246 }
247
248 static const char *
249 match_prefix (const char *path, 
250               const char *prefix)
251 {
252   int prefix_len;
253
254   prefix_len = strlen (prefix);
255   if (strncmp (path, prefix, prefix_len) != 0)
256     return NULL;
257   return path + prefix_len;
258 }
259
260 static gboolean
261 g_dummy_file_prefix_matches (GFile *parent, GFile *descendant)
262 {
263   GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
264   GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
265   const char *remainder;
266
267   if (parent_dummy->decoded_uri != NULL &&
268       descendant_dummy->decoded_uri != NULL)
269     {
270       if (uri_same_except_path (parent_dummy->decoded_uri,
271                                 descendant_dummy->decoded_uri)) 
272         {
273           remainder = match_prefix (descendant_dummy->decoded_uri->path,
274                                     parent_dummy->decoded_uri->path);
275           if (remainder != NULL && *remainder == '/')
276             {
277               while (*remainder == '/')
278                 remainder++;
279               if (*remainder != 0)
280                 return TRUE;
281             }
282         }
283     }
284   else
285     {
286       remainder = match_prefix (descendant_dummy->text_uri,
287                                 parent_dummy->text_uri);
288       if (remainder != NULL && *remainder == '/')
289           {
290             while (*remainder == '/')
291               remainder++;
292             if (*remainder != 0)
293               return TRUE;
294           }
295     }
296   
297   return FALSE;
298 }
299
300 static char *
301 g_dummy_file_get_relative_path (GFile *parent,
302                                 GFile *descendant)
303 {
304   GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
305   GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
306   const char *remainder;
307
308   if (parent_dummy->decoded_uri != NULL &&
309       descendant_dummy->decoded_uri != NULL)
310     {
311       if (uri_same_except_path (parent_dummy->decoded_uri,
312                                 descendant_dummy->decoded_uri)) 
313         {
314           remainder = match_prefix (descendant_dummy->decoded_uri->path,
315                                     parent_dummy->decoded_uri->path);
316           if (remainder != NULL && *remainder == '/')
317             {
318               while (*remainder == '/')
319                 remainder++;
320               if (*remainder != 0)
321                 return g_strdup (remainder);
322             }
323         }
324     }
325   else
326     {
327       remainder = match_prefix (descendant_dummy->text_uri,
328                                 parent_dummy->text_uri);
329       if (remainder != NULL && *remainder == '/')
330           {
331             while (*remainder == '/')
332               remainder++;
333             if (*remainder != 0)
334               return unescape_string (remainder, NULL, "/");
335           }
336     }
337   
338   return NULL;
339 }
340
341
342 static GFile *
343 g_dummy_file_resolve_relative_path (GFile      *file,
344                                     const char *relative_path)
345 {
346   GDummyFile *dummy = G_DUMMY_FILE (file);
347   GFile *child;
348   char *uri;
349   GDecodedUri new_decoded_uri;
350   GString *str;
351
352   if (dummy->decoded_uri == NULL)
353     {
354       str = g_string_new (dummy->text_uri);
355       g_string_append (str, "/");
356       g_string_append_encoded (str, relative_path, SUB_DELIM_CHARS ":@/");
357       child = _g_dummy_file_new (str->str);
358       g_string_free (str, TRUE);
359     }
360   else
361     {
362       new_decoded_uri = *dummy->decoded_uri;
363       
364       if (g_path_is_absolute (relative_path))
365         new_decoded_uri.path = g_strdup (relative_path);
366       else
367         new_decoded_uri.path = g_build_filename (new_decoded_uri.path, relative_path, NULL);
368       
369       uri = _g_encode_uri (&new_decoded_uri);
370       g_free (new_decoded_uri.path);
371       
372       child = _g_dummy_file_new (uri);
373       g_free (uri);
374     }
375
376   return child;
377 }
378
379 static GFile *
380 g_dummy_file_get_child_for_display_name (GFile        *file,
381                                          const char   *display_name,
382                                          GError      **error)
383 {
384   return g_file_get_child (file, display_name);
385 }
386
387 static gboolean
388 g_dummy_file_has_uri_scheme (GFile *file,
389                              const char *uri_scheme)
390 {
391   GDummyFile *dummy = G_DUMMY_FILE (file);
392   
393   if (dummy->decoded_uri)
394     return g_ascii_strcasecmp (uri_scheme, dummy->decoded_uri->scheme) == 0;
395   return FALSE;
396 }
397
398 static char *
399 g_dummy_file_get_uri_scheme (GFile *file)
400 {
401   GDummyFile *dummy = G_DUMMY_FILE (file);
402
403   if (dummy->decoded_uri)
404     return g_strdup (dummy->decoded_uri->scheme);
405     
406   return NULL;
407 }
408
409
410 static void
411 g_dummy_file_file_iface_init (GFileIface *iface)
412 {
413   iface->dup = g_dummy_file_dup;
414   iface->hash = g_dummy_file_hash;
415   iface->equal = g_dummy_file_equal;
416   iface->is_native = g_dummy_file_is_native;
417   iface->has_uri_scheme = g_dummy_file_has_uri_scheme;
418   iface->get_uri_scheme = g_dummy_file_get_uri_scheme;
419   iface->get_basename = g_dummy_file_get_basename;
420   iface->get_path = g_dummy_file_get_path;
421   iface->get_uri = g_dummy_file_get_uri;
422   iface->get_parse_name = g_dummy_file_get_parse_name;
423   iface->get_parent = g_dummy_file_get_parent;
424   iface->prefix_matches = g_dummy_file_prefix_matches;
425   iface->get_relative_path = g_dummy_file_get_relative_path;
426   iface->resolve_relative_path = g_dummy_file_resolve_relative_path;
427   iface->get_child_for_display_name = g_dummy_file_get_child_for_display_name;
428
429   iface->supports_thread_contexts = TRUE;
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_warn_if_fail (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   static const gchar hex[16] = "0123456789ABCDEF";
699
700   while ((c = *encoded) != 0)
701     {
702       if (is_valid (c, reserved_chars_allowed))
703         {
704           g_string_append_c (string, c);
705           encoded++;
706         }
707       else
708         {
709           g_string_append_c (string, '%');
710           g_string_append_c (string, hex[((guchar)c) >> 4]);
711           g_string_append_c (string, hex[((guchar)c) & 0xf]);
712           encoded++;
713         }
714     }
715 }
716
717 static char *
718 _g_encode_uri (GDecodedUri *decoded)
719 {
720   GString *uri;
721
722   uri = g_string_new (NULL);
723
724   g_string_append (uri, decoded->scheme);
725   g_string_append (uri, "://");
726
727   if (decoded->host != NULL)
728     {
729       if (decoded->userinfo)
730         {
731           /* userinfo    = *( unreserved / pct-encoded / sub-delims / ":" ) */
732           g_string_append_encoded (uri, decoded->userinfo, SUB_DELIM_CHARS ":");
733           g_string_append_c (uri, '@');
734         }
735       
736       g_string_append (uri, decoded->host);
737       
738       if (decoded->port != -1)
739         {
740           g_string_append_c (uri, ':');
741           g_string_append_printf (uri, "%d", decoded->port);
742         }
743     }
744
745   g_string_append_encoded (uri, decoded->path, SUB_DELIM_CHARS ":@/");
746   
747   if (decoded->query)
748     {
749       g_string_append_c (uri, '?');
750       g_string_append (uri, decoded->query);
751     }
752     
753   if (decoded->fragment)
754     {
755       g_string_append_c (uri, '#');
756       g_string_append (uri, decoded->fragment);
757     }
758
759   return g_string_free (uri, FALSE);
760 }