udapted vi.po
[rhythmbox.git] / lib / rb-file-helpers.c
blobfd99fd75ffb7771585cf2b4efdfd72a05de1b7af
1 /*
2 * arch-tag: Implementation of various Rhythmbox utility functions for URIs and files
4 * Copyright (C) 2002 Jorn Baayen
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <gtk/gtk.h>
23 #include <glib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <config.h>
27 #include <libgnome/gnome-i18n.h>
28 #include <libgnome/gnome-init.h>
29 #include <libgnomevfs/gnome-vfs-utils.h>
30 #include <libgnomevfs/gnome-vfs-file-info.h>
31 #include <libgnomevfs/gnome-vfs-ops.h>
32 #include <libgnomevfs/gnome-vfs-directory.h>
33 #include <unistd.h>
35 #include "rb-file-helpers.h"
36 #include "rb-debug.h"
38 static GHashTable *files = NULL;
40 static char *dot_dir = NULL;
42 const char *
43 rb_file (const char *filename)
45 char *ret;
46 int i;
48 static char *paths[] = {
49 #ifdef SHARE_UNINSTALLED_DIR
50 SHARE_UNINSTALLED_DIR "/",
51 SHARE_UNINSTALLED_DIR "/ui/",
52 SHARE_UNINSTALLED_DIR "/glade/",
53 SHARE_UNINSTALLED_DIR "/art/",
54 #endif
55 SHARE_DIR "/",
56 SHARE_DIR "/glade/",
57 SHARE_DIR "/art/",
60 g_assert (files != NULL);
62 ret = g_hash_table_lookup (files, filename);
63 if (ret != NULL)
64 return ret;
66 for (i = 0; i < (int) G_N_ELEMENTS (paths); i++) {
67 ret = g_strconcat (paths[i], filename, NULL);
68 if (g_file_test (ret, G_FILE_TEST_EXISTS) == TRUE) {
69 g_hash_table_insert (files, g_strdup (filename), ret);
70 return (const char *) ret;
72 g_free (ret);
75 return NULL;
78 const char *
79 rb_dot_dir (void)
81 if (dot_dir == NULL) {
82 dot_dir = g_build_filename (g_get_home_dir (),
83 GNOME_DOT_GNOME,
84 "rhythmbox",
85 NULL);
86 if (mkdir (dot_dir, 0750) == -1)
87 rb_debug ("unable to create Rhythmbox's dot dir");
90 return dot_dir;
93 void
94 rb_file_helpers_init (void)
96 files = g_hash_table_new_full (g_str_hash,
97 g_str_equal,
98 (GDestroyNotify) g_free,
99 (GDestroyNotify) g_free);
102 void
103 rb_file_helpers_shutdown (void)
105 g_hash_table_destroy (files);
107 g_free (dot_dir);
110 #define MAX_LINK_LEVEL 5
112 char *
113 rb_uri_resolve_symlink (const char *uri)
115 gint link_count;
116 GnomeVFSFileInfo *info;
117 char *followed;
119 g_return_val_if_fail (uri != NULL, NULL);
121 info = gnome_vfs_file_info_new ();
122 gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_DEFAULT);
124 if (info->type != GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) {
125 gnome_vfs_file_info_unref (info);
126 return g_strdup (uri);
129 link_count = 0;
130 followed = g_strdup (uri);
131 while (link_count < MAX_LINK_LEVEL) {
132 GnomeVFSURI *vfs_uri;
133 GnomeVFSURI *new_vfs_uri;
134 char *escaped_path;
136 vfs_uri = gnome_vfs_uri_new (followed);
137 escaped_path = gnome_vfs_escape_path_string (info->symlink_name);
138 new_vfs_uri = gnome_vfs_uri_resolve_relative (vfs_uri,
139 escaped_path);
140 g_free (escaped_path);
142 g_free (followed);
143 followed = gnome_vfs_uri_to_string (new_vfs_uri,
144 GNOME_VFS_URI_HIDE_NONE);
145 link_count++;
147 gnome_vfs_uri_unref (new_vfs_uri);
148 gnome_vfs_uri_unref (vfs_uri);
150 gnome_vfs_file_info_clear (info);
151 gnome_vfs_get_file_info (followed, info,
152 GNOME_VFS_FILE_INFO_DEFAULT);
154 if (info->type != GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) {
155 gnome_vfs_file_info_unref (info);
156 return followed;
160 /* Too many symlinks */
162 gnome_vfs_file_info_unref (info);
164 return NULL;
167 gboolean
168 rb_uri_is_directory (const char *uri)
170 GnomeVFSFileInfo *info;
171 gboolean dir;
173 g_return_val_if_fail (uri != NULL, FALSE);
175 info = gnome_vfs_file_info_new ();
177 gnome_vfs_get_file_info (uri, info,
178 GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE |
179 GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
181 if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
182 dir = TRUE;
183 else
184 dir = FALSE;
186 gnome_vfs_file_info_unref (info);
188 return dir;
191 gboolean
192 rb_uri_exists (const char *uri)
194 GnomeVFSURI *vuri;
195 gboolean ret;
197 g_return_val_if_fail (uri != NULL, FALSE);
199 vuri = gnome_vfs_uri_new (uri);
200 ret = gnome_vfs_uri_exists (vuri);
201 gnome_vfs_uri_unref (vuri);
203 return ret;
206 static gboolean
207 is_valid_scheme_character (char c)
209 return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
213 * FIXME this is not the simplest or most time-efficent way
214 * to do this. Probably a far more clear way of doing this processing
215 * is to split the path into segments, rather than doing the processing
216 * in place.
218 static void
219 remove_internal_relative_components (char *uri_current)
221 char *segment_prev, *segment_cur;
222 size_t len_prev, len_cur;
224 len_prev = len_cur = 0;
225 segment_prev = NULL;
227 g_return_if_fail (uri_current != NULL);
229 segment_cur = uri_current;
231 while (*segment_cur) {
232 len_cur = strcspn (segment_cur, "/");
234 if (len_cur == 1 && segment_cur[0] == '.') {
235 /* Remove "." 's */
236 if (segment_cur[1] == '\0') {
237 segment_cur[0] = '\0';
238 break;
239 } else {
240 memmove (segment_cur, segment_cur + 2, strlen (segment_cur + 2) + 1);
241 continue;
243 } else if (len_cur == 2 && segment_cur[0] == '.' && segment_cur[1] == '.' ) {
244 /* Remove ".."'s (and the component to the left of it) that aren't at the
245 * beginning or to the right of other ..'s
247 if (segment_prev) {
248 if (! (len_prev == 2
249 && segment_prev[0] == '.'
250 && segment_prev[1] == '.')) {
251 if (segment_cur[2] == '\0') {
252 segment_prev[0] = '\0';
253 break;
254 } else {
255 memmove (segment_prev, segment_cur + 3, strlen (segment_cur + 3) + 1);
257 segment_cur = segment_prev;
258 len_cur = len_prev;
260 /* now we find the previous segment_prev */
261 if (segment_prev == uri_current) {
262 segment_prev = NULL;
263 } else if (segment_prev - uri_current >= 2) {
264 segment_prev -= 2;
265 for ( ; segment_prev > uri_current && segment_prev[0] != '/'
266 ; segment_prev-- );
267 if (segment_prev[0] == '/') {
268 segment_prev++;
271 continue;
277 /*Forward to next segment */
279 if (segment_cur [len_cur] == '\0') {
280 break;
283 segment_prev = segment_cur;
284 len_prev = len_cur;
285 segment_cur += len_cur + 1;
289 static gboolean
290 is_uri_partial (const char *uri)
292 const char *current;
294 /* RFC 2396 section 3.1 */
295 for (current = uri ;
296 *current
297 && ((*current >= 'a' && *current <= 'z')
298 || (*current >= 'A' && *current <= 'Z')
299 || (*current >= '0' && *current <= '9')
300 || ('-' == *current)
301 || ('+' == *current)
302 || ('.' == *current)) ;
303 current++);
305 return !(':' == *current);
309 * eel_uri_make_full_from_relative:
311 * Returns a full URI given a full base URI, and a secondary URI which may
312 * be relative.
314 * Return value: the URI (NULL for some bad errors).
316 * FIXME: This code has been copied from eel-mozilla-content-view
317 * because eel-mozilla-content-view cannot link with libeel-extensions
318 * due to lame license issues. Really, this belongs in gnome-vfs, but was added
319 * after the Gnome 1.4 gnome-vfs API freeze
322 static char *
323 eel_uri_make_full_from_relative (const char *base_uri, const char *relative_uri)
325 char *result = NULL;
327 /* See section 5.2 in RFC 2396 */
329 if (base_uri == NULL && relative_uri == NULL) {
330 result = NULL;
331 } else if (base_uri == NULL) {
332 result = g_strdup (relative_uri);
333 } else if (relative_uri == NULL) {
334 result = g_strdup (base_uri);
335 } else if (!is_uri_partial (relative_uri)) {
336 result = g_strdup (relative_uri);
337 } else {
338 char *mutable_base_uri;
339 char *mutable_uri;
341 char *uri_current;
342 size_t base_uri_length;
343 char *separator;
345 mutable_base_uri = g_strdup (base_uri);
346 uri_current = mutable_uri = g_strdup (relative_uri);
348 /* Chew off Fragment and Query from the base_url */
350 separator = strrchr (mutable_base_uri, '#');
352 if (separator) {
353 *separator = '\0';
356 separator = strrchr (mutable_base_uri, '?');
358 if (separator) {
359 *separator = '\0';
362 if ('/' == uri_current[0] && '/' == uri_current [1]) {
363 /* Relative URI's beginning with the authority
364 * component inherit only the scheme from their parents
367 separator = strchr (mutable_base_uri, ':');
369 if (separator) {
370 separator[1] = '\0';
372 } else if ('/' == uri_current[0]) {
373 /* Relative URI's beginning with '/' absolute-path based
374 * at the root of the base uri
377 separator = strchr (mutable_base_uri, ':');
379 /* g_assert (separator), really */
380 if (separator) {
381 /* If we start with //, skip past the authority section */
382 if ('/' == separator[1] && '/' == separator[2]) {
383 separator = strchr (separator + 3, '/');
384 if (separator) {
385 separator[0] = '\0';
387 } else {
388 /* If there's no //, just assume the scheme is the root */
389 separator[1] = '\0';
392 } else if ('#' != uri_current[0]) {
393 /* Handle the ".." convention for relative uri's */
395 /* If there's a trailing '/' on base_url, treat base_url
396 * as a directory path.
397 * Otherwise, treat it as a file path, and chop off the filename
400 base_uri_length = strlen (mutable_base_uri);
401 if ('/' == mutable_base_uri[base_uri_length-1]) {
402 /* Trim off '/' for the operation below */
403 mutable_base_uri[base_uri_length-1] = 0;
404 } else {
405 separator = strrchr (mutable_base_uri, '/');
406 if (separator) {
407 *separator = '\0';
411 remove_internal_relative_components (uri_current);
413 /* handle the "../"'s at the beginning of the relative URI */
414 while (0 == strncmp ("../", uri_current, 3)) {
415 uri_current += 3;
416 separator = strrchr (mutable_base_uri, '/');
417 if (separator) {
418 *separator = '\0';
419 } else {
420 /* <shrug> */
421 break;
425 /* handle a ".." at the end */
426 if (uri_current[0] == '.' && uri_current[1] == '.'
427 && uri_current[2] == '\0') {
429 uri_current += 2;
430 separator = strrchr (mutable_base_uri, '/');
431 if (separator) {
432 *separator = '\0';
436 /* Re-append the '/' */
437 mutable_base_uri [strlen(mutable_base_uri)+1] = '\0';
438 mutable_base_uri [strlen(mutable_base_uri)] = '/';
441 result = g_strconcat (mutable_base_uri, uri_current, NULL);
442 g_free (mutable_base_uri);
443 g_free (mutable_uri);
446 return result;
449 /* Note that NULL's and full paths are also handled by this function.
450 * A NULL location will return the current working directory
452 static char *
453 file_uri_from_local_relative_path (const char *location)
455 char *current_dir;
456 char *base_uri, *base_uri_slash;
457 char *location_escaped;
458 char *uri;
460 current_dir = g_get_current_dir ();
461 base_uri = gnome_vfs_get_uri_from_local_path (current_dir);
462 /* g_get_current_dir returns w/o trailing / */
463 base_uri_slash = g_strconcat (base_uri, "/", NULL);
465 location_escaped = gnome_vfs_escape_path_string (location);
467 uri = eel_uri_make_full_from_relative (base_uri_slash, location_escaped);
469 g_free (location_escaped);
470 g_free (base_uri_slash);
471 g_free (base_uri);
472 g_free (current_dir);
474 return uri;
477 static gboolean
478 has_valid_scheme (const char *uri)
480 const char *p;
482 p = uri;
484 if (!is_valid_scheme_character (*p)) {
485 return FALSE;
488 do {
489 p++;
490 } while (is_valid_scheme_character (*p));
492 return *p == ':';
496 * eel_make_uri_from_shell_arg:
498 * Similar to eel_make_uri_from_input, except that:
500 * 1) guesses relative paths instead of http domains
501 * 2) doesn't bother stripping leading/trailing white space
502 * 3) doesn't bother with ~ expansion--that's done by the shell
504 * @location: a possibly mangled "uri"
506 * returns a newly allocated uri
509 char *
510 rb_uri_resolve_relative (const char *location)
512 char *uri;
514 g_return_val_if_fail (location != NULL, g_strdup (""));
516 switch (location[0]) {
517 case '\0':
518 uri = g_strdup ("");
519 break;
520 case '/':
521 uri = gnome_vfs_get_uri_from_local_path (location);
522 break;
523 default:
524 if (has_valid_scheme (location)) {
525 uri = g_strdup (location);
526 } else {
527 uri = file_uri_from_local_relative_path (location);
531 return uri;
534 static gboolean
535 have_uid (guint uid)
537 return (uid == getuid ());
540 static gboolean
541 have_gid (guint gid)
543 gid_t gids[100];
544 int n_groups, i;
546 n_groups = getgroups (100, gids);
548 for (i = 0; i < n_groups; i++)
550 if (gids[i] == getegid ())
551 continue;
552 if (gids[i] == gid)
553 return TRUE;
556 return FALSE;
559 gboolean
560 rb_uri_is_readable (const char *text_uri)
562 GnomeVFSFileInfo *info;
563 gboolean ret = FALSE;
565 info = gnome_vfs_file_info_new ();
566 if (info == NULL)
567 return FALSE;
568 if (gnome_vfs_get_file_info (text_uri, info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS) != GNOME_VFS_OK)
569 return FALSE;
571 if ((info->permissions & GNOME_VFS_PERM_OTHER_READ) ||
572 ((info->permissions & GNOME_VFS_PERM_USER_READ) &&
573 (have_uid (info->uid) == TRUE)) ||
574 ((info->permissions & GNOME_VFS_PERM_GROUP_READ) &&
575 (have_gid (info->gid) == TRUE)))
576 ret = TRUE;
578 gnome_vfs_file_info_unref (info);
580 return ret;
583 gboolean
584 rb_uri_is_writable (const char *text_uri)
586 GnomeVFSFileInfo *info;
587 gboolean ret = FALSE;
589 info = gnome_vfs_file_info_new ();
590 if (info == NULL)
591 return FALSE;
592 if (gnome_vfs_get_file_info (text_uri, info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS) != GNOME_VFS_OK)
593 return FALSE;
595 if ((info->permissions & GNOME_VFS_PERM_OTHER_WRITE) ||
596 ((info->permissions & GNOME_VFS_PERM_USER_WRITE) &&
597 (have_uid (info->uid) == TRUE)) ||
598 ((info->permissions & GNOME_VFS_PERM_GROUP_WRITE) &&
599 (have_gid (info->gid) == TRUE)))
600 ret = TRUE;
602 gnome_vfs_file_info_unref (info);
604 return ret;
607 gboolean
608 rb_uri_is_local (const char *text_uri)
610 return g_str_has_prefix (text_uri, "file://");
614 * gnome_vfs_uri_new escapes a few extra characters that
615 * gnome_vfs_escape_path doesn't ('&' and '='). If we
616 * don't adjust our URIs to match, we end up with duplicate
617 * entries, one with the characters encoded and one without.
619 static char *
620 escape_extra_gnome_vfs_chars (char *uri)
622 if (strspn (uri, "&=") != strlen (uri)) {
623 char *tmp = gnome_vfs_escape_set (uri, "&=");
624 g_free (uri);
625 return tmp;
628 return uri;
631 typedef struct {
632 char *uri;
633 RBUriRecurseFunc func;
634 gpointer user_data;
635 gboolean *cancel_flag;
636 GDestroyNotify data_destroy;
637 } RBUriHandleRecursivelyData;
639 typedef struct {
640 RBUriHandleRecursivelyData data;
642 /* real data */
643 RBUriRecurseFunc func;
644 gpointer user_data;
646 GMutex *results_lock;
647 guint results_idle_id;
648 GList *uri_results;
649 GList *dir_results;
650 } RBUriHandleRecursivelyAsyncData;
652 static void
653 _rb_uri_recurse_data_free (RBUriHandleRecursivelyData *data)
655 g_free (data->uri);
656 if (data->data_destroy)
657 data->data_destroy (data->user_data);
659 g_free (data);
662 static gboolean
663 rb_uri_handle_recursively_cb (const gchar *rel_path,
664 GnomeVFSFileInfo *info,
665 gboolean recursing_will_loop,
666 RBUriHandleRecursivelyData *data,
667 gboolean *recurse)
669 char *path, *escaped_rel_path;
670 gboolean dir;
672 dir = (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY);
674 if (data->cancel_flag && *data->cancel_flag)
675 return TRUE;
677 /* skip hidden and unreadable files and directories */
678 if (g_str_has_prefix (rel_path, ".") ||
679 ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_ACCESS) &&
680 !(info->permissions & GNOME_VFS_PERM_ACCESS_READABLE))) {
681 *recurse = FALSE;
682 return TRUE;
685 escaped_rel_path = gnome_vfs_escape_path_string (rel_path);
686 escaped_rel_path = escape_extra_gnome_vfs_chars (escaped_rel_path);
687 path = g_build_filename (data->uri, escaped_rel_path, NULL);
688 (data->func) (path, dir, data->user_data);
689 g_free (escaped_rel_path);
690 g_free (path);
692 *recurse = !recursing_will_loop;
693 return TRUE;
696 void
697 rb_uri_handle_recursively (const char *text_uri,
698 RBUriRecurseFunc func,
699 gboolean *cancelflag,
700 gpointer user_data)
702 RBUriHandleRecursivelyData *data = g_new0 (RBUriHandleRecursivelyData, 1);
703 GnomeVFSFileInfoOptions flags;
704 GnomeVFSResult result;
706 data->uri = g_strdup (text_uri);
707 data->func = func;
708 data->user_data = user_data;
709 data->cancel_flag = cancelflag;
710 data->data_destroy = NULL;
712 flags = GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
713 GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE |
714 GNOME_VFS_FILE_INFO_FOLLOW_LINKS |
715 GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS;
716 result = gnome_vfs_directory_visit (text_uri,
717 flags,
718 GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK,
719 (GnomeVFSDirectoryVisitFunc)rb_uri_handle_recursively_cb,
720 data);
721 _rb_uri_recurse_data_free (data);
724 /* runs in main thread */
725 static gboolean
726 _recurse_async_idle_cb (RBUriHandleRecursivelyAsyncData *data)
728 GList *ul, *dl;
730 g_mutex_lock (data->results_lock);
732 for (ul = data->uri_results, dl = data->dir_results;
733 ul != NULL;
734 ul = g_list_next (ul), dl = g_list_next (dl)) {
735 g_assert (dl != NULL);
737 data->func ((const char*)ul->data, (GPOINTER_TO_INT (dl->data) == 1), data->user_data);
738 g_free (ul->data);
740 g_assert (dl == NULL);
743 g_list_free (data->uri_results);
744 data->uri_results = NULL;
745 g_list_free (data->dir_results);
746 data->dir_results = NULL;
748 data->results_idle_id = 0;
749 g_mutex_unlock (data->results_lock);
750 return FALSE;
753 /* runs in main thread */
754 static gboolean
755 _recurse_async_data_free (RBUriHandleRecursivelyAsyncData *data)
757 if (data->results_idle_id) {
758 g_source_remove (data->results_idle_id);
759 _recurse_async_idle_cb (data); /* process last results */
762 g_list_free (data->uri_results);
763 data->uri_results = NULL;
764 g_list_free (data->dir_results);
765 data->dir_results = NULL;
767 g_mutex_free (data->results_lock);
768 _rb_uri_recurse_data_free (&data->data);
770 return FALSE;
773 /* runs in worker thread */
774 static void
775 _recurse_async_cb (const char *uri, gboolean dir, RBUriHandleRecursivelyAsyncData *data)
777 g_mutex_lock (data->results_lock);
779 data->uri_results = g_list_prepend (data->uri_results, g_strdup (uri));
780 data->dir_results = g_list_prepend (data->dir_results, GINT_TO_POINTER (dir ? 1 : 0));
781 if (data->results_idle_id == 0)
782 g_idle_add ((GSourceFunc)_recurse_async_idle_cb, data);
784 g_mutex_unlock (data->results_lock);
787 static gpointer
788 _recurse_async_func (RBUriHandleRecursivelyAsyncData *data)
790 GnomeVFSFileInfoOptions flags;
791 GnomeVFSResult result;
793 flags = GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
794 GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE |
795 GNOME_VFS_FILE_INFO_FOLLOW_LINKS |
796 GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS;
797 result = gnome_vfs_directory_visit (data->data.uri,
798 flags,
799 GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK,
800 (GnomeVFSDirectoryVisitFunc)rb_uri_handle_recursively_cb,
801 &data->data);
803 g_idle_add ((GSourceFunc)_recurse_async_data_free, data);
804 return NULL;
807 void
808 rb_uri_handle_recursively_async (const char *text_uri,
809 RBUriRecurseFunc func,
810 gboolean *cancelflag,
811 gpointer user_data,
812 GDestroyNotify data_destroy)
814 RBUriHandleRecursivelyAsyncData *data = g_new0 (RBUriHandleRecursivelyAsyncData, 1);
816 data->data.uri = g_strdup (text_uri);
817 data->func = (RBUriRecurseFunc)_recurse_async_cb;
818 data->data.user_data = user_data;
819 data->data.cancel_flag = cancelflag;
820 data->data.data_destroy = data_destroy;
822 data->results_lock = g_mutex_new ();
823 data->data.func = func;
824 data->user_data = data;
826 g_thread_create ((GThreadFunc)_recurse_async_func, data, FALSE, NULL);
830 GnomeVFSResult
831 rb_uri_mkstemp (const char *prefix, char **uri_ret, GnomeVFSHandle **ret)
833 GnomeVFSHandle *handle = NULL;
834 char *uri = NULL;
835 GnomeVFSResult result = GNOME_VFS_ERROR_FILE_EXISTS;
838 do {
839 g_free (uri);
840 uri = g_strdup_printf ("%s%06X", prefix, g_random_int_range (0, 0xFFFFFF));
841 result = gnome_vfs_create (&handle, uri, GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_RANDOM, TRUE, 0644);
842 } while (result == GNOME_VFS_ERROR_FILE_EXISTS);
844 if (result == GNOME_VFS_OK) {
845 *uri_ret = uri;
846 *ret = handle;
847 } else {
848 g_free (uri);
850 return result;
854 char *
855 rb_canonicalise_uri (const char *uri)
857 char *result = NULL;
859 if (uri[0] == '/') {
860 /* local path */
861 char *tmp;
862 result = gnome_vfs_make_path_name_canonical (uri);
863 tmp = gnome_vfs_escape_path_string (result);
864 g_free (result);
865 tmp = escape_extra_gnome_vfs_chars (tmp);
866 result = g_strconcat ("file://", tmp, NULL);
867 g_free (tmp);
868 } else if (g_str_has_prefix (uri, "file://")) {
869 /* local file, rhythmdb wants this path escaped */
870 char *tmp1, *tmp2;
871 tmp1 = gnome_vfs_unescape_string (uri + 7, NULL); /* ignore "file://" */
872 tmp2 = gnome_vfs_escape_path_string (tmp1);
873 g_free (tmp1);
874 tmp2 = escape_extra_gnome_vfs_chars (tmp2);
875 result = g_strconcat ("file://", tmp2, NULL); /* re-add scheme */
876 g_free (tmp2);
877 } else {
878 GnomeVFSURI *vfsuri = gnome_vfs_uri_new (uri);
880 if (vfsuri != NULL) {
881 /* non-local uri, leave as-is */
882 gnome_vfs_uri_unref (vfsuri);
883 result = g_strdup (uri);
884 } else {
885 /* this may just mean that gnome-vfs doesn't recognise the
886 * uri scheme, so return it as is */
887 rb_debug ("Error processing probable URI %s", uri);
888 result = g_strdup (uri);
892 return result;
895 char*
896 rb_uri_append_path (const char *uri, const char *path)
898 GnomeVFSURI *vfs_uri, *full_uri;
899 char *result;
901 vfs_uri = gnome_vfs_uri_new (uri);
902 if (vfs_uri == NULL) {
903 return NULL;
906 full_uri = gnome_vfs_uri_append_path (vfs_uri, path);
907 gnome_vfs_uri_unref (vfs_uri);
908 result = gnome_vfs_uri_to_string (full_uri, GNOME_VFS_URI_HIDE_NONE);
909 gnome_vfs_uri_unref (full_uri);
911 return result;
914 char*
915 rb_uri_append_uri (const char *uri, const char *fragment)
917 GnomeVFSURI *vfs_uri, *full_uri;
918 char *result;
920 vfs_uri = gnome_vfs_uri_new (uri);
921 if (vfs_uri == NULL) {
922 return NULL;
925 full_uri = gnome_vfs_uri_append_string (vfs_uri, fragment);
926 gnome_vfs_uri_unref (vfs_uri);
927 result = gnome_vfs_uri_to_string (full_uri, GNOME_VFS_URI_HIDE_NONE);
928 gnome_vfs_uri_unref (full_uri);
930 return result;
933 char *
934 rb_uri_get_dir_name (const char *uri)
936 GnomeVFSURI *vfs_uri;
937 char *dirname;
939 vfs_uri = gnome_vfs_uri_new (uri);
940 if (vfs_uri == NULL) {
941 return NULL;
944 dirname = gnome_vfs_uri_extract_dirname (vfs_uri);
945 gnome_vfs_uri_unref (vfs_uri);
947 return dirname;
950 char *
951 rb_uri_get_short_path_name (const char *uri)
953 const char *start;
954 const char *end;
956 if (uri == NULL)
957 return NULL;
959 /* skip query string */
960 end = g_utf8_strchr (uri, -1, '?');
962 start = g_utf8_strrchr (uri, end ? (end - uri) : -1, GNOME_VFS_URI_PATH_CHR);
963 if (start == NULL) {
964 /* no separator, just a single file name */
965 } else if ((start + 1 == end) || *(start + 1) == '\0') {
966 /* last character is the separator, so find the previous one */
967 end = start;
968 start = g_utf8_strrchr (uri, (end - uri)-1, GNOME_VFS_URI_PATH_CHR);
970 if (start != NULL)
971 start++;
972 } else {
973 start++;
976 if (start == NULL)
977 start = uri;
979 if (end == NULL) {
980 return g_strdup (start);
981 } else {
982 return g_strndup (start, (end - start));