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

Revision 15497, 14.7 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15496, 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
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->what == NULL)                   \
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
193/**
194 * gnome_vfs_directory_read_next:
195 * @handle: A directory handle
196 * @file_info: Pointer to a %GnomeVFSFileInfo struct where the data about
197 * the entry will be stored
198 *
199 * Read the next directory entry from @handle.
200 *
201 * Return value: An integer value representing the result of the operation.
202 **/
203GnomeVFSResult
204gnome_vfs_directory_read_next (GnomeVFSDirectoryHandle *handle,
205                               GnomeVFSFileInfo *file_info)
206{
207        CHECK_IF_SUPPORTED (handle->uri->method, read_directory);
208
209        gnome_vfs_file_info_clear (file_info);
210        return handle->uri->method->read_directory (handle->uri->method,
211                                                    handle->method_handle,
212                                                    file_info, NULL);
213}
214
215/**
216 * gnome_vfs_directory_close:
217 * @handle: A directory handle.
218 *
219 * Close @handle.
220 *
221 * Return value: An integer representing the result of the operation.
222 **/
223GnomeVFSResult
224gnome_vfs_directory_close (GnomeVFSDirectoryHandle *handle)
225{
226        GnomeVFSResult result;
227
228        CHECK_IF_SUPPORTED (handle->uri->method, close_directory);
229
230        result = handle->uri->method->close_directory (handle->uri->method,
231                                                       handle->method_handle,
232                                                       NULL);
233
234        gnome_vfs_directory_handle_destroy (handle);
235
236        return result;
237}
238
239
240struct _DirectoryReference {
241        ino_t inode;
242        dev_t device;
243};
244typedef struct _DirectoryReference DirectoryReference;
245
246static GList *
247prepend_reference (GList *reference_list,
248                   GnomeVFSFileInfo *info)
249{
250        DirectoryReference *reference;
251
252        reference = g_new (DirectoryReference, 1);
253        reference->device = info->device;
254        reference->inode = info->inode;
255
256        return g_list_prepend (reference_list, reference);
257}
258
259static GList *
260remove_first_reference (GList *reference_list)
261{
262        GList *first;
263
264        if (reference_list == NULL)
265                return NULL;
266
267        first = reference_list;
268        g_free (first->data);
269
270        reference_list = g_list_remove_link (reference_list, first);
271        g_list_free (first);
272
273        return reference_list;
274}
275
276static gboolean
277lookup_ancestor (GList *ancestors,
278                 ino_t inode,
279                 dev_t device)
280{
281        GList *p;
282
283        for (p = ancestors; p != NULL; p = p->next) {
284                DirectoryReference *reference;
285
286                reference = p->data;
287                if (reference->inode == inode && reference->device == device)
288                        return TRUE;
289        }
290
291        return FALSE;
292}
293
294static GnomeVFSResult
295directory_visit_internal (GnomeVFSURI *uri,
296                          const gchar *prefix,
297                          GList *ancestor_references, /* DirectoryReference */
298                          GnomeVFSFileInfoOptions info_options,
299                          const GnomeVFSDirectoryFilter *filter,
300                          GnomeVFSDirectoryVisitOptions visit_options,
301                          GnomeVFSDirectoryVisitFunc callback,
302                          gpointer data)
303{
304        GnomeVFSFileInfo *info;
305        GnomeVFSDirectoryHandle *handle;
306        GnomeVFSResult result;
307        gboolean stop;
308
309        /* The first time, initialize the ancestor list with this
310           directory.  */
311        if (prefix == NULL) {
312                GnomeVFSFileInfo *info;
313
314                info = gnome_vfs_file_info_new ();
315                result = gnome_vfs_get_file_info_uri (uri, info,
316                                                      info_options);
317                if (result != GNOME_VFS_OK) {
318                        gnome_vfs_file_info_unref (info);
319                        return result;
320                }
321
322                if (info->type != GNOME_VFS_FILE_TYPE_DIRECTORY) {
323                        gnome_vfs_file_info_unref (info);
324                        return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
325                }
326
327                ancestor_references = prepend_reference (ancestor_references,
328                                                         info);
329                gnome_vfs_file_info_unref (info);
330        }
331
332        result = gnome_vfs_directory_open_from_uri (&handle, uri, info_options,
333                                                    filter);
334        if (result != GNOME_VFS_OK)
335                return result;
336
337        info = gnome_vfs_file_info_new ();
338
339        stop = FALSE;
340        while (! stop) {
341                gchar *rel_path;
342                gboolean recurse;
343                gboolean recursing_will_loop;
344
345                result = gnome_vfs_directory_read_next (handle, info);
346                if (result != GNOME_VFS_OK)
347                        break;
348
349                /* Skip "." and "..".  */
350                if (info->name[0] == '.'
351                    && (info->name[1] == 0
352                        || (info->name[1] == '.' && info->name[2] == 0))) {
353                        gnome_vfs_file_info_clear (info);
354                        continue;
355                }
356
357                if (prefix == NULL)
358                        rel_path = g_strdup (info->name);
359                else
360                        rel_path = g_strconcat (prefix, info->name, NULL);
361
362                if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY
363                    && (visit_options & GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK))
364                        recursing_will_loop
365                                = lookup_ancestor (ancestor_references,
366                                                   info->inode, info->device);
367                else
368                        recursing_will_loop = FALSE;
369
370                recurse = FALSE;
371                stop = ! (* callback) (rel_path, info, recursing_will_loop,
372                                       data, &recurse);
373
374                if (! stop
375                    && recurse
376                    && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
377                        GnomeVFSURI *new_uri;
378                        gchar *new_prefix;
379
380                        if (prefix == NULL)
381                                new_prefix = g_strconcat (info->name, "/",
382                                                          NULL);
383                        else
384                                new_prefix = g_strconcat (prefix, info->name,
385                                                          "/", NULL);
386
387                        new_uri = gnome_vfs_uri_append_file_name (uri, info->name);
388
389
390                        if (GNOME_VFS_FILE_INFO_LOCAL (info))
391                                ancestor_references = prepend_reference
392                                        (ancestor_references, info);
393
394                        result = directory_visit_internal (new_uri,
395                                                           new_prefix,
396                                                           ancestor_references,
397                                                           info_options,
398                                                           filter,
399                                                           visit_options,
400                                                           callback, data);
401
402                        if (GNOME_VFS_FILE_INFO_LOCAL (info))
403                                ancestor_references = remove_first_reference
404                                        (ancestor_references);
405
406                        if (result != GNOME_VFS_OK)
407                                stop = TRUE;
408
409                        gnome_vfs_uri_unref (new_uri);
410                        g_free (new_prefix);
411                }
412
413                g_free (rel_path);
414
415                gnome_vfs_file_info_clear (info);
416
417                if (stop)
418                        break;
419        }
420
421        gnome_vfs_directory_close (handle);
422        gnome_vfs_file_info_unref (info);
423
424        /* The first time, we are responsible for de-allocating the directory
425           reference we have added by ourselves.  */
426        if (prefix == NULL)
427                ancestor_references
428                        = remove_first_reference (ancestor_references);
429
430        if (result == GNOME_VFS_ERROR_EOF)
431                return GNOME_VFS_OK;
432        else
433                return result;
434}
435
436/**
437 * gnome_vfs_directory_visit_uri:
438 * @uri: URI to start from
439 * @info_options: Options specifying what kind of file information must be
440 * retrieved
441 * @filter: Filter to be used while visiting the directory
442 * @visit_options: Options specifying the type of visit
443 * @callback: Callback to be called for every visited file
444 * @data: Data to be passed to @callback at each iteration
445 *
446 * Visit @uri, retrieving information as specified by @info_options.
447 * Also, @filter will be applied.
448 *
449 * Return value:
450 **/
451GnomeVFSResult
452gnome_vfs_directory_visit_uri (GnomeVFSURI *uri,
453                               GnomeVFSFileInfoOptions info_options,
454                               const GnomeVFSDirectoryFilter *filter,
455                               GnomeVFSDirectoryVisitOptions visit_options,
456                               GnomeVFSDirectoryVisitFunc callback,
457                               gpointer data)
458{
459        g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
460
461        return directory_visit_internal (uri, NULL, NULL,
462                                         info_options, filter,
463                                         visit_options, callback, data);
464}
465
466/**
467 * gnome_vfs_directory_visit:
468 * @uri: URI to start from
469 * @info_options: Options specifying what kind of file information must be
470 * retrieved
471 * @filter: Filter to be used while visiting the directory
472 * @visit_options: Options specifying the type of visit
473 * @callback: Callback to be called for every visited file
474 * @data: Data to be passed to @callback at each iteration
475 *
476 * Visit @uri, retrieving information as specified by @info_options. Also,
477 * @filter will be applied.
478 *
479 * Return value:
480 **/
481GnomeVFSResult
482gnome_vfs_directory_visit (const gchar *text_uri,
483                           GnomeVFSFileInfoOptions info_options,
484                           const GnomeVFSDirectoryFilter *filter,
485                           GnomeVFSDirectoryVisitOptions visit_options,
486                           GnomeVFSDirectoryVisitFunc callback,
487                           gpointer data)
488{
489        GnomeVFSURI *uri;
490        GnomeVFSResult result;
491
492        g_return_val_if_fail (text_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
493
494        uri = gnome_vfs_uri_new (text_uri);
495
496        result = directory_visit_internal (uri, NULL, NULL,
497                                           info_options, filter,
498                                           visit_options, callback, data);
499
500        gnome_vfs_uri_unref (uri);
501
502        return result;
503}
504
505GnomeVFSResult
506gnome_vfs_directory_visit_files_at_uri (GnomeVFSURI *uri,
507                                        GList *file_list,
508                                        GnomeVFSFileInfoOptions info_options,
509                                        const GnomeVFSDirectoryFilter *filter,
510                                        GnomeVFSDirectoryVisitOptions
511                                                visit_options,
512                                        GnomeVFSDirectoryVisitFunc callback,
513                                        gpointer data)
514{
515        GnomeVFSFileInfo *info;
516        GnomeVFSResult result;
517        GList *p;
518
519        g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
520        g_return_val_if_fail (file_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
521
522        info = gnome_vfs_file_info_new ();
523        result = GNOME_VFS_OK;
524
525        for (p = file_list; p != NULL; p = p->next) {
526                GnomeVFSURI *file_uri;
527                gboolean recurse;
528                gboolean stop;
529
530                file_uri = gnome_vfs_uri_append_file_name (uri, p->data);
531                gnome_vfs_get_file_info_uri (file_uri,
532                                             info,
533                                             info_options);
534
535                recurse = FALSE;
536                stop = ! (* callback) (info->name, info, FALSE, data,
537                                       &recurse);
538
539                if (! stop
540                    && recurse
541                    && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
542                        result = gnome_vfs_directory_visit_uri
543                                (uri,
544                                 info_options,
545                                 filter,
546                                 visit_options,
547                                 callback,
548                                 data);
549
550                gnome_vfs_uri_unref (file_uri);
551
552                if (result != GNOME_VFS_OK || stop)
553                        break;
554        }
555
556        gnome_vfs_file_info_unref (info);
557        return GNOME_VFS_OK;
558}
559
560GnomeVFSResult
561gnome_vfs_directory_visit_files (const gchar *text_uri,
562                                 GList *file_list,
563                                 GnomeVFSFileInfoOptions info_options,
564                                 const GnomeVFSDirectoryFilter *filter,
565                                 GnomeVFSDirectoryVisitOptions
566                                        visit_options,
567                                 GnomeVFSDirectoryVisitFunc callback,
568                                 gpointer data)
569{
570        GnomeVFSURI *uri;
571        GnomeVFSResult result;
572
573        uri = gnome_vfs_uri_new (text_uri);
574
575        result = gnome_vfs_directory_visit_files_at_uri (uri, file_list,
576                                                         info_options,
577                                                         filter,
578                                                         visit_options,
579                                                         callback,
580                                                         data);
581        gnome_vfs_uri_unref (uri);
582
583        return result;
584}
Note: See TracBrowser for help on using the repository browser.