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

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