source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-uri.c @ 17128

Revision 17128, 43.7 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17127, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* gnome-vfs-uri.c - URI handling for the GNOME Virtual File System.
3
4   Copyright (C) 1999 Free Software Foundation
5   Copyright (C) 2000, 2001 Eazel, Inc.
6
7   The Gnome Library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Library General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11
12   The Gnome Library is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   Library General Public License for more details.
16
17   You should have received a copy of the GNU Library General Public
18   License along with the Gnome Library; see the file COPYING.LIB.  If not,
19   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.
21
22   Author: Ettore Perazzoli <ettore@gnu.org>
23
24*/
25
26#include <config.h>
27#include "gnome-vfs-uri.h"
28
29#include "gnome-vfs-private.h"
30#include "gnome-vfs-transform.h"
31#include "gnome-vfs.h"
32#include <ctype.h>
33#include <stdio.h>
34#include <string.h>
35
36/*
37   split_toplevel_uri
38
39   Extract hostname and username from "path" with length "path_len"
40
41   examples:
42       sunsite.unc.edu/pub/linux
43       miguel@sphinx.nuclecu.unam.mx/c/nc
44       tsx-11.mit.edu:8192/
45       joe@foo.edu:11321/private
46       joe:password@foo.se
47
48   This function implements the following regexp: (whitespace for clarity)
49
50   ( ( ([^:@/]*) (:[^@/]*)? @ )? ([^/:]*) (:([0-9]*)?) )?  (/.*)?
51   ( ( ( user  ) (  pw  )?   )?   (host)    (port)?   )? (path <return value>)?
52
53  It returns NULL if neither <host> nor <path> could be matched.
54
55  port is checked to ensure that it does not exceed 0xffff.
56
57  return value is <path> or is "/" if the path portion is not present
58  All other arguments are set to 0 or NULL if their portions are not present
59
60  pedantic: this function ends up doing an unbounded lookahead, making it
61  potentially O(n^2) instead of O(n).  This could be avoided.  Realistically, though,
62  its just the password field.
63
64  Differences between the old and the new implemention:
65
66                     Old                     New
67  localhost:8080     host="localhost:8080"   host="localhost" port=8080
68  /Users/mikef       host=""                 host=NULL
69
70*/
71
72
73#define URI_MOVE_PAST_DELIMITER \
74        do {                                                    \
75                cur_tok_start = (++cur);                        \
76                if (path_end == cur) {                          \
77                        success = FALSE;                        \
78                        goto done;                              \
79                }                                               \
80        } while (0);
81
82
83#define uri_strlen_to(from, to)  ( (to) - (from) )
84#define uri_strdup_to(from, to)  g_strndup ((from), uri_strlen_to((from), (to)))
85
86typedef struct {
87        const char *chrs;
88        gboolean primed;
89        char bv[32];
90} UriStrspnSet;
91
92UriStrspnSet uri_strspn_sets[] = {
93        {":@" GNOME_VFS_URI_PATH_STR, FALSE, ""},
94        {"@" GNOME_VFS_URI_PATH_STR, FALSE, ""},
95        {":" GNOME_VFS_URI_PATH_STR, FALSE, ""}
96};
97
98#define URI_DELIMITER_ALL_SET (uri_strspn_sets + 0)
99#define URI_DELIMITER_USER_SET (uri_strspn_sets + 1)
100#define URI_DELIMITER_HOST_SET (uri_strspn_sets + 2)
101
102#define BV_SET(bv, idx) (bv)[((guchar)(idx))>>3] |= (1 << ( (idx) & 7) )
103#define BV_IS_SET(bv, idx) ((bv)[(idx)>>3] & (1 << ( (idx) & 7)))
104
105static const char *
106uri_strspn_to(const char *str, UriStrspnSet *set, const char *path_end)
107{
108        const char *cur;
109        const char *cur_chr;
110
111        if (!set->primed) {
112                memset (set->bv, 0, sizeof(set->bv));
113       
114                for (cur_chr = set->chrs; '\0' != *cur_chr; cur_chr++) {
115                        BV_SET (set->bv, *cur_chr);
116                }
117
118                BV_SET (set->bv, '\0');
119                set->primed = TRUE;
120        }
121       
122        for (cur = str; cur < path_end && ! BV_IS_SET (set->bv, *cur); cur++)
123                ;
124
125        if (cur >= path_end || '\0' == *cur) {
126                return NULL;
127        }
128
129        return cur;
130}
131
132
133static gchar *
134split_toplevel_uri (const gchar *path, guint path_len,
135                    gchar **host_return, gchar **user_return,
136                    guint *port_return, gchar **password_return)
137{
138        const char *path_end;
139        const char *cur_tok_start;
140        const char *cur;
141        const char *next_delimiter;
142        char *ret;
143        gboolean success;
144
145        g_assert (host_return != NULL);
146        g_assert (user_return != NULL);
147        g_assert (port_return != NULL);
148        g_assert (password_return != NULL);
149
150        *host_return = NULL;
151        *user_return = NULL;
152        *port_return = 0;
153        *password_return = NULL;
154        ret = NULL;
155
156        success = FALSE;
157
158        if (path == NULL || path_len == 0) {
159                return NULL;
160        }
161       
162
163        path_end = path + path_len;
164
165        cur_tok_start = path;
166        cur = uri_strspn_to (cur_tok_start, URI_DELIMITER_ALL_SET, path_end);
167
168        if (cur != NULL) {
169                next_delimiter = uri_strspn_to (cur, URI_DELIMITER_USER_SET, path_end);
170        } else {
171                next_delimiter = NULL;
172        }
173       
174        if (cur != NULL
175                && (*cur == '@'
176                    || (next_delimiter != NULL && *next_delimiter != '/' ))) {
177
178                /* *cur == ':' or '@' and string contains a @ before a / */
179
180                if (uri_strlen_to (cur_tok_start, cur) > 0) {
181                        *user_return = uri_strdup_to (cur_tok_start,cur);
182                }
183
184                if (*cur == ':') {
185                        URI_MOVE_PAST_DELIMITER;
186
187                        cur = uri_strspn_to(cur_tok_start, URI_DELIMITER_USER_SET, path_end);
188
189                        if (cur == NULL || *cur != '@') {
190                                success = FALSE;
191                                goto done;
192                        } else if (uri_strlen_to (cur_tok_start, cur) > 0) {
193                                *password_return = uri_strdup_to (cur_tok_start,cur);
194                        }
195                }
196
197                if (*cur != '/') {
198                        URI_MOVE_PAST_DELIMITER;
199                        cur = uri_strspn_to (cur_tok_start, URI_DELIMITER_HOST_SET, path_end);
200                } else {
201                        cur_tok_start = cur;
202                }
203        }
204
205        if (cur == NULL) {
206                /* [^:/]+$ */
207                if (uri_strlen_to (cur_tok_start, path_end) > 0) {
208                        *host_return = uri_strdup_to (cur_tok_start, path_end);
209                        if (*(path_end - 1) == GNOME_VFS_URI_PATH_CHR) {
210                                ret = g_strdup (GNOME_VFS_URI_PATH_STR);
211                        } else {
212                                ret = g_strdup ("");
213                        }
214                        success = TRUE;
215                } else { /* No host, no path */
216                        success = FALSE;
217                }
218
219                goto done;
220
221        } else if (*cur == ':') {
222                guint port;
223                /* [^:/]*:.* */
224
225                if (uri_strlen_to (cur_tok_start, cur) > 0) {
226                        *host_return = uri_strdup_to (cur_tok_start, cur);
227                } else {
228                        success = FALSE;
229                        goto done;      /*No host but a port?*/
230                }
231
232                URI_MOVE_PAST_DELIMITER;
233
234                port = 0;
235
236                for ( ; cur < path_end && '\0' != *cur && isdigit ((guchar) *cur); cur++) {
237                        port *= 10;
238                        port += *cur - '0';
239                }
240
241                /* We let :(/.*)$ be treated gracefully */
242                if (*cur != '\0' && *cur != GNOME_VFS_URI_PATH_CHR) {
243                        success = FALSE;
244                        goto done;      /* ...but this would be an error */
245                }
246
247                if (port > 0xffff) {
248                        success = FALSE;
249                        goto done;
250                }
251
252                *port_return = port;
253
254                cur_tok_start = cur;
255               
256        } else /* GNOME_VFS_URI_PATH_CHR == *cur */ {
257                /* ^[^:@/]+/.*$ */
258
259                if (uri_strlen_to (cur_tok_start, cur) > 0) {
260                        *host_return = uri_strdup_to (cur_tok_start, cur);
261                }
262
263                cur_tok_start = cur;
264        }
265
266        if (*cur_tok_start != '\0' && uri_strlen_to (cur_tok_start, path_end) > 0) {
267                ret = uri_strdup_to(cur, path_end);
268        } else if (*host_return != NULL) {
269                ret = g_strdup (GNOME_VFS_URI_PATH_STR);
270        }
271
272        success = TRUE;
273
274done:
275        if (*host_return != NULL) {
276                g_strdown (*host_return);
277
278        }
279
280        /* If we didn't complete our mission, discard all the partials */
281        if (!success) {
282                g_free (*host_return);
283                g_free (*user_return);
284                g_free (*password_return);
285                g_free (ret);
286
287                *host_return = NULL;
288                *user_return = NULL;
289                *port_return = 0;
290                *password_return = NULL;
291                ret = NULL;
292        }
293
294        return ret;
295}
296
297
298static void
299set_uri_element (GnomeVFSURI *uri,
300                 const gchar *text,
301                 guint len)
302{
303        char *escaped_text;
304
305        if (text == NULL || len == 0) {
306                uri->text = NULL;
307                return;
308        }
309
310        if (uri->parent == NULL && text[0] == '/' && text[1] == '/') {
311                GnomeVFSToplevelURI *toplevel;
312
313                toplevel = (GnomeVFSToplevelURI *) uri;
314                uri->text = split_toplevel_uri (text + 2, len - 2,
315                                                &toplevel->host_name,
316                                                &toplevel->user_name,
317                                                &toplevel->host_port,
318                                                &toplevel->password);
319        } else {
320                uri->text = g_strndup (text, len);
321        }
322
323        /* FIXME: this should be handled/supported by the specific method.
324         * This is a quick and dirty hack to minimize the amount of changes
325         * right before a milestone release.
326         *
327         * Do some method specific escaping. This for instance converts
328         * '?' to %3F in every method except "http" where it has a special
329         * meaning.
330         */
331        if ( ! (strcmp (uri->method_string, "http") == 0
332                || strcmp (uri->method_string, "eazel-services") == 0
333                || strcmp (uri->method_string, "ghelp") == 0
334                || strcmp (uri->method_string, "gnome-help") == 0
335                || strcmp (uri->method_string, "help") == 0
336                )) {
337
338                escaped_text = gnome_vfs_escape_set (uri->text, ";?&=+$,");
339                g_free (uri->text);
340                uri->text = escaped_text;
341        }
342       
343        gnome_vfs_remove_optional_escapes (uri->text);
344        gnome_vfs_canonicalize_pathname (uri->text);
345}
346
347static const gchar *
348get_method_string (const gchar *substring, gchar **method_string)
349{
350        const gchar *p;
351       
352        for (p = substring;
353             isalnum ((unsigned char)*p) || *p == '+' || *p == '-' || *p == '.';
354             p++)
355                ;
356
357        if (*p == ':') {
358                /* Found toplevel method specification.  */
359                *method_string = g_strndup (substring, p - substring);
360                g_strdown (*method_string);
361                p++;
362        } else {
363                *method_string = g_strdup ("file");
364                p = substring;
365        }
366        return p;
367}
368
369static GnomeVFSURI *
370parse_uri_substring (const gchar *substring, GnomeVFSURI *parent)
371{
372        GnomeVFSMethod *method;
373        GnomeVFSURI *uri, *child_uri;
374        gchar *method_string;
375        const gchar *method_scanner;
376        const gchar *extension_scanner;
377
378        if (substring == NULL || *substring == '\0') {
379                return NULL;
380        }
381       
382        method_scanner = get_method_string (substring, &method_string);
383
384        method = gnome_vfs_method_get (method_string);
385        if (!method) {
386                g_free (method_string);
387                return NULL;
388        }
389
390        uri = g_new0 (GnomeVFSURI, 1);
391        uri->method = method;
392        uri->method_string = method_string;
393        uri->ref_count = 1;
394        uri->parent = parent;
395
396        extension_scanner = strchr (method_scanner, GNOME_VFS_URI_MAGIC_CHR);
397
398        if (extension_scanner == NULL) {
399                set_uri_element (uri, method_scanner, strlen (method_scanner));
400                return uri;
401        }
402
403        /* handle '#' */
404        set_uri_element (uri, method_scanner, extension_scanner - method_scanner);
405
406        if (strchr (extension_scanner, ':') == NULL) {
407                /* extension is a fragment identifier */
408                uri->fragment_id = g_strdup (extension_scanner + 1);
409                return uri;
410        }
411
412        /* extension is a uri chain */
413        child_uri = parse_uri_substring (extension_scanner + 1, uri);
414
415        if (child_uri != NULL) {
416                return child_uri;
417        }
418
419        return uri;
420}
421
422/**
423 * gnome_vfs_uri_new:
424 * @text_uri: A string representing a URI.
425 *
426 * Create a new URI from @text_uri.
427 *
428 * Return value: The new URI.
429 **/
430GnomeVFSURI *
431gnome_vfs_uri_new (const gchar *text_uri)
432{
433        return gnome_vfs_uri_new_private (text_uri, FALSE, FALSE, TRUE);
434}
435
436GnomeVFSURI *
437gnome_vfs_uri_new_private (const gchar *text_uri,
438                           gboolean allow_unknown_methods,
439                           gboolean allow_unsafe_methods,
440                           gboolean allow_transforms)
441{
442        GnomeVFSMethod *method;
443        GnomeVFSTransform *trans;
444        GnomeVFSToplevelURI *toplevel;
445        GnomeVFSURI *uri, *child_uri;
446        const gchar *method_scanner, *extension_scanner;
447        gchar *method_string;
448        gchar *new_uri_string = NULL;
449
450        g_return_val_if_fail (text_uri != NULL, NULL);
451
452        if (text_uri[0] == '\0') {
453                return NULL;
454        }
455
456        method_scanner = get_method_string (text_uri, &method_string);
457        if (strcmp (method_string, "pipe") == 0 && !allow_unsafe_methods) {
458                g_free (method_string);
459                return NULL;
460        }
461
462        toplevel = g_new (GnomeVFSToplevelURI, 1);
463        toplevel->host_name = NULL;
464        toplevel->host_port = 0;
465        toplevel->user_name = NULL;
466        toplevel->password = NULL;
467
468        uri = (GnomeVFSURI *) toplevel;
469        uri->parent = NULL;
470
471        if (allow_transforms) {
472                trans = gnome_vfs_transform_get (method_string);
473                if (trans != NULL && trans->transform) {
474                        GnomeVFSContext *context;
475                       
476                        context = gnome_vfs_context_new ();
477                        (* trans->transform) (trans, method_scanner, &new_uri_string, context);
478                        gnome_vfs_context_unref (context);
479                        if (new_uri_string != NULL) {
480                                toplevel->urn = g_strdup (text_uri);
481                                g_free (method_string);
482                                method_scanner = get_method_string (new_uri_string, &method_string);
483                        }
484                }
485        }
486       
487        method = gnome_vfs_method_get (method_string);
488        /* The toplevel URI element is special, as it also contains host/user
489           information.  */
490        uri->method = method;
491        uri->ref_count = 1;
492        uri->method_string = method_string;
493        uri->text = NULL;
494        uri->fragment_id = NULL;
495        if (method == NULL && !allow_unknown_methods) {
496                g_free (new_uri_string);
497                gnome_vfs_uri_unref (uri);
498                return NULL;
499        }
500
501        extension_scanner = strchr (method_scanner, GNOME_VFS_URI_MAGIC_CHR);
502        if (extension_scanner == NULL) {
503                set_uri_element (uri, method_scanner, strlen (method_scanner));
504                g_free (new_uri_string);
505                return uri;
506        }
507
508        /* handle '#' */
509        set_uri_element (uri, method_scanner, extension_scanner - method_scanner);
510
511        if (strchr (extension_scanner, ':') == NULL) {
512                /* extension is a fragment identifier */
513                uri->fragment_id = g_strdup (extension_scanner + 1);
514                g_free (new_uri_string);
515                return uri;
516        }
517
518        /* extension is a uri chain */
519        child_uri = parse_uri_substring (extension_scanner + 1, uri);
520
521        g_free (new_uri_string);
522
523        if (child_uri != NULL) {
524                return child_uri;
525        }
526       
527        return uri;
528}
529
530/* Destroy an URI element, but not its parent.  */
531static void
532destroy_element (GnomeVFSURI *uri)
533{
534        g_free (uri->text);
535        g_free (uri->fragment_id);
536        g_free (uri->method_string);
537
538        if (uri->parent == NULL) {
539                GnomeVFSToplevelURI *toplevel;
540
541                toplevel = (GnomeVFSToplevelURI *) uri;
542                g_free (toplevel->host_name);
543                g_free (toplevel->user_name);
544                g_free (toplevel->password);
545        }
546
547        g_free (uri);
548}
549
550static gboolean
551is_uri_relative (const char *uri)
552{
553        const char *current;
554
555        /* RFC 2396 section 3.1 */
556        for (current = uri ;
557                *current
558                &&      ((*current >= 'a' && *current <= 'z')
559                         || (*current >= 'A' && *current <= 'Z')
560                         || (*current >= '0' && *current <= '9')
561                         || ('-' == *current)
562                         || ('+' == *current)
563                         || ('.' == *current)) ;
564             current++);
565
566        return  !(':' == *current);
567}
568
569/*
570 * Remove "./" segments
571 * Compact "../" segments inside the URI
572 * Remove "." at the end of the URL
573 * Leave any ".."'s at the beginning of the URI
574 */
575
576/* in case if you were wondering, this is probably one of the least time-efficient ways to do this*/
577static void
578remove_internal_relative_components (char *uri_current)
579{
580        char *segment_prev, *segment_cur;
581        gsize len_prev, len_cur;
582
583        len_prev = len_cur = 0;
584        segment_prev = NULL;
585
586        segment_cur = uri_current;
587
588        while (*segment_cur) {
589                len_cur = strcspn (segment_cur, "/");
590
591                if (len_cur == 1 && segment_cur[0] == '.') {
592                        /* Remove "." 's */
593                        if (segment_cur[1] == '\0') {
594                                segment_cur[0] = '\0';
595                                break;
596                        } else {
597                                memmove (segment_cur, segment_cur + 2, strlen (segment_cur + 2) + 1);
598                                continue;
599                        }
600                } else if (len_cur == 2 && segment_cur[0] == '.' && segment_cur[1] == '.' ) {
601                        /* Remove ".."'s (and the component to the left of it) that aren't at the
602                         * beginning or to the right of other ..'s
603                         */
604                        if (segment_prev) {
605                                if (! (len_prev == 2
606                                       && segment_prev[0] == '.'
607                                       && segment_prev[1] == '.')) {
608                                        if (segment_cur[2] == '\0') {
609                                                segment_prev[0] = '\0';
610                                                break;
611                                        } else {
612                                                memmove (segment_prev, segment_cur + 3, strlen (segment_cur + 3) + 1);
613
614                                                segment_cur = segment_prev;
615                                                len_cur = len_prev;
616
617                                                /* now we find the previous segment_prev */
618                                                if (segment_prev == uri_current) {
619                                                        segment_prev = NULL;
620                                                } else if (segment_prev - uri_current >= 2) {
621                                                        segment_prev -= 2;
622                                                        for ( ; segment_prev > uri_current && segment_prev[0] != '/'
623                                                              ; segment_prev-- );
624                                                        if (segment_prev[0] == '/') {
625                                                                segment_prev++;
626                                                        }
627                                                }
628                                                continue;
629                                        }
630                                }
631                        }
632                }
633
634                /*Forward to next segment */
635
636                if (segment_cur [len_cur] == '\0') {
637                        break;
638                }
639                 
640                segment_prev = segment_cur;
641                len_prev = len_cur;
642                segment_cur += len_cur + 1;     
643        }
644       
645}
646
647/* If I had known this relative uri code would have ended up this long, I would
648 * have done it a different way
649 */
650static char *
651make_full_uri_from_relative (const char *base_uri, const char *uri)
652{
653        char *result = NULL;
654
655        g_return_val_if_fail (base_uri != NULL, g_strdup (uri));
656        g_return_val_if_fail (uri != NULL, NULL);
657
658        /* See section 5.2 in RFC 2396 */
659
660        /* FIXME bugzilla.eazel.com 4413: This function does not take
661         * into account a BASE tag in an HTML document, so its
662         * functionality differs from what Mozilla itself would do.
663         */
664
665        if (is_uri_relative (uri)) {
666                char *mutable_base_uri;
667                char *mutable_uri;
668
669                char *uri_current;
670                gsize base_uri_length;
671                char *separator;
672
673                /* We may need one extra character
674                 * to append a "/" to uri's that have no "/"
675                 * (such as help:)
676                 */
677
678                mutable_base_uri = g_malloc(strlen(base_uri)+2);
679                strcpy (mutable_base_uri, base_uri);
680               
681                uri_current = mutable_uri = g_strdup (uri);
682
683                /* Chew off Fragment and Query from the base_url */
684
685                separator = strrchr (mutable_base_uri, '#');
686
687                if (separator) {
688                        *separator = '\0';
689                }
690
691                separator = strrchr (mutable_base_uri, '?');
692
693                if (separator) {
694                        *separator = '\0';
695                }
696
697                if ('/' == uri_current[0] && '/' == uri_current [1]) {
698                        /* Relative URI's beginning with the authority
699                         * component inherit only the scheme from their parents
700                         */
701
702                        separator = strchr (mutable_base_uri, ':');
703
704                        if (separator) {
705                                separator[1] = '\0';
706                        }                         
707                } else if ('/' == uri_current[0]) {
708                        /* Relative URI's beginning with '/' absolute-path based
709                         * at the root of the base uri
710                         */
711
712                        separator = strchr (mutable_base_uri, ':');
713
714                        /* g_assert (separator), really */
715                        if (separator) {
716                                /* If we start with //, skip past the authority section */
717                                if ('/' == separator[1] && '/' == separator[2]) {
718                                        separator = strchr (separator + 3, '/');
719                                        if (separator) {
720                                                separator[0] = '\0';
721                                        }
722                                } else {
723                                /* If there's no //, just assume the scheme is the root */
724                                        separator[1] = '\0';
725                                }
726                        }
727                } else if ('#' != uri_current[0]) {
728                        /* Handle the ".." convention for relative uri's */
729
730                        /* If there's a trailing '/' on base_url, treat base_url
731                         * as a directory path.
732                         * Otherwise, treat it as a file path, and chop off the filename
733                         */
734
735                        base_uri_length = strlen (mutable_base_uri);
736                        if ('/' == mutable_base_uri[base_uri_length-1]) {
737                                /* Trim off '/' for the operation below */
738                                mutable_base_uri[base_uri_length-1] = 0;
739                        } else {
740                                separator = strrchr (mutable_base_uri, '/');
741                                if (separator) {
742                                        *separator = '\0';
743                                }
744                        }
745
746                        remove_internal_relative_components (uri_current);
747
748                        /* handle the "../"'s at the beginning of the relative URI */
749                        while (0 == strncmp ("../", uri_current, 3)) {
750                                uri_current += 3;
751                                separator = strrchr (mutable_base_uri, '/');
752                                if (separator) {
753                                        *separator = '\0';
754                                } else {
755                                        /* <shrug> */
756                                        break;
757                                }
758                        }
759
760                        /* handle a ".." at the end */
761                        if (uri_current[0] == '.' && uri_current[1] == '.'
762                            && uri_current[2] == '\0') {
763
764                                uri_current += 2;
765                                separator = strrchr (mutable_base_uri, '/');
766                                if (separator) {
767                                        *separator = '\0';
768                                }
769                        }
770
771                        /* Re-append the '/' */
772                        mutable_base_uri [strlen(mutable_base_uri)+1] = '\0';
773                        mutable_base_uri [strlen(mutable_base_uri)] = '/';
774                }
775
776                result = g_strconcat (mutable_base_uri, uri_current, NULL);
777                g_free (mutable_base_uri);
778                g_free (mutable_uri);
779
780        } else {
781                result = g_strdup (uri);
782        }
783       
784        return result;
785}
786
787/**
788 * gnome_vfs_uri_resolve_relative:
789 * @base: The base URI.
790 * @relative_reference: A string representing a possibly-relative URI reference
791 *
792 * Create a new URI from @text_uri relative to @base.
793 *
794 * Return value: The new URI.
795 **/
796GnomeVFSURI *
797gnome_vfs_uri_resolve_relative (const GnomeVFSURI *base,
798                                const char *relative_reference)
799{
800        char *text_base;
801        char *text_new;
802        GnomeVFSURI *uri;
803
804        text_base = gnome_vfs_uri_to_string (base, 0);
805        text_new = make_full_uri_from_relative (text_base, relative_reference);
806
807        uri = gnome_vfs_uri_new (text_new);
808
809        g_free (text_base);
810        g_free (text_new);
811
812        return uri;
813}
814
815/**
816 * gnome_vfs_uri_ref:
817 * @uri: A GnomeVFSURI.
818 *
819 * Increment @uri's reference count.
820 *
821 * Return value: @uri.
822 **/
823GnomeVFSURI *
824gnome_vfs_uri_ref (GnomeVFSURI *uri)
825{
826        GnomeVFSURI *p;
827
828        g_return_val_if_fail (uri != NULL, NULL);
829
830        for (p = uri; p != NULL; p = p->parent)
831                p->ref_count++;
832
833        return uri;
834}
835
836/**
837 * gnome_vfs_uri_unref:
838 * @uri: A GnomeVFSURI.
839 *
840 * Decrement @uri's reference count.  If the reference count reaches zero,
841 * @uri is destroyed.
842 **/
843void
844gnome_vfs_uri_unref (GnomeVFSURI *uri)
845{
846        GnomeVFSURI *p, *parent;
847
848        g_return_if_fail (uri != NULL);
849        g_return_if_fail (uri->ref_count > 0);
850
851        for (p = uri; p != NULL; p = parent) {
852                parent = p->parent;
853                g_assert (p->ref_count > 0);
854                p->ref_count--;
855                if (p->ref_count == 0)
856                        destroy_element (p);
857        }
858}
859
860/**
861 * gnome_vfs_uri_dup:
862 * @uri: A GnomeVFSURI.
863 *
864 * Duplicate @uri.
865 *
866 * Return value: A pointer to a new URI that is exactly the same as @uri.
867 **/
868GnomeVFSURI *
869gnome_vfs_uri_dup (const GnomeVFSURI *uri)
870{
871        const GnomeVFSURI *p;
872        GnomeVFSURI *new_uri, *child;
873
874        if (uri == NULL) {
875                return NULL;
876        }
877
878        new_uri = NULL;
879        child = NULL;
880        for (p = uri; p != NULL; p = p->parent) {
881                GnomeVFSURI *new_element;
882
883                if (p->parent == NULL) {
884                        GnomeVFSToplevelURI *toplevel;
885                        GnomeVFSToplevelURI *new_toplevel;
886
887                        toplevel = (GnomeVFSToplevelURI *) p;
888                        new_toplevel = g_new (GnomeVFSToplevelURI, 1);
889
890                        new_toplevel->host_name = g_strdup (toplevel->host_name);
891                        new_toplevel->host_port = toplevel->host_port;
892                        new_toplevel->user_name = g_strdup (toplevel->user_name);
893                        new_toplevel->password = g_strdup (toplevel->password);
894
895                        new_element = (GnomeVFSURI *) new_toplevel;
896                } else {
897                        new_element = g_new (GnomeVFSURI, 1);
898                }
899
900                new_element->ref_count = 1;
901                new_element->text = g_strdup (p->text);
902                new_element->fragment_id = g_strdup (p->fragment_id);
903                new_element->method_string = g_strdup (p->method_string);
904                new_element->method = p->method;
905                new_element->parent = NULL;
906
907                if (child != NULL) {
908                        child->parent = new_element;
909                } else {
910                        new_uri = new_element;
911                }
912                       
913                child = new_element;
914        }
915
916        return new_uri;
917}
918
919/**
920 * gnome_vfs_uri_append_string:
921 * @uri: A GnomeVFSURI.
922 * @uri_fragment_string: A piece of a URI (ie a fully escaped partial path)
923 *
924 * Create a new URI obtained by appending @path to @uri.  This will take care
925 * of adding an appropriate directory separator between the end of @uri and
926 * the start of @path if necessary.
927 *
928 * Return value: The new URI obtained by combining @uri and @path.
929 **/
930GnomeVFSURI *
931gnome_vfs_uri_append_string (const GnomeVFSURI *uri,
932                             const char *uri_part_string)
933{
934        char *uri_string;
935        GnomeVFSURI *new_uri;
936        char *new_string;
937        guint len;
938
939        g_return_val_if_fail (uri != NULL, NULL);
940        g_return_val_if_fail (uri_part_string != NULL, NULL);
941
942        uri_string = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE);
943        len = strlen (uri_string);
944        if (len == 0) {
945                g_free (uri_string);
946                return gnome_vfs_uri_new (uri_part_string);
947        }
948
949        len--;
950        while (uri_string[len] == GNOME_VFS_URI_PATH_CHR && len > 0) {
951                len--;
952        }
953
954        uri_string[len + 1] = '\0';
955
956        while (*uri_part_string == GNOME_VFS_URI_PATH_CHR) {
957                uri_part_string++;
958        }
959
960        if (uri_part_string[0] != GNOME_VFS_URI_MAGIC_CHR) {
961                new_string = g_strconcat (uri_string, GNOME_VFS_URI_PATH_STR, uri_part_string, NULL);
962        } else {
963                new_string = g_strconcat (uri_string, uri_part_string, NULL);
964        }
965        new_uri = gnome_vfs_uri_new (new_string);
966
967        g_free (new_string);
968        g_free (uri_string);
969
970        return new_uri;
971}
972
973/**
974 * gnome_vfs_uri_append_path:
975 * @uri: A GnomeVFSURI.
976 * @path: A non-escaped file path
977 *
978 * Create a new URI obtained by appending @path to @uri.  This will take care
979 * of adding an appropriate directory separator between the end of @uri and
980 * the start of @path if necessary as well as escaping @path as necessary.
981 *
982 * Return value: The new URI obtained by combining @uri and @path.
983 **/
984GnomeVFSURI *
985gnome_vfs_uri_append_path (const GnomeVFSURI *uri,
986                           const char *path)
987{
988        char *escaped_string;
989        GnomeVFSURI *new_uri;
990       
991        escaped_string = gnome_vfs_escape_path_string (path);
992        new_uri = gnome_vfs_uri_append_string (uri, escaped_string);
993        g_free (escaped_string);
994        return new_uri;
995}
996
997/**
998 * gnome_vfs_uri_append_file_name:
999 * @uri: A GnomeVFSURI.
1000 * @path: any "regular" file name (can include #, /, etc)
1001 *
1002 * Create a new URI obtained by appending @file_name to @uri.  This will take care
1003 * of adding an appropriate directory separator between the end of @uri and
1004 * the start of @file_name if necessary.
1005 *
1006 * Return value: The new URI obtained by combining @uri and @path.
1007 **/
1008GnomeVFSURI *
1009gnome_vfs_uri_append_file_name (const GnomeVFSURI *uri,
1010                                const char *file_name)
1011{
1012        char *escaped_string;
1013        GnomeVFSURI *new_uri;
1014       
1015        escaped_string = gnome_vfs_escape_string (file_name);
1016        new_uri = gnome_vfs_uri_append_string (uri, escaped_string);
1017        g_free (escaped_string);
1018        return new_uri;
1019}
1020
1021
1022/**
1023 * gnome_vfs_uri_to_string:
1024 * @uri: A GnomeVFSURI.
1025 * @hide_options: Bitmask specifying what URI elements (e.g. password,
1026 * user name etc.) should not be represented in the returned string.
1027 *
1028 * Translate @uri into a printable string.  The string will not contain the
1029 * URI elements specified by @hide_options.
1030 *
1031 * Return value: A malloced printable string representing @uri.
1032 **/
1033char *
1034gnome_vfs_uri_to_string (const GnomeVFSURI *uri,
1035                         GnomeVFSURIHideOptions hide_options)
1036{
1037        GString *string;
1038        char *result;
1039
1040        string = g_string_new(uri->method_string);
1041        g_string_append_c (string, ':');
1042
1043        if (uri->parent == NULL) {
1044                GnomeVFSToplevelURI *top_level_uri = (GnomeVFSToplevelURI *)uri;
1045                gboolean shown_user_pass = FALSE;
1046
1047                if (top_level_uri->user_name != NULL
1048                        || top_level_uri->host_name != NULL
1049                        || (uri->text != NULL && uri->text[0] == GNOME_VFS_URI_PATH_CHR)) {
1050                        /* don't append '//' for uris such as pipe:foo */
1051                        g_string_append (string, "//");
1052                }
1053
1054                if ((hide_options & GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD) != 0) {
1055                        g_string_free (string, TRUE); /* throw away method */
1056                        string = g_string_new ("");
1057                }
1058
1059                if (top_level_uri->user_name != NULL
1060                        && (hide_options & GNOME_VFS_URI_HIDE_USER_NAME) == 0) {
1061                        g_string_append (string, top_level_uri->user_name);
1062                        shown_user_pass = TRUE;
1063                }
1064
1065                if (top_level_uri->password != NULL
1066                        && (hide_options & GNOME_VFS_URI_HIDE_PASSWORD) == 0) {
1067                        g_string_append_c (string, ':');
1068                        g_string_append (string, top_level_uri->password);
1069                        shown_user_pass = TRUE;
1070                }
1071
1072                if (shown_user_pass) {
1073                        g_string_append_c (string, '@');
1074                }
1075
1076                if (top_level_uri->host_name != NULL
1077                        && (hide_options & GNOME_VFS_URI_HIDE_HOST_NAME) == 0) {
1078                        g_string_append (string, top_level_uri->host_name);
1079                }
1080               
1081                if (top_level_uri->host_port > 0
1082                        && (hide_options & GNOME_VFS_URI_HIDE_HOST_PORT) == 0) {
1083                        char tmp[128];
1084                        sprintf (tmp, ":%d", top_level_uri->host_port);
1085                        g_string_append (string, tmp);
1086                }
1087
1088        }
1089       
1090        if (uri->text != NULL) {
1091                g_string_append (string, uri->text);
1092        }
1093
1094        if (uri->fragment_id != NULL
1095                && (hide_options & GNOME_VFS_URI_HIDE_FRAGMENT_IDENTIFIER) == 0) {
1096                g_string_append_c (string, '#');
1097                g_string_append (string, uri->fragment_id);
1098        }
1099
1100        if (uri->parent != NULL) {
1101                g_string_prepend_c (string, '#');
1102                g_string_prepend (string, gnome_vfs_uri_to_string (uri->parent,
1103                                                                   hide_options));
1104        }
1105
1106        result = string->str;
1107        g_string_free (string, FALSE);
1108
1109        return result;
1110}
1111
1112/**
1113 * gnome_vfs_uri_is_local:
1114 * @uri: A GnomeVFSURI.
1115 *
1116 * Check if @uri is a local (native) file system.
1117 *
1118 * Return value: %FALSE if @uri is not a local file system, %TRUE otherwise.
1119 **/
1120gboolean
1121gnome_vfs_uri_is_local (const GnomeVFSURI *uri)
1122{
1123        g_return_val_if_fail (uri != NULL, FALSE);
1124
1125        /* It's illegal to have is_local be NULL in a method.
1126         * That's why we fail here. If we decide that it's legal,
1127         * then we can change this into an if statement.
1128         */
1129        g_return_val_if_fail (VFS_METHOD_HAS_FUNC (uri->method, is_local), FALSE);
1130
1131        return uri->method->is_local (uri->method, uri);
1132}
1133
1134/**
1135 * gnome_vfs_uri_has_parent:
1136 * @uri: A GnomeVFSURI.
1137 *
1138 * Check if URI has a parent or not.
1139 *
1140 * Return value: %TRUE if @uri has a parent, %FALSE otherwise.
1141 **/
1142gboolean
1143gnome_vfs_uri_has_parent (const GnomeVFSURI *uri)
1144{
1145        GnomeVFSURI *parent;
1146
1147        parent = gnome_vfs_uri_get_parent (uri);
1148        if (parent == NULL) {
1149                return FALSE;
1150        }
1151
1152        gnome_vfs_uri_unref (parent);
1153        return TRUE;
1154}
1155
1156/**
1157 * gnome_vfs_uri_get_parent:
1158 * @uri: A GnomeVFSURI.
1159 *
1160 * Retrieve @uri's parent URI.
1161 *
1162 * Return value: A pointer to @uri's parent URI.
1163 **/
1164GnomeVFSURI *
1165gnome_vfs_uri_get_parent (const GnomeVFSURI *uri)
1166{
1167        g_return_val_if_fail (uri != NULL, NULL);
1168
1169        if (uri->text != NULL && strchr (uri->text, GNOME_VFS_URI_PATH_CHR) != NULL) {
1170                char *p;
1171                guint len;
1172
1173                len = strlen (uri->text);
1174                p = uri->text + len - 1;
1175
1176                /* Skip trailing slashes  */
1177                while (p != uri->text && *p == GNOME_VFS_URI_PATH_CHR)
1178                        p--;
1179
1180                /* Search backwards to the next slash.  */
1181                while (p != uri->text && *p != GNOME_VFS_URI_PATH_CHR)
1182                        p--;
1183
1184                /* Get the parent without slashes  */
1185                while (p > uri->text + 1 && p[-1] == GNOME_VFS_URI_PATH_CHR)
1186                        p--;
1187
1188                if (p[1] != '\0') {
1189                        GnomeVFSURI *new_uri;
1190                        char *new_uri_text;
1191                        int length;
1192
1193                        /* build a new parent text */
1194                        length = p - uri->text;                 
1195                        if (length == 0) {
1196                                new_uri_text = g_strdup (GNOME_VFS_URI_PATH_STR);
1197                        } else {
1198                                new_uri_text = g_malloc (length + 1);
1199                                memcpy (new_uri_text, uri->text, length);
1200                                new_uri_text[length] = '\0';
1201                        }
1202
1203                        /* copy the uri and replace the uri text with the new parent text */
1204                        new_uri = gnome_vfs_uri_dup (uri);
1205                        g_free (new_uri->text);
1206                        new_uri->text = new_uri_text;
1207
1208                        /* The parent doesn't have the child's fragment */
1209                        g_free (new_uri->fragment_id);
1210                        new_uri->fragment_id = NULL;
1211                       
1212                        return new_uri;
1213                }
1214        }
1215
1216        return gnome_vfs_uri_dup (uri->parent);
1217}
1218
1219/**
1220 * gnome_vfs_uri_get_toplevel:
1221 * @uri: A GnomeVFSURI.
1222 *
1223 * Retrieve the toplevel URI in @uri.
1224 *
1225 * Return value: A pointer to the toplevel URI object.
1226 **/
1227GnomeVFSToplevelURI *
1228gnome_vfs_uri_get_toplevel (const GnomeVFSURI *uri)
1229{
1230        const GnomeVFSURI *p;
1231
1232        g_return_val_if_fail (uri != NULL, NULL);
1233
1234        for (p = uri; p->parent != NULL; p = p->parent)
1235                ;
1236
1237        return (GnomeVFSToplevelURI *) p;
1238}
1239
1240/**
1241 * gnome_vfs_uri_get_host_name:
1242 * @uri: A GnomeVFSURI.
1243 *
1244 * Retrieve the host name for @uri.
1245 *
1246 * Return value: A string representing the host name.
1247 **/
1248const char *
1249gnome_vfs_uri_get_host_name (const GnomeVFSURI *uri)
1250{
1251        GnomeVFSToplevelURI *toplevel;
1252
1253        g_return_val_if_fail (uri != NULL, NULL);
1254
1255        toplevel = gnome_vfs_uri_get_toplevel (uri);
1256        return toplevel->host_name;
1257}
1258
1259/**
1260 * gnome_vfs_uri_get_scheme:
1261 * @uri: A GnomeVFSURI
1262 *
1263 * Retrieve the scheme used for @uri
1264 *
1265 * Return value: A string representing the scheme
1266 **/
1267const char *
1268gnome_vfs_uri_get_scheme (const GnomeVFSURI *uri)
1269{
1270        return uri->method_string;
1271}
1272
1273/**
1274 * gnome_vfs_uri_get_host_port:
1275 * @uri: A GnomeVFSURI.
1276 *
1277 * Retrieve the host port number in @uri.
1278 *
1279 * Return value: The host port number used by @uri.  If the value is zero, the
1280 * default port value for the specified toplevel access method is used.
1281 **/
1282guint
1283gnome_vfs_uri_get_host_port (const GnomeVFSURI *uri)
1284{
1285        GnomeVFSToplevelURI *toplevel;
1286
1287        g_return_val_if_fail (uri != NULL, 0);
1288
1289        toplevel = gnome_vfs_uri_get_toplevel (uri);
1290        return toplevel->host_port;
1291}
1292
1293/**
1294 * gnome_vfs_uri_get_user_name:
1295 * @uri: A GnomeVFSURI.
1296 *
1297 * Retrieve the user name in @uri.
1298 *
1299 * Return value: A string representing the user name in @uri.
1300 **/
1301const char *
1302gnome_vfs_uri_get_user_name (const GnomeVFSURI *uri)
1303{
1304        GnomeVFSToplevelURI *toplevel;
1305
1306        g_return_val_if_fail (uri != NULL, NULL);
1307
1308        toplevel = gnome_vfs_uri_get_toplevel (uri);
1309        return toplevel->user_name;
1310}
1311
1312/**
1313 * gnome_vfs_uri_get_password:
1314 * @uri: A GnomeVFSURI.
1315 *
1316 * Retrieve the password for @uri.
1317 *
1318 * Return value: The password for @uri.
1319 **/
1320const char *
1321gnome_vfs_uri_get_password (const GnomeVFSURI *uri)
1322{
1323        GnomeVFSToplevelURI *toplevel;
1324
1325        g_return_val_if_fail (uri != NULL, NULL);
1326
1327        toplevel = gnome_vfs_uri_get_toplevel (uri);
1328        return toplevel->password;
1329}
1330
1331/**
1332 * gnome_vfs_uri_set_host_name:
1333 * @uri: A GnomeVFSURI.
1334 * @host_name: A string representing a host name.
1335 *
1336 * Set @host_name as the host name accessed by @uri.
1337 **/
1338void
1339gnome_vfs_uri_set_host_name (GnomeVFSURI *uri,
1340                             const char *host_name)
1341{
1342        GnomeVFSToplevelURI *toplevel;
1343
1344        g_return_if_fail (uri != NULL);
1345
1346        toplevel = gnome_vfs_uri_get_toplevel (uri);
1347
1348        g_free (toplevel->host_name);
1349        toplevel->host_name = g_strdup (host_name);
1350}
1351
1352/**
1353 * gnome_vfs_uri_set_host_port:
1354 * @uri: A GnomeVFSURI.
1355 * @host_port: A TCP/IP port number.
1356 *
1357 * Set the host port number in @uri.  If @host_port is zero, the default port
1358 * for @uri's toplevel access method is used.
1359 **/
1360void
1361gnome_vfs_uri_set_host_port (GnomeVFSURI *uri,
1362                             guint host_port)
1363{
1364        GnomeVFSToplevelURI *toplevel;
1365
1366        g_return_if_fail (uri != NULL);
1367
1368        toplevel = gnome_vfs_uri_get_toplevel (uri);
1369
1370        toplevel->host_port = host_port;
1371}
1372
1373/**
1374 * gnome_vfs_uri_set_user_name:
1375 * @uri: A GnomeVFSURI.
1376 * @user_name: A string representing a user name on the host accessed by @uri.
1377 *
1378 * Set @user_name as the user name for @uri.
1379 **/
1380void
1381gnome_vfs_uri_set_user_name (GnomeVFSURI *uri,
1382                             const char *user_name)
1383{
1384        GnomeVFSToplevelURI *toplevel;
1385
1386        g_return_if_fail (uri != NULL);
1387
1388        toplevel = gnome_vfs_uri_get_toplevel (uri);
1389
1390        g_free (toplevel->user_name);
1391        toplevel->user_name = g_strdup (user_name);
1392}
1393
1394/**
1395 * gnome_vfs_uri_set_password:
1396 * @uri: A GnomeVFSURI.
1397 * @password: A password string.
1398 *
1399 * Set @password as the password for @uri.
1400 **/
1401void
1402gnome_vfs_uri_set_password (GnomeVFSURI *uri,
1403                            const char *password)
1404{
1405        GnomeVFSToplevelURI *toplevel;
1406
1407        g_return_if_fail (uri != NULL);
1408
1409        toplevel = gnome_vfs_uri_get_toplevel (uri);
1410
1411        g_free (toplevel->password);
1412        toplevel->password = g_strdup (password);
1413}
1414
1415static gboolean
1416string_match (const char *a, const char *b)
1417{
1418        if (a == NULL || *a == '\0') {
1419                return b == NULL || *b == '\0';
1420        }
1421
1422        if (a == NULL || b == NULL)
1423                return FALSE;
1424
1425        return strcmp (a, b) == 0;
1426}
1427
1428static gboolean
1429compare_elements (const GnomeVFSURI *a,
1430                  const GnomeVFSURI *b)
1431{
1432        if (!string_match (a->text, b->text)
1433                || !string_match (a->method_string, b->method_string))
1434                return FALSE;
1435
1436        /* The following should never fail, but we make sure anyway. */
1437        return a->method == b->method;
1438}
1439
1440/**
1441 * gnome_vfs_uri_equal:
1442 * @a: A GnomeVFSURI.
1443 * @b: A GnomeVFSURI.
1444 *
1445 * Compare @a and @b.
1446 *
1447 * Return value: %TRUE if @a and @b are equal, %FALSE otherwise.
1448 *
1449 * FIXME: This comparison should take into account the possiblity
1450 * that unreserved characters may be escaped.
1451 * ...or perhaps gnome_vfs_uri_new should unescape unreserved characters?
1452 **/
1453gboolean
1454gnome_vfs_uri_equal (const GnomeVFSURI *a,
1455                     const GnomeVFSURI *b)
1456{
1457        const GnomeVFSToplevelURI *toplevel_a;
1458        const GnomeVFSToplevelURI *toplevel_b;
1459
1460        g_return_val_if_fail (a != NULL, FALSE);
1461        g_return_val_if_fail (b != NULL, FALSE);
1462
1463        /* First check non-toplevel elements.  */
1464        while (a->parent != NULL && b->parent != NULL) {
1465                if (!compare_elements (a, b)) {
1466                        return FALSE;
1467                }
1468        }
1469
1470        /* Now we should be at toplevel for both.  */
1471        if (a->parent != NULL || b->parent != NULL) {
1472                return FALSE;
1473        }
1474
1475        if (!compare_elements (a, b)) {
1476                return FALSE;
1477        }
1478
1479        toplevel_a = (GnomeVFSToplevelURI *) a;
1480        toplevel_b = (GnomeVFSToplevelURI *) b;
1481
1482        /* Finally, compare the extra toplevel members.  */
1483        return toplevel_a->host_port == toplevel_b->host_port
1484            && string_match (toplevel_a->host_name, toplevel_b->host_name)
1485            && string_match (toplevel_a->user_name, toplevel_b->user_name)
1486            && string_match (toplevel_a->password, toplevel_b->password);
1487}
1488
1489/* Convenience function that deals with the problem where we distinguish
1490 * uris "foo://bar.com" and "foo://bar.com/" but we do not define
1491 * what a child item of "foo://bar.com" would be -- to work around this,
1492 * we will consider both "foo://bar.com" and "foo://bar.com/" the parent
1493 * of "foo://bar.com/child"
1494 */
1495static gboolean
1496uri_matches_as_parent (const GnomeVFSURI *possible_parent, const GnomeVFSURI *parent)
1497{
1498        GnomeVFSURI *alternate_possible_parent;
1499        gboolean result;
1500
1501        if (possible_parent->text == NULL ||
1502            strlen (possible_parent->text) == 0) {
1503                alternate_possible_parent = gnome_vfs_uri_append_string (possible_parent,
1504                        GNOME_VFS_URI_PATH_STR);
1505
1506                result = gnome_vfs_uri_equal (alternate_possible_parent, parent);
1507               
1508                gnome_vfs_uri_unref (alternate_possible_parent);
1509                return result;
1510        }
1511       
1512        return gnome_vfs_uri_equal (possible_parent, parent);
1513}
1514
1515/**
1516 * gnome_vfs_uri_is_parent:
1517 * @possible_parent: A GnomeVFSURI.
1518 * @possible_child: A GnomeVFSURI.
1519 * @recursive: a flag to turn recursive check on.
1520 *
1521 * Check if @possible_child is contained by @possible_parent.
1522 * If @recursive is FALSE, just try the immediate parent directory, else
1523 * search up through the hierarchy.
1524 *
1525 * Return value: %TRUE if @possible_child is contained in  @possible_child.
1526 **/
1527gboolean
1528gnome_vfs_uri_is_parent (const GnomeVFSURI *possible_parent,
1529                         const GnomeVFSURI *possible_child,
1530                         gboolean recursive)
1531{
1532        gboolean result;
1533        GnomeVFSURI *item_parent_uri;
1534        GnomeVFSURI *item;
1535
1536        if (!recursive) {
1537                item_parent_uri = gnome_vfs_uri_get_parent (possible_child);
1538
1539                if (item_parent_uri == NULL) {
1540                        return FALSE;
1541                }
1542
1543                result = uri_matches_as_parent (possible_parent, item_parent_uri);     
1544                gnome_vfs_uri_unref (item_parent_uri);
1545
1546                return result;
1547        }
1548       
1549        item = gnome_vfs_uri_dup (possible_child);
1550        for (;;) {
1551                item_parent_uri = gnome_vfs_uri_get_parent (item);
1552                gnome_vfs_uri_unref (item);
1553               
1554                if (item_parent_uri == NULL) {
1555                        return FALSE;
1556                }
1557
1558                result = uri_matches_as_parent (possible_parent, item_parent_uri);
1559       
1560                if (result) {
1561                        gnome_vfs_uri_unref (item_parent_uri);
1562                        break;
1563                }
1564
1565                item = item_parent_uri;
1566        }
1567
1568        return result;
1569}
1570
1571/**
1572 * gnome_vfs_uri_get_path:
1573 * @uri: A GnomeVFSURI
1574 *
1575 * Retrieve full path name for @uri.
1576 *
1577 * Return value: A pointer to the full path name in @uri.  Notice that the
1578 * pointer points to the name store in @uri, so the name returned must not
1579 * be modified nor freed.
1580 **/
1581const char *
1582gnome_vfs_uri_get_path (const GnomeVFSURI *uri)
1583{
1584        /* FIXME bugzilla.eazel.com 1472 */
1585        /* this is based on the assumtion that uri->text won't contain the
1586         * query string.
1587         */
1588        g_return_val_if_fail (uri != NULL, NULL);
1589
1590        return uri->text;
1591}
1592
1593/**
1594 * gnome_vfs_uri_get_fragment_id:
1595 * @uri: A GnomeVFSURI
1596 *
1597 * Retrieve the optional fragment identifier for @uri.
1598 *
1599 * Return value: A pointer to the fragment identifier for the uri or NULL.
1600 **/
1601const char *
1602gnome_vfs_uri_get_fragment_identifier (const GnomeVFSURI *uri)
1603{
1604        g_return_val_if_fail (uri != NULL, NULL);
1605
1606        return uri->fragment_id;
1607}
1608
1609/**
1610 * gnome_vfs_uri_get_basename:
1611 * @uri: A GnomeVFSURI
1612 *
1613 * Retrieve base file name for @uri.
1614 *
1615 * Return value: A pointer to the base file name in @uri.  Notice that the
1616 * pointer points to the name store in @uri, so the name returned must not
1617 * be modified nor freed.
1618 **/
1619const char *
1620gnome_vfs_uri_get_basename (const GnomeVFSURI *uri)
1621{
1622        /* FIXME bugzilla.eazel.com 1472: query parts of URIs aren't handled */
1623        char *p;
1624
1625        g_return_val_if_fail (uri != NULL, NULL);
1626
1627        if (uri->text == NULL) {
1628                return NULL;
1629        }
1630
1631        p = strrchr (uri->text, GNOME_VFS_URI_PATH_CHR);
1632        if (p == NULL) {
1633                return NULL;
1634        }
1635
1636        p++;
1637        if (*p == '\0') {
1638                return NULL;
1639        }
1640
1641        return p;
1642}
1643
1644/**
1645 * gnome_vfs_uri_extract_dirname:
1646 * @uri: A GnomeVFSURI
1647 *
1648 * Extract the name of the directory in which the file pointed to by @uri is
1649 * stored as a newly allocated string.  The string will end with a
1650 * GNOME_VFS_URI_PATH_CHR.
1651 *
1652 * Return value: A pointer to the newly allocated string representing the
1653 * parent directory.
1654 **/
1655char *
1656gnome_vfs_uri_extract_dirname (const GnomeVFSURI *uri)
1657{
1658        const char *base;
1659
1660        g_return_val_if_fail (uri != NULL, NULL);
1661
1662        base = gnome_vfs_uri_get_basename (uri);
1663        if (base == NULL || base == uri->text) {
1664                return g_strdup (GNOME_VFS_URI_PATH_STR);
1665        }
1666
1667        return g_strndup (uri->text, base - uri->text);
1668}
1669
1670/**
1671 * gnome_vfs_uri_extract_short_name:
1672 * @uri: A GnomeVFSURI
1673 *
1674 * Retrieve base file name for @uri, ignoring any trailing path separators.
1675 * This matches the XPG definition of basename, but not g_basename. This is
1676 * often useful when you want the name of something that's pointed to by a
1677 * uri, and don't care whether the uri has a directory or file form.
1678 * If @uri points to the root of a domain, returns the host name. If there's
1679 * no host name, returns GNOME_VFS_URI_PATH_STR.
1680 *
1681 * See also: gnome_vfs_uri_extract_short_path_name.
1682 *
1683 * Return value: A pointer to the newly allocated string representing the
1684 * unescaped short form of the name.
1685 **/
1686char *
1687gnome_vfs_uri_extract_short_name (const GnomeVFSURI *uri)
1688{
1689        char *escaped_short_path_name, *short_path_name;
1690        const char *host_name;
1691
1692        escaped_short_path_name = gnome_vfs_uri_extract_short_path_name (uri);
1693        short_path_name = gnome_vfs_unescape_string (escaped_short_path_name, "/");
1694        g_free (escaped_short_path_name);
1695
1696        host_name = NULL;
1697        if (short_path_name != NULL
1698                && strcmp (short_path_name, GNOME_VFS_URI_PATH_STR) == 0) {
1699                host_name = gnome_vfs_uri_get_host_name (uri);
1700        }
1701
1702        if (host_name == NULL || strlen (host_name) == 0) {
1703                return short_path_name;
1704        }
1705
1706        g_free (short_path_name);
1707        return g_strdup (host_name);
1708}
1709
1710/**
1711 * gnome_vfs_uri_extract_short_path_name:
1712 * @uri: A GnomeVFSURI
1713 *
1714 * Retrieve base file name for @uri, ignoring any trailing path separators.
1715 * This matches the XPG definition of basename, but not g_basename. This is
1716 * often useful when you want the name of something that's pointed to by a
1717 * uri, and don't care whether the uri has a directory or file form.
1718 * If @uri points to the root (including the root of any domain),
1719 * returns GNOME_VFS_URI_PATH_STR.
1720 *
1721 * See also: gnome_vfs_uri_extract_short_name.
1722 *
1723 * Return value: A pointer to the newly allocated string representing the
1724 * escaped short form of the name.
1725 **/
1726char *
1727gnome_vfs_uri_extract_short_path_name (const GnomeVFSURI *uri)
1728{
1729        const char *p, *short_name_start, *short_name_end;
1730
1731        g_return_val_if_fail (uri != NULL, NULL);
1732
1733        if (uri->text == NULL) {
1734                return NULL;
1735        }
1736
1737        /* Search for the last run of non-'/' characters. */
1738        p = uri->text;
1739        short_name_start = NULL;
1740        short_name_end = p;
1741        do {
1742                if (*p == '\0' || *p == GNOME_VFS_URI_PATH_CHR) {
1743                        /* While we are in a run of non-separators, short_name_end is NULL. */
1744                        if (short_name_end == NULL)
1745                                short_name_end = p;
1746                } else {
1747                        /* While we are in a run of separators, short_name_end is not NULL. */
1748                        if (short_name_end != NULL) {
1749                                short_name_start = p;
1750                                short_name_end = NULL;
1751                        }
1752                }
1753        } while (*p++ != '\0');
1754        g_assert (short_name_end != NULL);
1755       
1756        /* If we never found a short name, that means that the string is all
1757           directory separators. Since it can't be an empty string, that means
1758           it points to the root, so "/" is a good result.
1759        */
1760        if (short_name_start == NULL) {
1761                return g_strdup (GNOME_VFS_URI_PATH_STR);
1762        }
1763
1764        /* Return a copy of the short name. */
1765        return g_strndup (short_name_start, short_name_end - short_name_start);
1766}
1767
1768/* The following functions are useful for creating URI hash tables.  */
1769
1770gint
1771gnome_vfs_uri_hequal (gconstpointer a,
1772                      gconstpointer b)
1773{
1774        return gnome_vfs_uri_equal (a, b);
1775}
1776
1777guint
1778gnome_vfs_uri_hash (gconstpointer p)
1779{
1780        const GnomeVFSURI *uri;
1781        const GnomeVFSURI *uri_p;
1782        guint hash_value;
1783
1784#define HASH_STRING(value, string)              \
1785        if ((string) != NULL)                   \
1786                (value) ^= g_str_hash (string);
1787
1788#define HASH_NUMBER(value, number)              \
1789        (value) ^= number;
1790
1791        uri = (const GnomeVFSURI *) p;
1792        hash_value = 0;
1793
1794        for (uri_p = uri; uri_p != NULL; uri_p = uri_p->parent) {
1795                HASH_STRING (hash_value, uri_p->text);
1796                HASH_STRING (hash_value, uri_p->method_string);
1797
1798                if (uri_p->parent != NULL) {
1799                        const GnomeVFSToplevelURI *toplevel;
1800
1801                        toplevel = (const GnomeVFSToplevelURI *) uri_p;
1802
1803                        HASH_STRING (hash_value, toplevel->host_name);
1804                        HASH_NUMBER (hash_value, toplevel->host_port);
1805                        HASH_STRING (hash_value, toplevel->user_name);
1806                        HASH_STRING (hash_value, toplevel->password);
1807                }
1808        }
1809
1810        return hash_value;
1811
1812#undef HASH_STRING
1813#undef HASH_NUMBER
1814}
1815
1816GList *
1817gnome_vfs_uri_list_ref (GList *list)
1818{
1819        g_list_foreach (list, (GFunc) gnome_vfs_uri_ref, NULL);
1820        return list;
1821}
1822
1823GList *
1824gnome_vfs_uri_list_unref (GList *list)
1825{
1826        g_list_foreach (list, (GFunc) gnome_vfs_uri_unref, NULL);
1827        return list;
1828}
1829
1830GList *
1831gnome_vfs_uri_list_copy (GList *list)
1832{
1833        return g_list_copy (gnome_vfs_uri_list_ref (list));
1834}
1835
1836void
1837gnome_vfs_uri_list_free (GList *list)
1838{
1839        g_list_free (gnome_vfs_uri_list_unref (list));
1840}
Note: See TracBrowser for help on using the repository browser.