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, see <http://www.gnu.org/licenses/>.
18 * Author: Alexander Larsson <alexl@redhat.com>
23 #include <sys/types.h>
30 #include "gdummyfile.h"
34 static void g_dummy_file_file_iface_init (GFileIface
*iface
);
40 int port
; /* -1 => not in uri */
48 GObject parent_instance
;
50 GDecodedUri
*decoded_uri
;
54 #define g_dummy_file_get_type _g_dummy_file_get_type
55 G_DEFINE_TYPE_WITH_CODE (GDummyFile
, g_dummy_file
, G_TYPE_OBJECT
,
56 G_IMPLEMENT_INTERFACE (G_TYPE_FILE
,
57 g_dummy_file_file_iface_init
))
59 #define SUB_DELIM_CHARS "!$&'()*+,;="
61 static char * _g_encode_uri (GDecodedUri
*decoded
);
62 static void _g_decoded_uri_free (GDecodedUri
*decoded
);
63 static GDecodedUri
*_g_decode_uri (const char *uri
);
64 static GDecodedUri
*_g_decoded_uri_new (void);
66 static char * unescape_string (const gchar
*escaped_string
,
67 const gchar
*escaped_string_end
,
68 const gchar
*illegal_characters
);
70 static void g_string_append_encoded (GString
*string
,
72 const char *reserved_chars_allowed
);
75 g_dummy_file_finalize (GObject
*object
)
79 dummy
= G_DUMMY_FILE (object
);
81 if (dummy
->decoded_uri
)
82 _g_decoded_uri_free (dummy
->decoded_uri
);
84 g_free (dummy
->text_uri
);
86 G_OBJECT_CLASS (g_dummy_file_parent_class
)->finalize (object
);
90 g_dummy_file_class_init (GDummyFileClass
*klass
)
92 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
94 gobject_class
->finalize
= g_dummy_file_finalize
;
98 g_dummy_file_init (GDummyFile
*dummy
)
103 _g_dummy_file_new (const char *uri
)
107 g_return_val_if_fail (uri
!= NULL
, NULL
);
109 dummy
= g_object_new (G_TYPE_DUMMY_FILE
, NULL
);
110 dummy
->text_uri
= g_strdup (uri
);
111 dummy
->decoded_uri
= _g_decode_uri (uri
);
113 return G_FILE (dummy
);
117 g_dummy_file_is_native (GFile
*file
)
123 g_dummy_file_get_basename (GFile
*file
)
125 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
127 if (dummy
->decoded_uri
)
128 return g_path_get_basename (dummy
->decoded_uri
->path
);
129 return g_strdup (dummy
->text_uri
);
133 g_dummy_file_get_path (GFile
*file
)
139 g_dummy_file_get_uri (GFile
*file
)
141 return g_strdup (G_DUMMY_FILE (file
)->text_uri
);
145 g_dummy_file_get_parse_name (GFile
*file
)
147 return g_strdup (G_DUMMY_FILE (file
)->text_uri
);
151 g_dummy_file_get_parent (GFile
*file
)
153 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
157 GDecodedUri new_decoded_uri
;
159 if (dummy
->decoded_uri
== NULL
||
160 g_strcmp0 (dummy
->decoded_uri
->path
, "/") == 0)
163 dirname
= g_path_get_dirname (dummy
->decoded_uri
->path
);
165 if (strcmp (dirname
, ".") == 0)
171 new_decoded_uri
= *dummy
->decoded_uri
;
172 new_decoded_uri
.path
= dirname
;
173 uri
= _g_encode_uri (&new_decoded_uri
);
176 parent
= _g_dummy_file_new (uri
);
183 g_dummy_file_dup (GFile
*file
)
185 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
187 return _g_dummy_file_new (dummy
->text_uri
);
191 g_dummy_file_hash (GFile
*file
)
193 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
195 return g_str_hash (dummy
->text_uri
);
199 g_dummy_file_equal (GFile
*file1
,
202 GDummyFile
*dummy1
= G_DUMMY_FILE (file1
);
203 GDummyFile
*dummy2
= G_DUMMY_FILE (file2
);
205 return g_str_equal (dummy1
->text_uri
, dummy2
->text_uri
);
209 safe_strcmp (const char *a
,
217 return strcmp (a
, b
);
221 uri_same_except_path (GDecodedUri
*a
,
224 if (safe_strcmp (a
->scheme
, b
->scheme
) != 0)
226 if (safe_strcmp (a
->userinfo
, b
->userinfo
) != 0)
228 if (safe_strcmp (a
->host
, b
->host
) != 0)
230 if (a
->port
!= b
->port
)
237 match_prefix (const char *path
,
242 prefix_len
= strlen (prefix
);
243 if (strncmp (path
, prefix
, prefix_len
) != 0)
245 return path
+ prefix_len
;
249 g_dummy_file_prefix_matches (GFile
*parent
, GFile
*descendant
)
251 GDummyFile
*parent_dummy
= G_DUMMY_FILE (parent
);
252 GDummyFile
*descendant_dummy
= G_DUMMY_FILE (descendant
);
253 const char *remainder
;
255 if (parent_dummy
->decoded_uri
!= NULL
&&
256 descendant_dummy
->decoded_uri
!= NULL
)
258 if (uri_same_except_path (parent_dummy
->decoded_uri
,
259 descendant_dummy
->decoded_uri
))
261 remainder
= match_prefix (descendant_dummy
->decoded_uri
->path
,
262 parent_dummy
->decoded_uri
->path
);
263 if (remainder
!= NULL
&& *remainder
== '/')
265 while (*remainder
== '/')
274 remainder
= match_prefix (descendant_dummy
->text_uri
,
275 parent_dummy
->text_uri
);
276 if (remainder
!= NULL
&& *remainder
== '/')
278 while (*remainder
== '/')
289 g_dummy_file_get_relative_path (GFile
*parent
,
292 GDummyFile
*parent_dummy
= G_DUMMY_FILE (parent
);
293 GDummyFile
*descendant_dummy
= G_DUMMY_FILE (descendant
);
294 const char *remainder
;
296 if (parent_dummy
->decoded_uri
!= NULL
&&
297 descendant_dummy
->decoded_uri
!= NULL
)
299 if (uri_same_except_path (parent_dummy
->decoded_uri
,
300 descendant_dummy
->decoded_uri
))
302 remainder
= match_prefix (descendant_dummy
->decoded_uri
->path
,
303 parent_dummy
->decoded_uri
->path
);
304 if (remainder
!= NULL
&& *remainder
== '/')
306 while (*remainder
== '/')
309 return g_strdup (remainder
);
315 remainder
= match_prefix (descendant_dummy
->text_uri
,
316 parent_dummy
->text_uri
);
317 if (remainder
!= NULL
&& *remainder
== '/')
319 while (*remainder
== '/')
322 return unescape_string (remainder
, NULL
, "/");
331 g_dummy_file_resolve_relative_path (GFile
*file
,
332 const char *relative_path
)
334 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
337 GDecodedUri new_decoded_uri
;
340 if (dummy
->decoded_uri
== NULL
)
342 str
= g_string_new (dummy
->text_uri
);
343 g_string_append (str
, "/");
344 g_string_append_encoded (str
, relative_path
, SUB_DELIM_CHARS
":@/");
345 child
= _g_dummy_file_new (str
->str
);
346 g_string_free (str
, TRUE
);
350 new_decoded_uri
= *dummy
->decoded_uri
;
352 if (g_path_is_absolute (relative_path
))
353 new_decoded_uri
.path
= g_strdup (relative_path
);
355 new_decoded_uri
.path
= g_build_filename (new_decoded_uri
.path
, relative_path
, NULL
);
357 uri
= _g_encode_uri (&new_decoded_uri
);
358 g_free (new_decoded_uri
.path
);
360 child
= _g_dummy_file_new (uri
);
368 g_dummy_file_get_child_for_display_name (GFile
*file
,
369 const char *display_name
,
372 return g_file_get_child (file
, display_name
);
376 g_dummy_file_has_uri_scheme (GFile
*file
,
377 const char *uri_scheme
)
379 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
381 if (dummy
->decoded_uri
)
382 return g_ascii_strcasecmp (uri_scheme
, dummy
->decoded_uri
->scheme
) == 0;
387 g_dummy_file_get_uri_scheme (GFile
*file
)
389 GDummyFile
*dummy
= G_DUMMY_FILE (file
);
391 if (dummy
->decoded_uri
)
392 return g_strdup (dummy
->decoded_uri
->scheme
);
399 g_dummy_file_file_iface_init (GFileIface
*iface
)
401 iface
->dup
= g_dummy_file_dup
;
402 iface
->hash
= g_dummy_file_hash
;
403 iface
->equal
= g_dummy_file_equal
;
404 iface
->is_native
= g_dummy_file_is_native
;
405 iface
->has_uri_scheme
= g_dummy_file_has_uri_scheme
;
406 iface
->get_uri_scheme
= g_dummy_file_get_uri_scheme
;
407 iface
->get_basename
= g_dummy_file_get_basename
;
408 iface
->get_path
= g_dummy_file_get_path
;
409 iface
->get_uri
= g_dummy_file_get_uri
;
410 iface
->get_parse_name
= g_dummy_file_get_parse_name
;
411 iface
->get_parent
= g_dummy_file_get_parent
;
412 iface
->prefix_matches
= g_dummy_file_prefix_matches
;
413 iface
->get_relative_path
= g_dummy_file_get_relative_path
;
414 iface
->resolve_relative_path
= g_dummy_file_resolve_relative_path
;
415 iface
->get_child_for_display_name
= g_dummy_file_get_child_for_display_name
;
417 iface
->supports_thread_contexts
= TRUE
;
420 /* Uri handling helper functions: */
423 unescape_character (const char *scanner
)
428 first_digit
= g_ascii_xdigit_value (*scanner
++);
432 second_digit
= g_ascii_xdigit_value (*scanner
++);
433 if (second_digit
< 0)
436 return (first_digit
<< 4) | second_digit
;
440 unescape_string (const gchar
*escaped_string
,
441 const gchar
*escaped_string_end
,
442 const gchar
*illegal_characters
)
448 if (escaped_string
== NULL
)
451 if (escaped_string_end
== NULL
)
452 escaped_string_end
= escaped_string
+ strlen (escaped_string
);
454 result
= g_malloc (escaped_string_end
- escaped_string
+ 1);
457 for (in
= escaped_string
; in
< escaped_string_end
; in
++)
463 if (escaped_string_end
- in
< 2)
469 character
= unescape_character (in
);
471 /* Check for an illegal character. We consider '\0' illegal here. */
472 if (character
<= 0 ||
473 (illegal_characters
!= NULL
&&
474 strchr (illegal_characters
, (char)character
) != NULL
))
479 in
++; /* The other char will be eaten in the loop header */
481 *out
++ = (char)character
;
485 g_warn_if_fail (out
- result
<= strlen (escaped_string
));
490 _g_decoded_uri_free (GDecodedUri
*decoded
)
495 g_free (decoded
->scheme
);
496 g_free (decoded
->query
);
497 g_free (decoded
->fragment
);
498 g_free (decoded
->userinfo
);
499 g_free (decoded
->host
);
500 g_free (decoded
->path
);
505 _g_decoded_uri_new (void)
509 uri
= g_new0 (GDecodedUri
, 1);
516 _g_decode_uri (const char *uri
)
518 GDecodedUri
*decoded
;
519 const char *p
, *in
, *hier_part_start
, *hier_part_end
, *query_start
, *fragment_start
;
523 /* From RFC 3986 Decodes:
524 * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
530 scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
533 if (!g_ascii_isalpha (*p
))
543 if (!(g_ascii_isalnum(c
) ||
550 decoded
= _g_decoded_uri_new ();
552 decoded
->scheme
= g_malloc (p
- uri
);
553 out
= decoded
->scheme
;
554 for (in
= uri
; in
< p
- 1; in
++)
555 *out
++ = g_ascii_tolower (*in
);
560 query_start
= strchr (p
, '?');
563 hier_part_end
= query_start
++;
564 fragment_start
= strchr (query_start
, '#');
567 decoded
->query
= g_strndup (query_start
, fragment_start
- query_start
);
568 decoded
->fragment
= g_strdup (fragment_start
+1);
572 decoded
->query
= g_strdup (query_start
);
573 decoded
->fragment
= NULL
;
579 decoded
->query
= NULL
;
580 fragment_start
= strchr (p
, '#');
583 hier_part_end
= fragment_start
++;
584 decoded
->fragment
= g_strdup (fragment_start
);
588 hier_part_end
= p
+ strlen (p
);
589 decoded
->fragment
= NULL
;
594 hier-part = "//" authority path-abempty
601 if (hier_part_start
[0] == '/' &&
602 hier_part_start
[1] == '/')
604 const char *authority_start
, *authority_end
;
605 const char *userinfo_start
, *userinfo_end
;
606 const char *host_start
, *host_end
;
607 const char *port_start
;
609 authority_start
= hier_part_start
+ 2;
610 /* authority is always followed by / or nothing */
611 authority_end
= memchr (authority_start
, '/', hier_part_end
- authority_start
);
612 if (authority_end
== NULL
)
613 authority_end
= hier_part_end
;
616 authority = [ userinfo "@" ] host [ ":" port ]
619 userinfo_end
= memchr (authority_start
, '@', authority_end
- authority_start
);
622 userinfo_start
= authority_start
;
623 decoded
->userinfo
= unescape_string (userinfo_start
, userinfo_end
, NULL
);
624 if (decoded
->userinfo
== NULL
)
626 _g_decoded_uri_free (decoded
);
629 host_start
= userinfo_end
+ 1;
632 host_start
= authority_start
;
634 port_start
= memchr (host_start
, ':', authority_end
- host_start
);
637 host_end
= port_start
++;
639 decoded
->port
= atoi(port_start
);
643 host_end
= authority_end
;
647 decoded
->host
= g_strndup (host_start
, host_end
- host_start
);
649 hier_part_start
= authority_end
;
652 decoded
->path
= unescape_string (hier_part_start
, hier_part_end
, "/");
654 if (decoded
->path
== NULL
)
656 _g_decoded_uri_free (decoded
);
664 is_valid (char c
, const char *reserved_chars_allowed
)
666 if (g_ascii_isalnum (c
) ||
673 if (reserved_chars_allowed
&&
674 strchr (reserved_chars_allowed
, c
) != NULL
)
681 g_string_append_encoded (GString
*string
,
683 const char *reserved_chars_allowed
)
686 static const gchar hex
[16] = "0123456789ABCDEF";
688 while ((c
= *encoded
) != 0)
690 if (is_valid (c
, reserved_chars_allowed
))
692 g_string_append_c (string
, c
);
697 g_string_append_c (string
, '%');
698 g_string_append_c (string
, hex
[((guchar
)c
) >> 4]);
699 g_string_append_c (string
, hex
[((guchar
)c
) & 0xf]);
706 _g_encode_uri (GDecodedUri
*decoded
)
710 uri
= g_string_new (NULL
);
712 g_string_append (uri
, decoded
->scheme
);
713 g_string_append (uri
, "://");
715 if (decoded
->host
!= NULL
)
717 if (decoded
->userinfo
)
719 /* userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) */
720 g_string_append_encoded (uri
, decoded
->userinfo
, SUB_DELIM_CHARS
":");
721 g_string_append_c (uri
, '@');
724 g_string_append (uri
, decoded
->host
);
726 if (decoded
->port
!= -1)
728 g_string_append_c (uri
, ':');
729 g_string_append_printf (uri
, "%d", decoded
->port
);
733 g_string_append_encoded (uri
, decoded
->path
, SUB_DELIM_CHARS
":@/");
737 g_string_append_c (uri
, '?');
738 g_string_append (uri
, decoded
->query
);
741 if (decoded
->fragment
)
743 g_string_append_c (uri
, '#');
744 g_string_append (uri
, decoded
->fragment
);
747 return g_string_free (uri
, FALSE
);