3
* Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org>
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Library 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
* Library General Public License for more details.
15
* You should have received a copy of the GNU Library General Public
16
* License along with this library; if not, write to the
17
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
* Boston, MA 02111-1307, USA.
35
/* implement thunar-vfs-path's inline functions */
36
#define G_IMPLEMENT_INLINES 1
37
#define __THUNAR_VFS_PATH_C__
38
#include <thunar-vfs/thunar-vfs-path.h>
40
#include <thunar-vfs/thunar-vfs-io-trash.h>
41
#include <thunar-vfs/thunar-vfs-private.h>
42
#include <thunar-vfs/thunar-vfs-util.h>
43
#include <thunar-vfs/thunar-vfs-alias.h>
47
/* Masks to handle the 4-byte aligned path names */
48
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
49
#define THUNAR_VFS_PATH_MASK (((gsize) 0xffu) << ((sizeof (gsize) - 1) * 8))
50
#define THUNAR_VFS_PATH_ROOT (0x2fu)
51
#elif G_BYTE_ORDER == G_BIG_ENDIAN
52
#define THUNAR_VFS_PATH_MASK (0xffu)
53
#define THUNAR_VFS_PATH_ROOT (((gsize) 0x2fu) << ((sizeof (gsize) - 1) * 8))
55
#error "Unsupported endianess"
60
static guint thunar_vfs_path_escape_uri_length (const ThunarVfsPath *path);
61
static guint thunar_vfs_path_escape_uri (const ThunarVfsPath *path,
66
/* A table of the ASCII chars from space (32) to DEL (127) */
67
static const guchar ACCEPTABLE_URI_CHARS[96] = {
68
/* ! " # $ % & ' ( ) * + , - . / */
69
0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
70
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
71
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
72
/* @ A B C D E F G H I J K L M N O */
73
0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
74
/* P Q R S T U V W X Y Z [ \ ] ^ _ */
75
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
76
/* ` a b c d e f g h i j k l m n o */
77
0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
78
/* p q r s t u v w x y z { | } ~ DEL */
79
0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
82
/* List of hexadecimal characters */
83
static const gchar HEX_CHARS[16] = "0123456789ABCDEF";
85
#define ACCEPTABLE_URI_CHAR(c) ((c) >= 32 && (c) < 128 && (ACCEPTABLE_URI_CHARS[(c) - 32] & 0x08))
89
/* Debugging support in ThunarVfsPath */
92
G_LOCK_DEFINE_STATIC (debug_paths);
93
static GList *debug_paths = NULL;
95
#define THUNAR_VFS_PATH_DEBUG_INSERT(path) \
97
G_LOCK (debug_paths); \
98
debug_paths = g_list_prepend (debug_paths, (path)); \
99
G_UNLOCK (debug_paths); \
102
#define THUNAR_VFS_PATH_DEBUG_REMOVE(path) \
104
G_LOCK (debug_paths); \
105
debug_paths = g_list_remove (debug_paths, (path)); \
106
G_UNLOCK (debug_paths); \
109
#define THUNAR_VFS_PATH_DEBUG_SHUTDOWN() \
111
if (G_UNLIKELY (debug_paths != NULL)) \
116
G_LOCK (debug_paths); \
117
g_print ("--- Leaked a total of %u ThunarVfsPath objects:\n", g_list_length (debug_paths)); \
118
for (lp = debug_paths; lp != NULL; lp = lp->next) \
120
uri = thunar_vfs_path_dup_string (lp->data); \
121
n = ((ThunarVfsPath *) lp->data)->ref_count; \
122
g_print ("--> %s (%d)\n", uri, (n & ~THUNAR_VFS_PATH_SCHEME_MASK)); \
125
G_UNLOCK (debug_paths); \
129
#else /* !G_ENABLE_DEBUG */
131
#define THUNAR_VFS_PATH_DEBUG_INSERT(path) G_STMT_START{ (void)0; }G_STMT_END
132
#define THUNAR_VFS_PATH_DEBUG_REMOVE(path) G_STMT_START{ (void)0; }G_STMT_END
133
#define THUNAR_VFS_PATH_DEBUG_SHUTDOWN() G_STMT_START{ (void)0; }G_STMT_END
135
#endif /* !G_ENABLE_DEBUG */
139
/* components of the path to the users home folder */
140
static ThunarVfsPath **home_components;
141
static guint n_home_components;
144
* _thunar_vfs_path_trash_root:
146
* The shared instance of the #ThunarVfsPath that points to the
149
ThunarVfsPath *_thunar_vfs_path_trash_root = NULL;
154
thunar_vfs_path_escape_uri_length (const ThunarVfsPath *path)
160
/* determine the base length for the scheme (file:/// or trash:///) */
161
length = base_length = _thunar_vfs_path_is_local (path) ? 8 : 9;
163
/* determine the length for the path part */
164
for (; path->parent != NULL; path = path->parent)
166
/* prepend a path separator */
167
if (length > base_length)
170
for (s = (const guchar *) thunar_vfs_path_get_name (path); *s != '\0'; ++s)
171
length += ACCEPTABLE_URI_CHAR (*s) ? 1 : 3;
180
thunar_vfs_path_escape_uri (const ThunarVfsPath *path,
183
typedef struct _ThunarVfsPathItem
185
const ThunarVfsPath *path;
186
struct _ThunarVfsPathItem *next;
189
ThunarVfsPathItem *item;
190
ThunarVfsPathItem *root = NULL;
195
/* prepend 'trash:///' or 'file:///' string (using a simple optimization on i386/ppc) */
196
if (G_LIKELY (thunar_vfs_path_get_scheme (path) == THUNAR_VFS_PATH_SCHEME_FILE))
198
#if defined(__GNUC__) && (defined(__i386__) || defined(__ppc__))
199
((guint32 *) buffer)[0] = ((const guint32 *) "file:///")[0];
200
((guint32 *) buffer)[1] = ((const guint32 *) "file:///")[1];
202
/* hopefully the compiler will be able to optimize this */
203
buffer[0] = 'f'; buffer[1] = 'i';
204
buffer[2] = 'l'; buffer[3] = 'e';
205
buffer[4] = ':'; buffer[5] = '/';
206
buffer[6] = '/'; buffer[7] = '/';
212
#if defined(__GNUC__) && (defined(__i386__) || defined(__ppc__))
213
((guint32 *) buffer)[0] = ((const guint32 *) "trash://")[0];
214
((guint32 *) buffer)[1] = ((const guint32 *) "trash://")[1];
216
/* hopefully the compiler will be able to optimize this */
217
buffer[0] = 't'; buffer[1] = 'r';
218
buffer[2] = 'a'; buffer[3] = 's';
219
buffer[4] = 'h'; buffer[5] = ':';
220
buffer[6] = '/'; buffer[7] = '/';
226
/* generate the path item list (reverse parent relation) */
227
for (; path->parent != NULL; path = path->parent)
229
item = g_newa (ThunarVfsPathItem, 1);
235
/* generate the uri */
236
for (item = root; item != NULL; item = item->next)
238
/* append a '/' character */
239
if (G_LIKELY (item != root))
242
/* copy the path component name */
243
for (s = thunar_vfs_path_get_name (item->path); *s != '\0'; ++s)
245
c = *((const guchar *) s);
246
if (G_UNLIKELY (!ACCEPTABLE_URI_CHAR (c)))
249
*t++ = HEX_CHARS[c >> 4];
250
*t++ = HEX_CHARS[c & 15];
259
/* zero-terminate the URI */
262
return (t - buffer) + 1;
268
thunar_vfs_path_get_type (void)
270
static GType type = G_TYPE_INVALID;
272
if (G_UNLIKELY (type == G_TYPE_INVALID))
274
type = g_boxed_type_register_static (I_("ThunarVfsPath"),
275
(GBoxedCopyFunc) thunar_vfs_path_ref,
276
(GBoxedFreeFunc) thunar_vfs_path_unref);
285
thunar_vfs_path_list_get_type (void)
287
static GType type = G_TYPE_INVALID;
289
if (G_UNLIKELY (type == G_TYPE_INVALID))
291
type = g_boxed_type_register_static (I_("ThunarVfsPathList"),
292
(GBoxedCopyFunc) thunar_vfs_path_list_copy,
293
(GBoxedFreeFunc) thunar_vfs_path_list_free);
302
* thunar_vfs_path_new:
303
* @identifier : an URI identifier or an absolute path.
304
* @error : return location for errors or %NULL.
306
* Returns a #ThunarVfsPath that represents the given
307
* @identifier or %NULL on error. In the latter case
308
* @error will be set to point to an #GError describing
311
* The caller is responsible to free the returned
312
* object using thunar_vfs_path_unref() when no
315
* Return value: the #ThunarVfsPath for @identifier
319
thunar_vfs_path_new (const gchar *identifier,
322
ThunarVfsPath *path = home_components[0]; /* default to file system root */
330
/* check if we have an absolute path or an URI */
331
if (G_UNLIKELY (*identifier != G_DIR_SEPARATOR))
333
/* treat the identifier as URI */
334
filename = g_filename_from_uri (identifier, NULL, NULL);
335
if (G_UNLIKELY (filename == NULL))
337
/* hey, but maybe it's a trash:-URI */
338
if (G_LIKELY (identifier[0] == 't' && identifier[1] == 'r' && identifier[2] == 'a'
339
&& identifier[3] == 's' && identifier[4] == 'h' && identifier[5] == ':'))
341
/* skip slashes (yes, not dir separators) */
342
for (s = identifier + 6; *s == '/'; ++s)
345
/* start at the trash root folder */
346
path = _thunar_vfs_path_trash_root;
348
/* check if it's the trash root folder */
349
if (G_LIKELY (*s == '\0'))
350
return thunar_vfs_path_ref (path);
352
/* try to interpret the file part */
353
t = g_strconcat ("file:/", s, NULL);
354
filename = g_filename_from_uri (t, NULL, NULL);
359
/* check if the URI is invalid */
360
if (G_UNLIKELY (filename == NULL))
362
g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI, _("The URI \"%s\" is invalid"), identifier);
368
/* canonicalize the absolute path, to remove additional slashes and dots */
369
filename = thunar_vfs_canonicalize_filename (identifier);
372
/* parse the filename to a ThunarVfsPath */
375
/* local paths may be relative to a home component */
376
if (G_LIKELY (path == home_components[0]))
378
/* start at the root path */
379
for (n = 1; n < n_home_components; ++n)
381
/* skip additional slashes */
382
for (; G_UNLIKELY (*s == G_DIR_SEPARATOR); ++s)
385
/* check if we have reached the end of the filename */
386
if (G_UNLIKELY (*s == '\0'))
389
/* check if the path component equals the next home path component */
390
for (s1 = thunar_vfs_path_get_name (home_components[n]), s2 = s; *s1 != '\0' && *s1 == *s2; ++s1, ++s2)
392
if (*s1 != '\0' || (*s2 != '\0' && *s2 != G_DIR_SEPARATOR))
395
/* go on with the next home path component */
396
path = home_components[n];
401
/* determine the subpath (takes appropriate references) */
402
path = _thunar_vfs_path_new_relative (path, s);
413
* thunar_vfs_path_get_for_home:
415
* Returns the #ThunarVfsPath that represents
416
* the current users home directory.
418
* The caller is responsible to free the
419
* returned object using thunar_vfs_path_unref()
420
* when no longer needed.
422
* Return value: the #ThunarVfsPath for the
423
* current users home directory.
426
thunar_vfs_path_get_for_home (void)
428
return thunar_vfs_path_ref (home_components[n_home_components - 1]);
434
* thunar_vfs_path_get_for_root:
436
* Returns the #ThunarVfsPath that represents the
437
* file systems root folder.
439
* The caller is responsible to free the returned
440
* object using thunar_vfs_path_unref() when no
443
* Return value: the #ThunarVfsPath for the file
444
* systems root directory.
447
thunar_vfs_path_get_for_root (void)
449
return thunar_vfs_path_ref (home_components[0]);
455
* thunar_vfs_path_get_for_trash:
457
* Returns the #ThunarVfsPath that represents the
460
* The caller is responsible to free the returned
461
* object using thunar_vfs_path_unref() when no
464
* Return value: the #ThunarVfsPath for the trash
468
thunar_vfs_path_get_for_trash (void)
470
return thunar_vfs_path_ref (_thunar_vfs_path_trash_root);
476
* thunar_vfs_path_unref:
477
* @path : a #ThunarVfsPath.
479
* Decreases the reference count on @path and
480
* frees the resources allocated for @path
481
* once the reference count drops to zero.
484
thunar_vfs_path_unref (ThunarVfsPath *path)
486
ThunarVfsPath *parent;
489
while (path != NULL && (g_atomic_int_exchange_and_add (&path->ref_count, -1) & ~THUNAR_VFS_PATH_SCHEME_MASK) == 1)
491
/* verify that we don't free the paths for trash root or home components */
492
#ifdef G_ENABLE_DEBUG
493
if (G_UNLIKELY (path == _thunar_vfs_path_trash_root))
495
/* the trash root path may not be freed */
496
g_error (G_STRLOC ": Attempt to free the trash root path detected");
500
/* same for the home component paths */
502
for (n = 0; n < n_home_components; ++n)
503
if (G_UNLIKELY (path == home_components[n]))
506
/* check if one of the home components matched */
507
if (G_UNLIKELY (n < n_home_components))
509
/* none of the home component paths can be freed this way */
510
g_error (G_STRLOC ": Attempt to free the home component path \"%s\" detected", thunar_vfs_path_get_name (path));
515
/* remember the parent path */
516
parent = path->parent;
518
/* remove the path from the debug list */
519
THUNAR_VFS_PATH_DEBUG_REMOVE (path);
521
/* release the path resources (we need to determine the size for the slice allocator) */
522
for (p = (const gsize *) thunar_vfs_path_get_name (path); (*p & THUNAR_VFS_PATH_MASK) != 0u; ++p)
524
_thunar_vfs_slice_free1 (((const guint8 *) (p + 1)) - ((const guint8 *) path), path);
526
/* continue with the parent */
534
* thunar_vfs_path_hash:
535
* @path_ptr : a #ThunarVfsPath.
537
* Generates a hash value for the given @path_ptr.
539
* Return value: the hash value for @path_ptr.
542
thunar_vfs_path_hash (gconstpointer path_ptr)
544
const gchar *p = thunar_vfs_path_get_name (path_ptr);
545
guint h = *p + thunar_vfs_path_get_scheme (path_ptr);
547
/* hash the last path component (which cannot be empty) */
549
h = (h << 5) - h + *p;
557
* thunar_vfs_path_equal:
558
* @path_ptr_a : first #ThunarVfsPath.
559
* @path_ptr_b : second #ThunarVfsPath.
561
* Checks whether @path_ptr_a and @path_ptr_b refer
562
* to the same local path.
564
* Return value: %TRUE if @path_ptr_a and @path_ptr_b
568
thunar_vfs_path_equal (gconstpointer path_ptr_a,
569
gconstpointer path_ptr_b)
571
const ThunarVfsPath *path_a = path_ptr_a;
572
const ThunarVfsPath *path_b = path_ptr_b;
576
/* compare the schemes */
577
if (thunar_vfs_path_get_scheme (path_a) != thunar_vfs_path_get_scheme (path_b))
581
/* check if the paths are the same object */
582
if (G_UNLIKELY (path_a == path_b))
585
/* compare the last path component */
586
a = (const gsize *) thunar_vfs_path_get_name (path_a);
587
b = (const gsize *) thunar_vfs_path_get_name (path_b);
592
else if ((*a & THUNAR_VFS_PATH_MASK) == 0u)
599
/* compare the parent path components */
600
if (G_LIKELY (path_a->parent != NULL && path_b->parent != NULL))
602
path_a = path_a->parent;
603
path_b = path_b->parent;
607
/* verify that both paths have no parents then */
608
return (path_a->parent == NULL && path_b->parent == NULL);
614
* thunar_vfs_path_relative:
615
* @parent : a #ThunarVfsPath.
616
* @name : a valid filename in the local file system encoding.
618
* Returns a #ThunarVfsPath for the file @name relative to
619
* @parent. @name must be a valid filename in the local file
620
* system encoding and it may not contain any slashes.
622
* The caller is responsible to free the returned object
623
* using thunar_vfs_path_unref() when no longer needed.
625
* Return value: the path to @name relative to @parent.
628
thunar_vfs_path_relative (ThunarVfsPath *parent,
631
g_return_val_if_fail (parent != NULL, NULL);
632
g_return_val_if_fail (name != NULL, NULL);
633
g_return_val_if_fail (*name != '\0', NULL);
634
g_return_val_if_fail (strchr (name, '/') == NULL, NULL);
636
/* let _thunar_vfs_path_child() do it's work */
637
return _thunar_vfs_path_child (parent, name);
643
* thunar_vfs_path_is_ancestor:
644
* @path : a #ThunarVfsPath.
645
* @ancestor : another #ThunarVfsPath.
647
* Determines whether @path is somewhere below @ancestor,
648
* possible with intermediate folders.
650
* Return value: %TRUE if @ancestor contains @path as a
651
* child, grandchild, great grandchild, etc.
654
thunar_vfs_path_is_ancestor (const ThunarVfsPath *path,
655
const ThunarVfsPath *ancestor)
657
g_return_val_if_fail (path != NULL, FALSE);
658
g_return_val_if_fail (ancestor != NULL, FALSE);
660
for (path = path->parent; path != NULL; path = path->parent)
661
if (thunar_vfs_path_equal (path, ancestor))
670
* thunar_vfs_path_is_home:
671
* @path : a #ThunarVfsPath.
673
* Checks whether @path refers to the users home
676
* Return value: %TRUE if @path refers to the users
680
thunar_vfs_path_is_home (const ThunarVfsPath *path)
682
g_return_val_if_fail (path != NULL, FALSE);
683
return (path == home_components[n_home_components - 1]);
689
* thunar_vfs_path_dup_string:
690
* @path : a #ThunarVfsPath.
692
* Like thunar_vfs_path_to_string(), this function transform
693
* the @path to its string representation, but unlike
694
* thunar_vfs_path_to_string(), this function automatically
695
* allocates the required amount of memory from the heap.
696
* The returned string must be freed by the caller when
699
* Return value: the string representation of @path.
702
thunar_vfs_path_dup_string (const ThunarVfsPath *path)
704
const ThunarVfsPath *p;
708
/* determine the number of bytes required to
709
* store the path's string representation.
711
for (n = 0, p = path; p != NULL; p = p->parent)
712
n += strlen (thunar_vfs_path_get_name (p)) + 2;
714
/* allocate the buffer to store the string */
717
/* store the path string to the buffer */
718
thunar_vfs_path_to_string (path, s, n, NULL);
720
/* return the string buffer */
727
* thunar_vfs_path_to_string:
728
* @path : a #ThunarVfsPath.
729
* @buffer : the buffer to store the path string to.
730
* @bufsize : the size of @buffer in bytes.
731
* @error : return location for errors or %NULL.
733
* Stores the @path into the string pointed to by @buffer,
734
* so it can be used for system path operations. Returns
735
* the number of bytes stored to @buffer or a negative
736
* value if @bufsize is too small to store the whole @path.
737
* In the latter case @error will be set to point to an
738
* error describing the problem.
740
* If @buffer is allocated on the stack, it is suggested
741
* to use #THUNAR_VFS_PATH_MAXSTRLEN for the buffer size
742
* in most cases. The stack should never be used in recursive
743
* functions; use thunar_vfs_path_dup_string() instead there.
745
* Return value: the number of bytes (including the null
746
* byte) stored to @buffer or a negative
747
* value if @buffer cannot hold the whole
751
thunar_vfs_path_to_string (const ThunarVfsPath *path,
756
typedef struct _ThunarVfsPathItem
759
struct _ThunarVfsPathItem *next;
762
ThunarVfsPathItem *items = NULL;
763
ThunarVfsPathItem *item;
768
g_return_val_if_fail (buffer != NULL, -1);
769
g_return_val_if_fail (bufsize > 0, -1);
770
g_return_val_if_fail (error == NULL || *error == NULL, -1);
772
/* the root element is a special case to ease the processing */
773
if (G_UNLIKELY (path->parent == NULL))
775
if (G_UNLIKELY (bufsize < 2))
777
buffer[0] = G_DIR_SEPARATOR;
782
/* determine the buffer size required for the path buffer */
783
for (n = 1; path->parent != NULL; path = path->parent)
785
/* add the path to the item list */
786
item = g_newa (ThunarVfsPathItem, 1);
787
item->name = thunar_vfs_path_get_name (path);
791
/* add the size constraint (including the '/') */
792
n += strlen (item->name) + 1;
795
/* verify the buffer size */
796
if (G_UNLIKELY (bufsize < n))
799
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NAMETOOLONG,
800
_("Path too long to fit into buffer"));
804
/* generate the path string */
805
for (bp = buffer, item = items; item != NULL; item = item->next)
807
/* prepend the path separator */
808
*bp++ = G_DIR_SEPARATOR;
810
/* append the component name */
811
for (name = item->name; *name != '\0'; )
815
/* append the string terminator */
818
/* return the number of bytes written to the buffer */
825
* thunar_vfs_path_dup_uri:
826
* @path : a #ThunarVfsPath.
828
* Similar to thunar_vfs_path_to_uri(), but automatically
829
* allocates memory on the heap instead of using a user
830
* supplied buffer for the URI.
832
* The caller is responsible to free the returned string
833
* using g_free() when no longer needed.
835
* Return value: the escaped URI for @path.
838
thunar_vfs_path_dup_uri (const ThunarVfsPath *path)
844
g_return_val_if_fail (path != NULL, NULL);
846
/* calculate the length of the uri string */
847
n = thunar_vfs_path_escape_uri_length (path) + 1;
849
/* escape the path to an uri string */
850
s = g_malloc (sizeof (gchar) * n);
851
m = thunar_vfs_path_escape_uri (path, s);
853
/* verify the result */
854
_thunar_vfs_assert (strlen (s) == m - 1);
855
_thunar_vfs_assert (m == n);
863
* thunar_vfs_path_to_uri:
864
* @path : a #ThunarVfsPath.
865
* @buffer : the buffer to store the URI string to.
866
* @bufsize : the size of @buffer in bytes.
867
* @error : return location for errors or %NULL.
869
* Escapes @path according to the rules of the file URI
870
* specification and stores the escaped URI to @buffer.
871
* Returns the number of bytes stored to @buffer or a
872
* negative value if @bufsize is too small to store the
873
* escaped URI. In the latter case @error will be set to
874
* point to an #GError describing the problem.
876
* When using the stack for @buffer, it is suggested to
877
* use #THUNAR_VFS_PATH_MAXURILEN for the buffer size in
878
* most cases. The stack should never be used in recursive
879
* functions; use thunar_vfs_path_dup_uri() instead there.
881
* Return value: the number of bytes (including the null
882
* byte) stored to @buffer or a negative
883
* value if @buffer cannot hold the URI.
886
thunar_vfs_path_to_uri (const ThunarVfsPath *path,
894
g_return_val_if_fail (path != NULL, -1);
895
g_return_val_if_fail (buffer != NULL, -1);
896
g_return_val_if_fail (error == NULL || *error == NULL, -1);
898
/* verify that the buffer is large enough */
899
n = thunar_vfs_path_escape_uri_length (path) + 1;
900
if (G_UNLIKELY (bufsize < n))
902
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NAMETOOLONG,
903
_("URI too long to fit into buffer"));
907
/* copy the URI to the buffer */
908
m = thunar_vfs_path_escape_uri (path, buffer);
910
/* verify the result */
911
_thunar_vfs_assert (strlen (buffer) == m - 1);
912
_thunar_vfs_assert (m == n);
920
* thunar_vfs_path_list_from_string:
921
* @uri_string : a string representation of an URI list.
922
* @error : return location for errors.
924
* Splits an URI list conforming to the text/uri-list
925
* mime type defined in RFC 2483 into individual URIs,
926
* discarding any comments and whitespace.
928
* If all URIs were successfully parsed into #ThunarVfsPath
929
* objects, the list of parsed URIs will be returned, and
930
* you'll need to call thunar_vfs_path_list_free() to
931
* release the list resources. Else if the parsing fails
932
* at some point, %NULL will be returned and @error will
933
* be set to describe the cause.
935
* Note, that if @string contains no URIs, this function
936
* will also return %NULL, but @error won't be set. So
937
* take care when checking for an error condition!
939
* Return value: the list of #ThunarVfsPath's or %NULL.
942
thunar_vfs_path_list_from_string (const gchar *uri_string,
948
GList *path_list = NULL;
951
for (s = uri_string; s != NULL; )
955
while (g_ascii_isspace (*s))
958
for (t = s; *t != '\0' && *t != '\n' && *t != '\r'; ++t)
963
for (t--; t > s && g_ascii_isspace (*t); t--)
968
/* try to parse the URI */
969
identifier = g_strndup (s, t - s + 1);
970
path = thunar_vfs_path_new (identifier, error);
973
/* check if we succeed */
974
if (G_UNLIKELY (path == NULL))
976
thunar_vfs_path_list_free (path_list);
981
/* append the newly parsed path */
982
path_list = g_list_append (path_list, path);
988
for (; *s != '\0' && *s != '\n'; ++s)
1001
* thunar_vfs_path_list_to_string:
1002
* @path_list : a list of #ThunarVfsPath<!---->s.
1004
* Free the returned value using g_free() when you
1007
* Return value: the string representation of @path_list conforming to the
1008
* text/uri-list mime type defined in RFC 2483.
1011
thunar_vfs_path_list_to_string (GList *path_list)
1014
gsize bufsize = 512;
1019
/* allocate initial buffer */
1020
buffer = g_malloc (bufsize + 1);
1022
for (lp = path_list; lp != NULL; lp = lp->next)
1026
/* determine the size required to store the URI and the line break */
1027
n = thunar_vfs_path_escape_uri_length (lp->data) + 2;
1028
if (n > (bufsize - bufpos))
1030
/* automatically increase the buffer */
1032
buffer = g_realloc (buffer, bufsize + 1);
1036
/* append the URI to the buffer */
1037
n = thunar_vfs_path_escape_uri (lp->data, buffer + bufpos);
1039
/* shift the buffer position */
1042
/* append a line break */
1043
buffer[bufpos++] = '\r';
1044
buffer[bufpos++] = '\n';
1047
_thunar_vfs_assert (bufpos <= bufsize);
1052
/* zero terminate the string */
1053
buffer[bufpos] = '\0';
1061
* thunar_vfs_path_list_copy:
1062
* @path_list : a list of #ThunarVfsPath<!---->s.
1064
* Takes a deep copy of @path_list and returns the
1065
* result. The caller is responsible to free the
1066
* returned list using thunar_vfs_path_list_free().
1068
* Return value: a deep copy of @path_list.
1071
thunar_vfs_path_list_copy (GList *path_list)
1076
for (list = NULL, lp = g_list_last (path_list); lp != NULL; lp = lp->prev)
1077
list = g_list_prepend (list, thunar_vfs_path_ref (lp->data));
1085
* thunar_vfs_path_list_free:
1086
* @path_list : a list of #ThunarVfsPath<!---->s.
1088
* Frees the #ThunarVfsPath<!---->s in @path_list and
1089
* the @path_list itself.
1092
thunar_vfs_path_list_free (GList *path_list)
1095
for (lp = path_list; lp != NULL; lp = lp->next)
1096
thunar_vfs_path_unref (lp->data);
1097
g_list_free (path_list);
1103
* _thunar_vfs_path_init:
1105
* Intialize the #ThunarVfsPath module.
1108
_thunar_vfs_path_init (void)
1110
ThunarVfsPath *path;
1119
_thunar_vfs_return_if_fail (home_components == NULL);
1120
_thunar_vfs_return_if_fail (n_home_components == 0);
1122
/* include the root element */
1123
n_bytes = sizeof (ThunarVfsPath) + sizeof (gsize);
1124
n_home_components = 1;
1126
/* split the home path into its components */
1127
components = g_strsplit (g_get_home_dir (), "/", -1);
1128
for (component = components; *component != NULL; ++component)
1129
if (G_LIKELY (**component != '\0'))
1131
n_bytes += sizeof (ThunarVfsPath) + ((strlen (*component) + sizeof (gsize)) / sizeof (gsize)) * sizeof (gsize);
1132
n_home_components += 1;
1135
/* allocate the memory (including the pointer table overhead) */
1136
home_components = g_malloc (n_bytes + n_home_components * sizeof (ThunarVfsPath *));
1137
offset = ((gchar *) home_components) + n_home_components * sizeof (ThunarVfsPath *);
1139
/* add the root node */
1140
path = (gpointer) offset;
1141
path->ref_count = 1;
1142
path->parent = NULL;
1143
home_components[0] = path;
1144
*((gsize *) thunar_vfs_path_get_name (path)) = THUNAR_VFS_PATH_ROOT;
1145
offset += sizeof (ThunarVfsPath) + sizeof (gsize);
1147
/* add the remaining path components */
1148
for (component = components; *component != NULL; ++component)
1149
if (G_LIKELY (**component != '\0'))
1151
/* setup the path basics */
1152
path = (gpointer) offset;
1153
path->ref_count = 1;
1154
path->parent = home_components[n];
1155
home_components[++n] = path;
1157
/* calculate the offset for the next home path component */
1158
offset += sizeof (ThunarVfsPath) + ((strlen (*component) + sizeof (gsize)) / sizeof (gsize)) * sizeof (gsize);
1161
for (s = *component, t = (gchar *) thunar_vfs_path_get_name (path); *s != '\0'; )
1164
/* fill the path with zeros */
1170
g_assert (n_home_components == n + 1);
1172
/* allocate the trash root path */
1173
_thunar_vfs_path_trash_root = g_malloc (sizeof (ThunarVfsPath) + sizeof (gsize));
1174
_thunar_vfs_path_trash_root->ref_count = 1 | THUNAR_VFS_PATH_SCHEME_TRASH;
1175
_thunar_vfs_path_trash_root->parent = NULL;
1176
*((gsize *) thunar_vfs_path_get_name (_thunar_vfs_path_trash_root)) = THUNAR_VFS_PATH_ROOT;
1179
g_strfreev (components);
1185
* _thunar_vfs_path_shutdown:
1187
* Shutdown the #ThunarVfsPath module.
1190
_thunar_vfs_path_shutdown (void)
1194
_thunar_vfs_return_if_fail (home_components != NULL);
1195
_thunar_vfs_return_if_fail (n_home_components != 0);
1197
/* print out the list of leaked paths */
1198
THUNAR_VFS_PATH_DEBUG_SHUTDOWN ();
1200
for (n = 0; n < n_home_components; ++n)
1201
g_assert (home_components[n]->ref_count == 1);
1203
g_free (home_components);
1204
home_components = NULL;
1205
n_home_components = 0;
1207
g_assert (_thunar_vfs_path_trash_root->ref_count == (1 | THUNAR_VFS_PATH_SCHEME_TRASH));
1208
g_free (_thunar_vfs_path_trash_root);
1209
_thunar_vfs_path_trash_root = NULL;
1215
* _thunar_vfs_path_new_relative:
1216
* @parent : the parent path.
1217
* @relative_path : a relative path, or the empty string.
1219
* Creates a new #ThunarVfsPath, which represents the subpath of @parent,
1220
* identified by the @relative_path. If @relative_path is the empty string,
1221
* a new reference to @parent will be returned.
1223
* The caller is responsible to free the returned #ThunarVfsPath object
1224
* using thunar_vfs_path_unref() when no longer needed.
1226
* Return value: the #ThunarVfsPath object to the subpath of @parent
1227
* identified by @relative_path.
1230
_thunar_vfs_path_new_relative (ThunarVfsPath *parent,
1231
const gchar *relative_path)
1233
ThunarVfsPath *path = parent;
1235
const gchar *s = relative_path;
1239
_thunar_vfs_return_val_if_fail (relative_path != NULL, NULL);
1240
_thunar_vfs_return_val_if_fail (parent != NULL, NULL);
1242
/* skip additional slashes */
1243
for (; G_UNLIKELY (*s == G_DIR_SEPARATOR); ++s)
1246
/* generate the additional path components (if any) */
1249
/* remember the current path as parent path */
1252
/* determine the length of the path component in bytes */
1253
for (s1 = s + 1; *s1 != '\0' && *s1 != G_DIR_SEPARATOR; ++s1)
1255
n = (((s1 - s) + sizeof (gsize)) / sizeof (gsize)) * sizeof (gsize)
1256
+ sizeof (ThunarVfsPath);
1258
/* allocate memory for the new path component */
1259
path = _thunar_vfs_slice_alloc (n);
1260
path->ref_count = thunar_vfs_path_get_scheme (parent);
1261
path->parent = thunar_vfs_path_ref (parent);
1263
/* insert the path into the debug list */
1264
THUNAR_VFS_PATH_DEBUG_INSERT (path);
1266
/* zero out the last word to have the name zero-terminated */
1267
*(((gsize *) (((gchar *) path) + n)) - 1) = 0;
1269
/* copy the path component name */
1270
for (t = (gchar *) thunar_vfs_path_get_name (path); *s != '\0' && *s != G_DIR_SEPARATOR; )
1273
/* skip additional slashes */
1274
for (; G_UNLIKELY (*s == G_DIR_SEPARATOR); ++s)
1278
/* return a reference to the path */
1279
return thunar_vfs_path_ref (path);
1285
* _thunar_vfs_path_child:
1286
* @parent : a #ThunarVfsPath.
1287
* @name : a valid filename in the local file system encoding.
1289
* Internal implementation of thunar_vfs_path_relative(), that performs
1290
* no external sanity checking of it's parameters.
1292
* Returns a #ThunarVfsPath for the file @name relative to
1293
* @parent. @name must be a valid filename in the local file
1294
* system encoding and it may not contain any slashes.
1296
* The caller is responsible to free the returned object
1297
* using thunar_vfs_path_unref() when no longer needed.
1299
* Return value: the child path to @name relative to @parent.
1302
_thunar_vfs_path_child (ThunarVfsPath *parent,
1305
ThunarVfsPath *path;
1310
_thunar_vfs_return_val_if_fail (parent != NULL, NULL);
1311
_thunar_vfs_return_val_if_fail (name != NULL, NULL);
1312
_thunar_vfs_return_val_if_fail (*name != '\0', NULL);
1313
_thunar_vfs_return_val_if_fail (strchr (name, '/') == NULL, NULL);
1315
/* check if parent is one of the home path components */
1316
for (n = n_home_components - 2; n >= 0; --n)
1317
if (G_UNLIKELY (home_components[n] == parent))
1319
/* check if the name equals the home path child component */
1320
if (strcmp (name, thunar_vfs_path_get_name (home_components[n + 1])) == 0)
1321
return thunar_vfs_path_ref (home_components[n + 1]);
1325
/* determine the length of the name in bytes */
1326
for (s = name + 1; *s != '\0'; ++s)
1328
n = (((s - name) + sizeof (gsize)) / sizeof (gsize)) * sizeof (gsize)
1329
+ sizeof (ThunarVfsPath);
1331
/* allocate memory for the new path component */
1332
path = _thunar_vfs_slice_alloc (n);
1333
path->ref_count = 1 | thunar_vfs_path_get_scheme (parent);
1334
path->parent = thunar_vfs_path_ref (parent);
1336
/* insert the path into the debug list */
1337
THUNAR_VFS_PATH_DEBUG_INSERT (path);
1339
/* zero out the last word to have the name zero-terminated */
1340
*(((gsize *) (((gchar *) path) + n)) - 1) = 0;
1342
/* copy the path component name */
1343
for (s = name, t = (gchar *) thunar_vfs_path_get_name (path); *s != '\0'; )
1352
* _thunar_vfs_path_dup_display_name:
1353
* @path : a #ThunarVfsPath.
1355
* Returns the display name for the @path<!---->s name. That said,
1356
* the method is similar to thunar_vfs_path_get_name(), but the
1357
* returned string is garantied to be valid UTF-8.
1359
* The caller is responsible to free the returned string using
1360
* g_free() when no longer needed.
1362
* Return value: a displayable variant of the @path<!---->s name.
1365
_thunar_vfs_path_dup_display_name (const ThunarVfsPath *path)
1367
return g_filename_display_name (thunar_vfs_path_get_name (path));
1373
* _thunar_vfs_path_translate:
1374
* @src_path : the source #ThunarVfsPath.
1375
* @dst_scheme : the destination #ThunarVfsPathScheme.
1376
* @error : return location for errors or %NULL.
1378
* Translates the @src_path to the specified @dst_scheme if possible.
1380
* If the @src_path is already in the @dst_scheme, a new reference
1381
* on @src_path will be returned.
1383
* The caller is responsible to free the returned #ThunarVfsPath using
1384
* thunar_vfs_path_unref() when no longer needed.
1386
* Return value: the #ThunarVfsPath that corresponds to @src_path in the
1387
* @dst_scheme, or %NULL on error.
1390
_thunar_vfs_path_translate (ThunarVfsPath *src_path,
1391
ThunarVfsPathScheme dst_scheme,
1394
ThunarVfsPathScheme src_scheme;
1395
ThunarVfsPath *dst_path = NULL;
1396
gchar *absolute_path;
1398
_thunar_vfs_return_val_if_fail (error == NULL || *error == NULL, NULL);
1400
/* check if the src_path is already in dst_scheme */
1401
src_scheme = thunar_vfs_path_get_scheme (src_path);
1402
if (G_LIKELY (dst_scheme == src_scheme))
1403
return thunar_vfs_path_ref (src_path);
1405
/* we can translate trash:-URIs to file:-URIs */
1406
if (src_scheme == THUNAR_VFS_PATH_SCHEME_TRASH && dst_scheme == THUNAR_VFS_PATH_SCHEME_FILE)
1408
/* resolve the local path to the trash resource */
1409
absolute_path = _thunar_vfs_io_trash_path_resolve (src_path, error);
1410
if (G_LIKELY (absolute_path != NULL))
1412
/* generate a file:-URI path for the trash resource */
1413
dst_path = thunar_vfs_path_new (absolute_path, error);
1414
g_free (absolute_path);
1419
/* cannot perform the translation */
1420
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, "%s", g_strerror (EINVAL));
1429
* _thunar_vfs_path_translate_dup_string:
1430
* @src_path : the source #ThunarVfsPath.
1431
* @dst_scheme : the destination #ThunarVfsPathScheme.
1432
* @error : return location for errors or %NULL.
1434
* Uses _thunar_vfs_path_translate() and thunar_vfs_path_dup_string()
1435
* to generate the string representation of the @src_path in the
1438
* The caller is responsible to free the returned string using
1439
* g_free() when no longer needed.
1441
* Return value: the string representation of the @src_path in the
1442
* @dst_scheme, or %NULL in case of an error.
1445
_thunar_vfs_path_translate_dup_string (ThunarVfsPath *src_path,
1446
ThunarVfsPathScheme dst_scheme,
1449
ThunarVfsPath *dst_path;
1452
_thunar_vfs_return_val_if_fail (error == NULL || *error == NULL, NULL);
1454
/* handle the common case first */
1455
if (G_LIKELY (dst_scheme == THUNAR_VFS_PATH_SCHEME_FILE))
1457
if (_thunar_vfs_path_is_local (src_path))
1458
return thunar_vfs_path_dup_string (src_path);
1459
else if (_thunar_vfs_path_is_trash (src_path))
1460
return _thunar_vfs_io_trash_path_resolve (src_path, error);
1463
/* translate the source path to the destination scheme */
1464
dst_path = _thunar_vfs_path_translate (src_path, dst_scheme, error);
1465
if (G_LIKELY (dst_path != NULL))
1467
/* determine the string representation */
1468
dst_string = thunar_vfs_path_dup_string (dst_path);
1469
thunar_vfs_path_unref (dst_path);
1473
/* we failed to translate */
1482
#define __THUNAR_VFS_PATH_C__
1483
#include <thunar-vfs/thunar-vfs-aliasdef.c>