1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-directory.c - Directory handling for the GNOME Virtual
5 Copyright (C) 1999 Free Software Foundation
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.
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.
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.
22 Author: Ettore Perazzoli <ettore@gnu.org> */
25 #include "gnome-vfs-directory.h"
27 #include "gnome-vfs-cancellable-ops.h"
28 #include "gnome-vfs-method.h"
29 #include "gnome-vfs-ops.h"
30 #include <glib/gmessages.h>
31 #include <glib/gstrfuncs.h>
33 #define VFS_MAXIMUM_SYMBOLIC_LINK_DEPTH 256
35 struct GnomeVFSDirectoryHandle {
36 /* URI of the directory being accessed through the handle. */
40 GnomeVFSFileInfoOptions options;
42 /* Method-specific handle. */
43 GnomeVFSMethodHandle *method_handle;
46 #define CHECK_IF_SUPPORTED(vfs_method, what) \
48 if (!VFS_METHOD_HAS_FUNC(vfs_method, what)) \
49 return GNOME_VFS_ERROR_NOT_SUPPORTED; \
53 static GnomeVFSDirectoryHandle *
54 gnome_vfs_directory_handle_new (GnomeVFSURI *uri,
55 GnomeVFSMethodHandle *method_handle,
56 GnomeVFSFileInfoOptions options)
58 GnomeVFSDirectoryHandle *new;
60 g_return_val_if_fail (uri != NULL, NULL);
61 g_return_val_if_fail (method_handle != NULL, NULL);
63 new = g_new (GnomeVFSDirectoryHandle, 1);
65 gnome_vfs_uri_ref (uri);
68 new->method_handle = method_handle;
69 new->options = options;
75 gnome_vfs_directory_handle_destroy (GnomeVFSDirectoryHandle *handle)
77 g_return_if_fail (handle != NULL);
79 gnome_vfs_uri_unref (handle->uri);
86 open_from_uri (GnomeVFSDirectoryHandle **handle,
88 GnomeVFSFileInfoOptions options,
89 GnomeVFSContext *context)
91 GnomeVFSMethodHandle *method_handle;
92 GnomeVFSResult result;
94 CHECK_IF_SUPPORTED (uri->method, open_directory);
97 result = uri->method->open_directory (uri->method,
102 if (result != GNOME_VFS_OK) {
106 *handle = gnome_vfs_directory_handle_new (uri,
113 static GnomeVFSResult
114 open (GnomeVFSDirectoryHandle **handle,
115 const gchar *text_uri,
116 GnomeVFSFileInfoOptions options,
117 GnomeVFSContext *context)
120 GnomeVFSResult result;
122 g_return_val_if_fail (handle != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
123 g_return_val_if_fail (text_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
125 uri = gnome_vfs_uri_new (text_uri);
127 return GNOME_VFS_ERROR_INVALID_URI;
129 result = open_from_uri (handle, uri, options,
132 gnome_vfs_uri_unref (uri);
138 * gnome_vfs_directory_open
139 * @handle: A pointer to a pointer to a GnomeVFSDirectoryHandle object
140 * @text_uri: String representing the URI to open
141 * @options: Options for reading file information
143 * Open directory @text_uri for reading. On return, @*handle will point to
144 * a %GnomeVFSDirectoryHandle object which can be used to read the directory
145 * entries one by one.
147 * Returns: An integer representing the result of the operation
150 gnome_vfs_directory_open (GnomeVFSDirectoryHandle **handle,
151 const gchar *text_uri,
152 GnomeVFSFileInfoOptions options)
154 g_return_val_if_fail (handle != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
155 g_return_val_if_fail (text_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
157 return open (handle, text_uri, options, NULL);
161 * gnome_vfs_directory_open_from_uri
162 * @handle: A pointer to a pointer to a GnomeVFSDirectoryHandle object
164 * @options: Options for reading file information
166 * Open directory @text_uri for reading. On return, @*handle will point to
167 * a %GnomeVFSDirectoryHandle object which can be used to read the directory
168 * entries one by one.
170 * Returns: An integer representing the result of the operation.
173 gnome_vfs_directory_open_from_uri (GnomeVFSDirectoryHandle **handle,
175 GnomeVFSFileInfoOptions options)
177 g_return_val_if_fail (handle != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
178 g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
180 return open_from_uri (handle, uri, options, NULL);
184 gnome_vfs_directory_open_from_uri_cancellable (GnomeVFSDirectoryHandle **handle,
186 GnomeVFSFileInfoOptions options,
187 GnomeVFSContext *context)
189 g_return_val_if_fail (handle != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
190 g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
192 return open_from_uri (handle, uri, options, context);
196 * gnome_vfs_directory_read_next:
197 * @handle: A directory handle
198 * @file_info: Pointer to a %GnomeVFSFileInfo struct where the data about
199 * the entry will be stored
201 * Read the next directory entry from @handle.
203 * Returns: An integer value representing the result of the operation.
206 gnome_vfs_directory_read_next (GnomeVFSDirectoryHandle *handle,
207 GnomeVFSFileInfo *file_info)
209 CHECK_IF_SUPPORTED (handle->uri->method, read_directory);
211 gnome_vfs_file_info_clear (file_info);
212 return handle->uri->method->read_directory (handle->uri->method,
213 handle->method_handle,
218 gnome_vfs_directory_read_next_cancellable (GnomeVFSDirectoryHandle *handle,
219 GnomeVFSFileInfo *file_info,
220 GnomeVFSContext *context)
222 CHECK_IF_SUPPORTED (handle->uri->method, read_directory);
224 gnome_vfs_file_info_clear (file_info);
225 return handle->uri->method->read_directory (handle->uri->method,
226 handle->method_handle,
232 * gnome_vfs_directory_close:
233 * @handle: A directory handle.
237 * Returns: An integer representing the result of the operation.
240 gnome_vfs_directory_close (GnomeVFSDirectoryHandle *handle)
242 GnomeVFSResult result;
244 CHECK_IF_SUPPORTED (handle->uri->method, close_directory);
246 result = handle->uri->method->close_directory (handle->uri->method,
247 handle->method_handle,
250 gnome_vfs_directory_handle_destroy (handle);
256 struct _DirectoryReference {
260 typedef struct _DirectoryReference DirectoryReference;
263 prepend_reference (GList *reference_list,
264 GnomeVFSFileInfo *info)
266 DirectoryReference *reference;
268 reference = g_new (DirectoryReference, 1);
269 reference->device = info->device;
270 reference->inode = info->inode;
272 return g_list_prepend (reference_list, reference);
276 remove_first_reference (GList *reference_list)
280 if (reference_list == NULL)
283 first = reference_list;
284 g_free (first->data);
286 reference_list = g_list_remove_link (reference_list, first);
289 return reference_list;
293 lookup_ancestor (GList *ancestors,
294 gboolean inode_and_device_are_valid,
300 if (!inode_and_device_are_valid) {
301 return g_list_length (ancestors) >= VFS_MAXIMUM_SYMBOLIC_LINK_DEPTH;
304 for (p = ancestors; p != NULL; p = p->next) {
305 DirectoryReference *reference;
308 if (reference->inode == inode && reference->device == device)
315 static GnomeVFSResult
316 directory_visit_internal (GnomeVFSURI *uri,
318 GList *ancestor_references, /* DirectoryReference */
319 GnomeVFSFileInfoOptions info_options,
320 GnomeVFSDirectoryVisitOptions visit_options,
321 GnomeVFSDirectoryVisitFunc callback,
324 GnomeVFSFileInfo *info;
325 GnomeVFSDirectoryHandle *handle;
326 GnomeVFSResult result;
329 /* The first time, initialize the ancestor list with this
331 if (prefix == NULL) {
332 GnomeVFSFileInfo *info;
334 info = gnome_vfs_file_info_new ();
335 result = gnome_vfs_get_file_info_uri (uri, info,
337 if (result != GNOME_VFS_OK) {
338 gnome_vfs_file_info_unref (info);
342 if (info->type != GNOME_VFS_FILE_TYPE_DIRECTORY) {
343 gnome_vfs_file_info_unref (info);
344 return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
347 ancestor_references = prepend_reference (ancestor_references,
349 gnome_vfs_file_info_unref (info);
352 result = gnome_vfs_directory_open_from_uri (&handle, uri, info_options);
353 if (result != GNOME_VFS_OK)
356 info = gnome_vfs_file_info_new ();
362 gboolean recursing_will_loop;
364 result = gnome_vfs_directory_read_next (handle, info);
365 if (result != GNOME_VFS_OK)
368 /* Skip "." and "..". */
369 if (info->name[0] == '.'
370 && (info->name[1] == 0
371 || (info->name[1] == '.' && info->name[2] == 0))) {
372 gnome_vfs_file_info_clear (info);
377 rel_path = g_strdup (info->name);
379 rel_path = g_strconcat (prefix, info->name, NULL);
381 if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY
382 && (visit_options & GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK))
384 = lookup_ancestor (ancestor_references,
385 (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_DEVICE) &&
386 (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_INODE),
387 info->inode, info->device);
389 recursing_will_loop = FALSE;
392 stop = ! (* callback) (rel_path, info, recursing_will_loop,
397 && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
398 GnomeVFSURI *new_uri;
402 new_prefix = g_strconcat (info->name, "/",
405 new_prefix = g_strconcat (prefix, info->name,
408 new_uri = gnome_vfs_uri_append_file_name (uri, info->name);
411 if (GNOME_VFS_FILE_INFO_LOCAL (info))
412 ancestor_references = prepend_reference
413 (ancestor_references, info);
415 result = directory_visit_internal (new_uri,
422 if (GNOME_VFS_FILE_INFO_LOCAL (info))
423 ancestor_references = remove_first_reference
424 (ancestor_references);
426 if (result != GNOME_VFS_OK)
429 gnome_vfs_uri_unref (new_uri);
435 gnome_vfs_file_info_clear (info);
441 gnome_vfs_directory_close (handle);
442 gnome_vfs_file_info_unref (info);
444 /* The first time, we are responsible for de-allocating the directory
445 reference we have added by ourselves. */
448 = remove_first_reference (ancestor_references);
450 if (result == GNOME_VFS_ERROR_EOF)
457 * gnome_vfs_directory_visit_uri
458 * @uri: URI to start from
459 * @info_options: Options specifying what kind of file information must be
461 * @visit_options: Options specifying the type of visit
462 * @callback: Callback to be called for every visited file
463 * @data: Data to be passed to @callback at each iteration
465 * Visit @uri, retrieving information as specified by @info_options.
467 * Returns: A result code indicating whether the operation succeeded.
471 gnome_vfs_directory_visit_uri (GnomeVFSURI *uri,
472 GnomeVFSFileInfoOptions info_options,
473 GnomeVFSDirectoryVisitOptions visit_options,
474 GnomeVFSDirectoryVisitFunc callback,
477 g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
479 return directory_visit_internal (uri, NULL, NULL,
481 visit_options, callback, data);
485 * gnome_vfs_directory_visit:
486 * @uri: URI to start from
487 * @info_options: Options specifying what kind of file information must be
489 * @visit_options: Options specifying the type of visit
490 * @callback: Callback to be called for every visited file
491 * @data: Data to be passed to @callback at each iteration
493 * Visit @uri, retrieving information as specified by @info_options.
498 gnome_vfs_directory_visit (const gchar *text_uri,
499 GnomeVFSFileInfoOptions info_options,
500 GnomeVFSDirectoryVisitOptions visit_options,
501 GnomeVFSDirectoryVisitFunc callback,
505 GnomeVFSResult result;
507 g_return_val_if_fail (text_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
509 uri = gnome_vfs_uri_new (text_uri);
511 return GNOME_VFS_ERROR_INVALID_URI;
514 result = directory_visit_internal (uri, NULL, NULL,
516 visit_options, callback, data);
518 gnome_vfs_uri_unref (uri);
524 * gnome_vfs_directory_visit_files_at_uri:
525 * @uri: URI of a directory to "visit" the files in
526 * @file_list: GList of char *s of file names in @uri to visit
527 * @info_options: bitmask controlling the type of information to fetch
528 * @visit_options: options controlling e.g. loop prevention, and filesystem checks.
529 * Affects the way visiting is done.
530 * @callback: function to call with the file info structs
531 * @data: data to pass to @callback.
533 * Fetches information about a list of files in a base URI @uri.
535 * Return value: a GnomeVFSResult indication the success of the operation
538 gnome_vfs_directory_visit_files_at_uri (GnomeVFSURI *uri,
540 GnomeVFSFileInfoOptions info_options,
541 GnomeVFSDirectoryVisitOptions visit_options,
542 GnomeVFSDirectoryVisitFunc callback,
545 GnomeVFSFileInfo *info;
546 GnomeVFSResult result;
549 g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
550 g_return_val_if_fail (file_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
552 info = gnome_vfs_file_info_new ();
553 result = GNOME_VFS_OK;
555 for (p = file_list; p != NULL; p = p->next) {
556 GnomeVFSURI *file_uri;
560 file_uri = gnome_vfs_uri_append_file_name (uri, p->data);
561 gnome_vfs_get_file_info_uri (file_uri,
566 stop = ! (* callback) (info->name, info, FALSE, data,
571 && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
572 result = gnome_vfs_directory_visit_uri
579 gnome_vfs_uri_unref (file_uri);
581 if (result != GNOME_VFS_OK || stop)
585 gnome_vfs_file_info_unref (info);
590 * gnome_vfs_directory_visit_files:
591 * @text_uri: string representing the URI of a directory to "visit" the files in
592 * @file_list: GList of char *s of file names in @uri to visit
593 * @info_options: bitmask controlling the type of information to fetch
594 * @visit_options: options controlling e.g. loop prevention, and filesystem checks.
595 * Affects the way visiting is done.
596 * @callback: function to call with the file info structs
597 * @data: data to pass to @callback.
599 * Fetches information about a list of files in a base URI @uri.
601 * Return value: a GnomeVFSResult indication the success of the operation
604 gnome_vfs_directory_visit_files (const gchar *text_uri,
606 GnomeVFSFileInfoOptions info_options,
607 GnomeVFSDirectoryVisitOptions visit_options,
608 GnomeVFSDirectoryVisitFunc callback,
612 GnomeVFSResult result;
614 uri = gnome_vfs_uri_new (text_uri);
616 result = gnome_vfs_directory_visit_files_at_uri (uri, file_list,
621 gnome_vfs_uri_unref (uri);
626 static GnomeVFSResult
627 load_from_handle (GList **list,
628 GnomeVFSDirectoryHandle *handle)
630 GnomeVFSResult result;
631 GnomeVFSFileInfo *info;
636 info = gnome_vfs_file_info_new ();
637 result = gnome_vfs_directory_read_next (handle, info);
638 if (result != GNOME_VFS_OK)
640 *list = g_list_prepend (*list, info);
643 *list = g_list_reverse (*list);
645 gnome_vfs_file_info_unref (info);
647 if (result != GNOME_VFS_ERROR_EOF) {
648 gnome_vfs_file_info_list_free (*list);
656 * gnome_vfs_directory_list_load:
657 * @list: An address of a pointer to a list of GnomeVFSFileInfo
658 * @text_uri: A text URI
659 * @options: Options for loading the directory
661 * Load a directory from @text_uri with the specified @options
664 * Return value: An integer representing the result of the operation.
667 gnome_vfs_directory_list_load (GList **list,
668 const gchar *text_uri,
669 GnomeVFSFileInfoOptions options)
671 GnomeVFSDirectoryHandle *handle;
672 GnomeVFSResult result;
674 result = gnome_vfs_directory_open (&handle, text_uri, options);
675 if (result != GNOME_VFS_OK) {
679 result = load_from_handle (list, handle);
681 gnome_vfs_directory_close (handle);