1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
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.
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.
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.
20 * Author: Alexander Larsson <alexl@redhat.com>
25 #include <sys/types.h>
32 #include "gdummyfile.h"
36 static void g_dummy_file_file_iface_init (GFileIface
*iface
);
42 int port
; /* -1 => not in uri */
50 GObject parent_instance
;
52 GDecodedUri
*decoded_uri
;
56 #define g_dummy_file_get_type _g_dummy_file_get_type
57 G_DEFINE_TYPE_WITH_CODE (GDummyFile
, g_dummy_file
, G_TYPE_OBJECT
,
58 G_IMPLEMENT_INTERFACE (G_TYPE_FILE
,
59 g_dummy_file_file_iface_init
))
61 #define SUB_DELIM_CHARS "!$&'()*+,;="
63 static char * _g_encode_uri (GDecodedUri
*decoded
);
64 static void _g_decoded_uri_free (GDecodedUri
*decoded
);
65 static GDecodedUri
*_g_decode_uri (const char *uri
);
66 static GDecodedUri
*_g_decoded_uri_new (void);
68 static char * unescape_string (const gchar
*escaped_string
,
69 const gchar
*escaped_string_end
,
70 const gchar
*illegal_characters
);
72 static void g_string_append_encoded (GString
*string
,
74 const char *reserved_chars_allowed
);
77 g_dummy_file_finalize (GObject
*object
)
81 dummy
= G_DUMMY_FILE (object
);
83 if (dummy
->decoded_uri
)
84 _g_decoded_uri_free (dummy
->decoded_uri
);
86 g_free (dummy
->text_uri
);
88 G_OBJECT_CLASS (g_dummy_file_parent_class
)->finalize (object
);
92 g_dummy_file_class_init (GDummyFileClass
*klass
)
94 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
96 gobject_class
->finalize
= g_dummy_file_finalize
;
100 g_dummy_file_init (GDummyFile
*dummy
)
105 _g_dummy_file_new (const char *uri
)
109 g_return_val_if_fail (uri
!= NULL
, NULL
);
111 dummy
= g_object_new (G_TYPE_DUMMY_FILE
, NULL
);
112 dummy
->text_uri
= g_strdup (uri
);
113 dummy
->decoded_uri
= _g_decode_uri (uri
);
115 return G_FILE (dummy
);
119 g_dummy_file_is_native (GFile
*file
)
125 g_dummy_file_get_basename (GFile
*file
)
127 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
129 if (dummy
->decoded_uri
)
130 return g_path_get_basename (dummy
->decoded_uri
->path
);
131 return g_strdup (dummy
->text_uri
);
135 g_dummy_file_get_path (GFile
*file
)
141 g_dummy_file_get_uri (GFile
*file
)
143 return g_strdup (G_DUMMY_FILE (file
)->text_uri
);
147 g_dummy_file_get_parse_name (GFile
*file
)
149 return g_strdup (G_DUMMY_FILE (file
)->text_uri
);
153 g_dummy_file_get_parent (GFile
*file
)
155 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
159 GDecodedUri new_decoded_uri
;
161 if (dummy
->decoded_uri
== NULL
||
162 g_strcmp0 (dummy
->decoded_uri
->path
, "/") == 0)
165 dirname
= g_path_get_dirname (dummy
->decoded_uri
->path
);
167 if (strcmp (dirname
, ".") == 0)
173 new_decoded_uri
= *dummy
->decoded_uri
;
174 new_decoded_uri
.path
= dirname
;
175 uri
= _g_encode_uri (&new_decoded_uri
);
178 parent
= _g_dummy_file_new (uri
);
185 g_dummy_file_dup (GFile
*file
)
187 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
189 return _g_dummy_file_new (dummy
->text_uri
);
193 g_dummy_file_hash (GFile
*file
)
195 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
197 return g_str_hash (dummy
->text_uri
);
201 g_dummy_file_equal (GFile
*file1
,
204 GDummyFile
*dummy1
= G_DUMMY_FILE (file1
);
205 GDummyFile
*dummy2
= G_DUMMY_FILE (file2
);
207 return g_str_equal (dummy1
->text_uri
, dummy2
->text_uri
);
211 safe_strcmp (const char *a
,
219 return strcmp (a
, b
);
223 uri_same_except_path (GDecodedUri
*a
,
226 if (safe_strcmp (a
->scheme
, b
->scheme
) != 0)
228 if (safe_strcmp (a
->userinfo
, b
->userinfo
) != 0)
230 if (safe_strcmp (a
->host
, b
->host
) != 0)
232 if (a
->port
!= b
->port
)
239 match_prefix (const char *path
,
244 prefix_len
= strlen (prefix
);
245 if (strncmp (path
, prefix
, prefix_len
) != 0)
247 return path
+ prefix_len
;
251 g_dummy_file_prefix_matches (GFile
*parent
, GFile
*descendant
)
253 GDummyFile
*parent_dummy
= G_DUMMY_FILE (parent
);
254 GDummyFile
*descendant_dummy
= G_DUMMY_FILE (descendant
);
255 const char *remainder
;
257 if (parent_dummy
->decoded_uri
!= NULL
&&
258 descendant_dummy
->decoded_uri
!= NULL
)
260 if (uri_same_except_path (parent_dummy
->decoded_uri
,
261 descendant_dummy
->decoded_uri
))
263 remainder
= match_prefix (descendant_dummy
->decoded_uri
->path
,
264 parent_dummy
->decoded_uri
->path
);
265 if (remainder
!= NULL
&& *remainder
== '/')
267 while (*remainder
== '/')
276 remainder
= match_prefix (descendant_dummy
->text_uri
,
277 parent_dummy
->text_uri
);
278 if (remainder
!= NULL
&& *remainder
== '/')
280 while (*remainder
== '/')
291 g_dummy_file_get_relative_path (GFile
*parent
,
294 GDummyFile
*parent_dummy
= G_DUMMY_FILE (parent
);
295 GDummyFile
*descendant_dummy
= G_DUMMY_FILE (descendant
);
296 const char *remainder
;
298 if (parent_dummy
->decoded_uri
!= NULL
&&
299 descendant_dummy
->decoded_uri
!= NULL
)
301 if (uri_same_except_path (parent_dummy
->decoded_uri
,
302 descendant_dummy
->decoded_uri
))
304 remainder
= match_prefix (descendant_dummy
->decoded_uri
->path
,
305 parent_dummy
->decoded_uri
->path
);
306 if (remainder
!= NULL
&& *remainder
== '/')
308 while (*remainder
== '/')
311 return g_strdup (remainder
);
317 remainder
= match_prefix (descendant_dummy
->text_uri
,
318 parent_dummy
->text_uri
);
319 if (remainder
!= NULL
&& *remainder
== '/')
321 while (*remainder
== '/')
324 return unescape_string (remainder
, NULL
, "/");
333 g_dummy_file_resolve_relative_path (GFile
*file
,
334 const char *relative_path
)
336 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
339 GDecodedUri new_decoded_uri
;
342 if (dummy
->decoded_uri
== NULL
)
344 str
= g_string_new (dummy
->text_uri
);
345 g_string_append (str
, "/");
346 g_string_append_encoded (str
, relative_path
, SUB_DELIM_CHARS
":@/");
347 child
= _g_dummy_file_new (str
->str
);
348 g_string_free (str
, TRUE
);
352 new_decoded_uri
= *dummy
->decoded_uri
;
354 if (g_path_is_absolute (relative_path
))
355 new_decoded_uri
.path
= g_strdup (relative_path
);
357 new_decoded_uri
.path
= g_build_filename (new_decoded_uri
.path
, relative_path
, NULL
);
359 uri
= _g_encode_uri (&new_decoded_uri
);
360 g_free (new_decoded_uri
.path
);
362 child
= _g_dummy_file_new (uri
);
370 g_dummy_file_get_child_for_display_name (GFile
*file
,
371 const char *display_name
,
374 return g_file_get_child (file
, display_name
);
378 g_dummy_file_has_uri_scheme (GFile
*file
,
379 const char *uri_scheme
)
381 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
383 if (dummy
->decoded_uri
)
384 return g_ascii_strcasecmp (uri_scheme
, dummy
->decoded_uri
->scheme
) == 0;
389 g_dummy_file_get_uri_scheme (GFile
*file
)
391 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
393 if (dummy
->decoded_uri
)
394 return g_strdup (dummy
->decoded_uri
->scheme
);
401 g_dummy_file_file_iface_init (GFileIface
*iface
)
403 iface
->dup
= g_dummy_file_dup
;
404 iface
->hash
= g_dummy_file_hash
;
405 iface
->equal
= g_dummy_file_equal
;
406 iface
->is_native
= g_dummy_file_is_native
;
407 iface
->has_uri_scheme
= g_dummy_file_has_uri_scheme
;
408 iface
->get_uri_scheme
= g_dummy_file_get_uri_scheme
;
409 iface
->get_basename
= g_dummy_file_get_basename
;
410 iface
->get_path
= g_dummy_file_get_path
;
411 iface
->get_uri
= g_dummy_file_get_uri
;
412 iface
->get_parse_name
= g_dummy_file_get_parse_name
;
413 iface
->get_parent
= g_dummy_file_get_parent
;
414 iface
->prefix_matches
= g_dummy_file_prefix_matches
;
415 iface
->get_relative_path
= g_dummy_file_get_relative_path
;
416 iface
->resolve_relative_path
= g_dummy_file_resolve_relative_path
;
417 iface
->get_child_for_display_name
= g_dummy_file_get_child_for_display_name
;
419 iface
->supports_thread_contexts
= TRUE
;
422 /* Uri handling helper functions: */
425 unescape_character (const char *scanner
)
430 first_digit
= g_ascii_xdigit_value (*scanner
++);
434 second_digit
= g_ascii_xdigit_value (*scanner
++);
435 if (second_digit
< 0)
438 return (first_digit
<< 4) | second_digit
;
442 unescape_string (const gchar
*escaped_string
,
443 const gchar
*escaped_string_end
,
444 const gchar
*illegal_characters
)
450 if (escaped_string
== NULL
)
453 if (escaped_string_end
== NULL
)
454 escaped_string_end
= escaped_string
+ strlen (escaped_string
);
456 result
= g_malloc (escaped_string_end
- escaped_string
+ 1);
459 for (in
= escaped_string
; in
< escaped_string_end
; in
++)
465 if (escaped_string_end
- in
< 2)
471 character
= unescape_character (in
);
473 /* Check for an illegal character. We consider '\0' illegal here. */
474 if (character
<= 0 ||
475 (illegal_characters
!= NULL
&&
476 strchr (illegal_characters
, (char)character
) != NULL
))
481 in
++; /* The other char will be eaten in the loop header */
483 *out
++ = (char)character
;
487 g_warn_if_fail (out
- result
<= strlen (escaped_string
));
492 _g_decoded_uri_free (GDecodedUri
*decoded
)
497 g_free (decoded
->scheme
);
498 g_free (decoded
->query
);
499 g_free (decoded
->fragment
);
500 g_free (decoded
->userinfo
);
501 g_free (decoded
->host
);
502 g_free (decoded
->path
);
507 _g_decoded_uri_new (void)
511 uri
= g_new0 (GDecodedUri
, 1);
518 _g_decode_uri (const char *uri
)
520 GDecodedUri
*decoded
;
521 const char *p
, *in
, *hier_part_start
, *hier_part_end
, *query_start
, *fragment_start
;
525 /* From RFC 3986 Decodes:
526 * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
532 scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
535 if (!g_ascii_isalpha (*p
))
545 if (!(g_ascii_isalnum(c
) ||
552 decoded
= _g_decoded_uri_new ();
554 decoded
->scheme
= g_malloc (p
- uri
);
555 out
= decoded
->scheme
;
556 for (in
= uri
; in
< p
- 1; in
++)
557 *out
++ = g_ascii_tolower (*in
);
562 query_start
= strchr (p
, '?');
565 hier_part_end
= query_start
++;
566 fragment_start
= strchr (query_start
, '#');
569 decoded
->query
= g_strndup (query_start
, fragment_start
- query_start
);
570 decoded
->fragment
= g_strdup (fragment_start
+1);
574 decoded
->query
= g_strdup (query_start
);
575 decoded
->fragment
= NULL
;
581 decoded
->query
= NULL
;
582 fragment_start
= strchr (p
, '#');
585 hier_part_end
= fragment_start
++;
586 decoded
->fragment
= g_strdup (fragment_start
);
590 hier_part_end
= p
+ strlen (p
);
591 decoded
->fragment
= NULL
;
596 hier-part = "//" authority path-abempty
603 if (hier_part_start
[0] == '/' &&
604 hier_part_start
[1] == '/')
606 const char *authority_start
, *authority_end
;
607 const char *userinfo_start
, *userinfo_end
;
608 const char *host_start
, *host_end
;
609 const char *port_start
;
611 authority_start
= hier_part_start
+ 2;
612 /* authority is always followed by / or nothing */
613 authority_end
= memchr (authority_start
, '/', hier_part_end
- authority_start
);
614 if (authority_end
== NULL
)
615 authority_end
= hier_part_end
;
618 authority = [ userinfo "@" ] host [ ":" port ]
621 userinfo_end
= memchr (authority_start
, '@', authority_end
- authority_start
);
624 userinfo_start
= authority_start
;
625 decoded
->userinfo
= unescape_string (userinfo_start
, userinfo_end
, NULL
);
626 if (decoded
->userinfo
== NULL
)
628 _g_decoded_uri_free (decoded
);
631 host_start
= userinfo_end
+ 1;
634 host_start
= authority_start
;
636 port_start
= memchr (host_start
, ':', authority_end
- host_start
);
639 host_end
= port_start
++;
641 decoded
->port
= atoi(port_start
);
645 host_end
= authority_end
;
649 decoded
->host
= g_strndup (host_start
, host_end
- host_start
);
651 hier_part_start
= authority_end
;
654 decoded
->path
= unescape_string (hier_part_start
, hier_part_end
, "/");
656 if (decoded
->path
== NULL
)
658 _g_decoded_uri_free (decoded
);
666 is_valid (char c
, const char *reserved_chars_allowed
)
668 if (g_ascii_isalnum (c
) ||
675 if (reserved_chars_allowed
&&
676 strchr (reserved_chars_allowed
, c
) != NULL
)
683 g_string_append_encoded (GString
*string
,
685 const char *reserved_chars_allowed
)
688 static const gchar hex
[16] = "0123456789ABCDEF";
690 while ((c
= *encoded
) != 0)
692 if (is_valid (c
, reserved_chars_allowed
))
694 g_string_append_c (string
, c
);
699 g_string_append_c (string
, '%');
700 g_string_append_c (string
, hex
[((guchar
)c
) >> 4]);
701 g_string_append_c (string
, hex
[((guchar
)c
) & 0xf]);
708 _g_encode_uri (GDecodedUri
*decoded
)
712 uri
= g_string_new (NULL
);
714 g_string_append (uri
, decoded
->scheme
);
715 g_string_append (uri
, "://");
717 if (decoded
->host
!= NULL
)
719 if (decoded
->userinfo
)
721 /* userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) */
722 g_string_append_encoded (uri
, decoded
->userinfo
, SUB_DELIM_CHARS
":");
723 g_string_append_c (uri
, '@');
726 g_string_append (uri
, decoded
->host
);
728 if (decoded
->port
!= -1)
730 g_string_append_c (uri
, ':');
731 g_string_append_printf (uri
, "%d", decoded
->port
);
735 g_string_append_encoded (uri
, decoded
->path
, SUB_DELIM_CHARS
":@/");
739 g_string_append_c (uri
, '?');
740 g_string_append (uri
, decoded
->query
);
743 if (decoded
->fragment
)
745 g_string_append_c (uri
, '#');
746 g_string_append (uri
, decoded
->fragment
);
749 return g_string_free (uri
, FALSE
);