source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-directory.c @ 15858

Revision 15858, 17.3 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15857, 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-directory.c - Directory handling for the GNOME Virtual
3   File System.
4
5   Copyright (C) 1999 Free Software Foundation
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#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include "gnome-vfs.h"
29#include "gnome-vfs-private.h"
30
31
32
33#define VFS_MAXIMUM_SYMBOLIC_LINK_DEPTH 256
34
35struct GnomeVFSDirectoryHandle {
36        /* URI of the directory being accessed through the handle.  */
37        GnomeVFSURI *uri;
38
39        /* Options.  */
40        GnomeVFSFileInfoOptions options;
41
42        /* Method-specific handle.  */
43        GnomeVFSMethodHandle *method_handle;
44
45        /* Filter.  */
46        const GnomeVFSDirectoryFilter *filter;
47};
48
49#define CHECK_IF_SUPPORTED(vfs_method, what)            \
50G_STMT_START{                                           \
51        if (!VFS_METHOD_HAS_FUNC(vfs_method, what))                     \
52                return GNOME_VFS_ERROR_NOT_SUPPORTED;   \
53}G_STMT_END
54
55
56static GnomeVFSDirectoryHandle *
57gnome_vfs_directory_handle_new (GnomeVFSURI *uri,
58                                GnomeVFSMethodHandle *method_handle,
59                                GnomeVFSFileInfoOptions options,
60                                const GnomeVFSDirectoryFilter *filter)
61{
62        GnomeVFSDirectoryHandle *new;
63
64        g_return_val_if_fail (uri != NULL, NULL);
65        g_return_val_if_fail (method_handle != NULL, NULL);
66
67        new = g_new (GnomeVFSDirectoryHandle, 1);
68
69        gnome_vfs_uri_ref (uri);
70
71        new->uri = uri;
72        new->method_handle = method_handle;
73        new->options = options;
74        new->filter = filter;
75
76        return new;
77}
78
79static void
80gnome_vfs_directory_handle_destroy (GnomeVFSDirectoryHandle *handle)
81{
82        g_return_if_fail (handle != NULL);
83
84        gnome_vfs_uri_unref (handle->uri);
85
86        g_free (handle);
87}
88
89
90static GnomeVFSResult
91open_from_uri (GnomeVFSDirectoryHandle **handle,
92               GnomeVFSURI *uri,
93               GnomeVFSFileInfoOptions options,
94               const GnomeVFSDirectoryFilter *filter,
95               GnomeVFSContext *context)
96{
97        GnomeVFSMethodHandle *method_handle;
98        GnomeVFSResult result;
99
100        CHECK_IF_SUPPORTED (uri->method, open_directory);
101
102
103        result = uri->method->open_directory (uri->method,
104                                              &method_handle,
105                                              uri,
106                                              options,
107                                              filter,
108                                              context);
109        if (result != GNOME_VFS_OK) {
110                return result;
111        }
112
113        *handle = gnome_vfs_directory_handle_new (uri,
114                                                  method_handle,
115                                                  options,
116                                                  filter);
117
118        return GNOME_VFS_OK;
119}
120
121static GnomeVFSResult
122open (GnomeVFSDirectoryHandle **handle,
123      const gchar *text_uri,
124      GnomeVFSFileInfoOptions options,
125      const GnomeVFSDirectoryFilter *filter,
126      GnomeVFSContext *context)
127{
128        GnomeVFSURI *uri;
129        GnomeVFSResult result;
130
131        g_return_val_if_fail (handle != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
132        g_return_val_if_fail (text_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
133
134        uri = gnome_vfs_uri_new (text_uri);
135        if (uri == NULL)
136                return GNOME_VFS_ERROR_INVALID_URI;
137
138        result = open_from_uri (handle, uri, options, filter,
139                                context);
140
141        gnome_vfs_uri_unref (uri);
142
143        return result;
144}
145
146/**
147 * gnome_vfs_directory_open:
148 * @handle: A pointer to a pointer to a GnomeVFSDirectoryHandle object
149 * @text_uri: String representing the URI to open
150 * @options: Options for reading file information
151 * @filter: Filter to be applied to the directory entries
152 *
153 * Open directory @text_uri for reading.  On return, @*handle will point to
154 * a %GnomeVFSDirectoryHandle object which can be used to read the directory
155 * entries one by one.
156 *
157 * Return value: An integer representing the result of the operation.
158 **/
159GnomeVFSResult
160gnome_vfs_directory_open (GnomeVFSDirectoryHandle **handle,
161                          const gchar *text_uri,
162                          GnomeVFSFileInfoOptions options,
163                          const GnomeVFSDirectoryFilter *filter)
164{
165        g_return_val_if_fail (handle != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
166        g_return_val_if_fail (text_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
167
168        return open (handle, text_uri, options, filter, NULL);
169}
170
171/**
172 * gnome_vfs_directory_open_from_uri:
173 * @handle: A pointer to a pointer to a GnomeVFSDirectoryHandle object
174 * @uri: URI to open
175 * @options: Options for reading file information
176 * @filter: Filter to be applied to the directory entries
177 *
178 * Open directory @text_uri for reading.  On return, @*handle will point to
179 * a %GnomeVFSDirectoryHandle object which can be used to read the directory
180 * entries one by one.
181 *
182 * Return value: An integer representing the result of the operation.
183 **/
184GnomeVFSResult
185gnome_vfs_directory_open_from_uri (GnomeVFSDirectoryHandle **handle,
186                                   GnomeVFSURI *uri,
187                                   GnomeVFSFileInfoOptions options,
188                                   const GnomeVFSDirectoryFilter *filter)
189{
190        g_return_val_if_fail (handle != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
191        g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
192
193        return open_from_uri (handle, uri, options, filter, NULL);
194}
195
196GnomeVFSResult
197gnome_vfs_directory_open_from_uri_cancellable (GnomeVFSDirectoryHandle **handle,
198                                   GnomeVFSURI *uri,
199                                   GnomeVFSFileInfoOptions options,
200                                   const GnomeVFSDirectoryFilter *filter,
201                                   GnomeVFSContext *context)
202{
203        g_return_val_if_fail (handle != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
204        g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
205
206        return open_from_uri (handle, uri, options, filter, context);
207}
208
209/**
210 * gnome_vfs_directory_read_next:
211 * @handle: A directory handle
212 * @file_info: Pointer to a %GnomeVFSFileInfo struct where the data about
213 * the entry will be stored
214 *
215 * Read the next directory entry from @handle.
216 *
217 * Return value: An integer value representing the result of the operation.
218 **/
219GnomeVFSResult
220gnome_vfs_directory_read_next (GnomeVFSDirectoryHandle *handle,
221                               GnomeVFSFileInfo *file_info)
222{
223        CHECK_IF_SUPPORTED (handle->uri->method, read_directory);
224
225        gnome_vfs_file_info_clear (file_info);
226        return handle->uri->method->read_directory (handle->uri->method,
227                                                    handle->method_handle,
228                                                    file_info, NULL);
229}
230
231GnomeVFSResult
232gnome_vfs_directory_read_next_cancellable (GnomeVFSDirectoryHandle *handle,
233                                           GnomeVFSFileInfo *file_info,
234                                           GnomeVFSContext *context)
235{
236        CHECK_IF_SUPPORTED (handle->uri->method, read_directory);
237
238        gnome_vfs_file_info_clear (file_info);
239        return handle->uri->method->read_directory (handle->uri->method,
240                                                    handle->method_handle,
241                                                    file_info, context);
242}
243
244
245/**
246 * gnome_vfs_directory_close:
247 * @handle: A directory handle.
248 *
249 * Close @handle.
250 *
251 * Return value: An integer representing the result of the operation.
252 **/
253GnomeVFSResult
254gnome_vfs_directory_close (GnomeVFSDirectoryHandle *handle)
255{
256        GnomeVFSResult result;
257
258        CHECK_IF_SUPPORTED (handle->uri->method, close_directory);
259
260        result = handle->uri->method->close_directory (handle->uri->method,
261                                                       handle->method_handle,
262                                                       NULL);
263
264        gnome_vfs_directory_handle_destroy (handle);
265
266        return result;
267}
268
269
270struct _DirectoryReference {
271        ino_t inode;
272        dev_t device;
273};
274typedef struct _DirectoryReference DirectoryReference;
275
276static GList *
277prepend_reference (GList *reference_list,
278                   GnomeVFSFileInfo *info)
279{
280        DirectoryReference *reference;
281
282        reference = g_new (DirectoryReference, 1);
283        reference->device = info->device;
284        reference->inode = info->inode;
285
286        return g_list_prepend (reference_list, reference);
287}
288
289static GList *
290remove_first_reference (GList *reference_list)
291{
292        GList *first;
293
294        if (reference_list == NULL)
295                return NULL;
296
297        first = reference_list;
298        g_free (first->data);
299
300        reference_list = g_list_remove_link (reference_list, first);
301        g_list_free (first);
302
303        return reference_list;
304}
305
306static gboolean
307lookup_ancestor (GList *ancestors,
308                 gboolean inode_and_device_are_valid,
309                 ino_t inode,
310                 dev_t device)
311{
312        GList *p;
313
314        if (!inode_and_device_are_valid) {
315                return g_list_length (ancestors) >= VFS_MAXIMUM_SYMBOLIC_LINK_DEPTH;
316        }
317
318        for (p = ancestors; p != NULL; p = p->next) {
319                DirectoryReference *reference;
320
321                reference = p->data;
322                if (reference->inode == inode && reference->device == device)
323                        return TRUE;
324        }
325
326        return FALSE;
327}
328
329static GnomeVFSResult
330directory_visit_internal (GnomeVFSURI *uri,
331                          const gchar *prefix,
332                          GList *ancestor_references, /* DirectoryReference */
333                          GnomeVFSFileInfoOptions info_options,
334                          const GnomeVFSDirectoryFilter *filter,
335                          GnomeVFSDirectoryVisitOptions visit_options,
336                          GnomeVFSDirectoryVisitFunc callback,
337                          gpointer data)
338{
339        GnomeVFSFileInfo *info;
340        GnomeVFSDirectoryHandle *handle;
341        GnomeVFSResult result;
342        gboolean stop;
343
344        /* The first time, initialize the ancestor list with this
345           directory.  */
346        if (prefix == NULL) {
347                GnomeVFSFileInfo *info;
348
349                info = gnome_vfs_file_info_new ();
350                result = gnome_vfs_get_file_info_uri (uri, info,
351                                                      info_options);
352                if (result != GNOME_VFS_OK) {
353                        gnome_vfs_file_info_unref (info);
354                        return result;
355                }
356
357                if (info->type != GNOME_VFS_FILE_TYPE_DIRECTORY) {
358                        gnome_vfs_file_info_unref (info);
359                        return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
360                }
361
362                ancestor_references = prepend_reference (ancestor_references,
363                                                         info);
364                gnome_vfs_file_info_unref (info);
365        }
366
367        result = gnome_vfs_directory_open_from_uri (&handle, uri, info_options,
368                                                    filter);
369        if (result != GNOME_VFS_OK)
370                return result;
371
372        info = gnome_vfs_file_info_new ();
373
374        stop = FALSE;
375        while (! stop) {
376                gchar *rel_path;
377                gboolean recurse;
378                gboolean recursing_will_loop;
379
380                result = gnome_vfs_directory_read_next (handle, info);
381                if (result != GNOME_VFS_OK)
382                        break;
383
384                /* Skip "." and "..".  */
385                if (info->name[0] == '.'
386                    && (info->name[1] == 0
387                        || (info->name[1] == '.' && info->name[2] == 0))) {
388                        gnome_vfs_file_info_clear (info);
389                        continue;
390                }
391
392                if (prefix == NULL)
393                        rel_path = g_strdup (info->name);
394                else
395                        rel_path = g_strconcat (prefix, info->name, NULL);
396
397                if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY
398                    && (visit_options & GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK))
399                        recursing_will_loop
400                                = lookup_ancestor (ancestor_references,
401                                                   (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_DEVICE) &&
402                                                   (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_INODE),
403                                                   info->inode, info->device);
404                else
405                        recursing_will_loop = FALSE;
406
407                recurse = FALSE;
408                stop = ! (* callback) (rel_path, info, recursing_will_loop,
409                                       data, &recurse);
410
411                if (! stop
412                    && recurse
413                    && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
414                        GnomeVFSURI *new_uri;
415                        gchar *new_prefix;
416
417                        if (prefix == NULL)
418                                new_prefix = g_strconcat (info->name, "/",
419                                                          NULL);
420                        else
421                                new_prefix = g_strconcat (prefix, info->name,
422                                                          "/", NULL);
423
424                        new_uri = gnome_vfs_uri_append_file_name (uri, info->name);
425
426
427                        if (GNOME_VFS_FILE_INFO_LOCAL (info))
428                                ancestor_references = prepend_reference
429                                        (ancestor_references, info);
430
431                        result = directory_visit_internal (new_uri,
432                                                           new_prefix,
433                                                           ancestor_references,
434                                                           info_options,
435                                                           filter,
436                                                           visit_options,
437                                                           callback, data);
438
439                        if (GNOME_VFS_FILE_INFO_LOCAL (info))
440                                ancestor_references = remove_first_reference
441                                        (ancestor_references);
442
443                        if (result != GNOME_VFS_OK)
444                                stop = TRUE;
445
446                        gnome_vfs_uri_unref (new_uri);
447                        g_free (new_prefix);
448                }
449
450                g_free (rel_path);
451
452                gnome_vfs_file_info_clear (info);
453
454                if (stop)
455                        break;
456        }
457
458        gnome_vfs_directory_close (handle);
459        gnome_vfs_file_info_unref (info);
460
461        /* The first time, we are responsible for de-allocating the directory
462           reference we have added by ourselves.  */
463        if (prefix == NULL)
464                ancestor_references
465                        = remove_first_reference (ancestor_references);
466
467        if (result == GNOME_VFS_ERROR_EOF)
468                return GNOME_VFS_OK;
469        else
470                return result;
471}
472
473/**
474 * gnome_vfs_directory_visit_uri:
475 * @uri: URI to start from
476 * @info_options: Options specifying what kind of file information must be
477 * retrieved
478 * @filter: Filter to be used while visiting the directory
479 * @visit_options: Options specifying the type of visit
480 * @callback: Callback to be called for every visited file
481 * @data: Data to be passed to @callback at each iteration
482 *
483 * Visit @uri, retrieving information as specified by @info_options.
484 * Also, @filter will be applied.
485 *
486 * Return value:
487 **/
488GnomeVFSResult
489gnome_vfs_directory_visit_uri (GnomeVFSURI *uri,
490                               GnomeVFSFileInfoOptions info_options,
491                               const GnomeVFSDirectoryFilter *filter,
492                               GnomeVFSDirectoryVisitOptions visit_options,
493                               GnomeVFSDirectoryVisitFunc callback,
494                               gpointer data)
495{
496        g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
497
498        return directory_visit_internal (uri, NULL, NULL,
499                                         info_options, filter,
500                                         visit_options, callback, data);
501}
502
503/**
504 * gnome_vfs_directory_visit:
505 * @uri: URI to start from
506 * @info_options: Options specifying what kind of file information must be
507 * retrieved
508 * @filter: Filter to be used while visiting the directory
509 * @visit_options: Options specifying the type of visit
510 * @callback: Callback to be called for every visited file
511 * @data: Data to be passed to @callback at each iteration
512 *
513 * Visit @uri, retrieving information as specified by @info_options. Also,
514 * @filter will be applied.
515 *
516 * Return value:
517 **/
518GnomeVFSResult
519gnome_vfs_directory_visit (const gchar *text_uri,
520                           GnomeVFSFileInfoOptions info_options,
521                           const GnomeVFSDirectoryFilter *filter,
522                           GnomeVFSDirectoryVisitOptions visit_options,
523                           GnomeVFSDirectoryVisitFunc callback,
524                           gpointer data)
525{
526        GnomeVFSURI *uri;
527        GnomeVFSResult result;
528
529        g_return_val_if_fail (text_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
530
531        uri = gnome_vfs_uri_new (text_uri);
532
533        result = directory_visit_internal (uri, NULL, NULL,
534                                           info_options, filter,
535                                           visit_options, callback, data);
536
537        gnome_vfs_uri_unref (uri);
538
539        return result;
540}
541
542GnomeVFSResult
543gnome_vfs_directory_visit_files_at_uri (GnomeVFSURI *uri,
544                                        GList *file_list,
545                                        GnomeVFSFileInfoOptions info_options,
546                                        const GnomeVFSDirectoryFilter *filter,
547                                        GnomeVFSDirectoryVisitOptions
548                                                visit_options,
549                                        GnomeVFSDirectoryVisitFunc callback,
550                                        gpointer data)
551{
552        GnomeVFSFileInfo *info;
553        GnomeVFSResult result;
554        GList *p;
555
556        g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
557        g_return_val_if_fail (file_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
558
559        info = gnome_vfs_file_info_new ();
560        result = GNOME_VFS_OK;
561
562        for (p = file_list; p != NULL; p = p->next) {
563                GnomeVFSURI *file_uri;
564                gboolean recurse;
565                gboolean stop;
566
567                file_uri = gnome_vfs_uri_append_file_name (uri, p->data);
568                gnome_vfs_get_file_info_uri (file_uri,
569                                             info,
570                                             info_options);
571
572                recurse = FALSE;
573                stop = ! (* callback) (info->name, info, FALSE, data,
574                                       &recurse);
575
576                if (! stop
577                    && recurse
578                    && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
579                        result = gnome_vfs_directory_visit_uri
580                                (uri,
581                                 info_options,
582                                 filter,
583                                 visit_options,
584                                 callback,
585                                 data);
586
587                gnome_vfs_uri_unref (file_uri);
588
589                if (result != GNOME_VFS_OK || stop)
590                        break;
591        }
592
593        gnome_vfs_file_info_unref (info);
594        return GNOME_VFS_OK;
595}
596
597GnomeVFSResult
598gnome_vfs_directory_visit_files (const gchar *text_uri,
599                                 GList *file_list,
600                                 GnomeVFSFileInfoOptions info_options,
601                                 const GnomeVFSDirectoryFilter *filter,
602                                 GnomeVFSDirectoryVisitOptions
603                                        visit_options,
604                                 GnomeVFSDirectoryVisitFunc callback,
605                                 gpointer data)
606{
607        GnomeVFSURI *uri;
608        GnomeVFSResult result;
609
610        uri = gnome_vfs_uri_new (text_uri);
611
612        result = gnome_vfs_directory_visit_files_at_uri (uri, file_list,
613                                                         info_options,
614                                                         filter,
615                                                         visit_options,
616                                                         callback,
617                                                         data);
618        gnome_vfs_uri_unref (uri);
619
620        return result;
621}
622
623static GnomeVFSResult
624load_from_handle (GList **list,
625                  GnomeVFSDirectoryHandle *handle)
626{
627        GnomeVFSResult result;
628        GnomeVFSFileInfo *info;
629
630        *list = NULL;
631
632        for (;;) {
633                info = gnome_vfs_file_info_new ();
634                result = gnome_vfs_directory_read_next (handle, info);
635                if (result != GNOME_VFS_OK)
636                        break;
637                *list = g_list_prepend (*list, info);
638        }
639
640        *list = g_list_reverse (*list);
641       
642        gnome_vfs_file_info_unref (info);
643
644        if (result != GNOME_VFS_ERROR_EOF) {
645                gnome_vfs_file_info_list_free (*list);
646                *list = NULL;
647        }
648
649        return GNOME_VFS_OK;
650}
651
652/**
653 * gnome_vfs_directory_list_load:
654 * @list: An address of a pointer to a list
655 * @text_uri: A text URI
656 * @options: Options for loading the directory
657 * @filter: Filter to be applied to the files being read
658 *
659 * Load a directory from @text_uri with the specified @options
660 * into a list.  Directory entries are filtered through
661 * @filter.
662 *
663 * Return value: An integer representing the result of the operation.
664 **/
665GnomeVFSResult
666gnome_vfs_directory_list_load (GList **list,
667                               const gchar *text_uri,
668                               GnomeVFSFileInfoOptions options,
669                               const GnomeVFSDirectoryFilter *filter)
670{
671        GnomeVFSDirectoryHandle *handle;
672        GnomeVFSResult result;
673
674        result = gnome_vfs_directory_open (&handle, text_uri, options, filter);
675        if (result != GNOME_VFS_OK) {
676                return result;
677        }
678
679        result = load_from_handle (list, handle);
680
681        gnome_vfs_directory_close (handle);
682        return result;
683}
684
Note: See TracBrowser for help on using the repository browser.