ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-directory.c
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-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>
32
33 #define VFS_MAXIMUM_SYMBOLIC_LINK_DEPTH 256
34
35 struct 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
46 #define CHECK_IF_SUPPORTED(vfs_method, what)            \
47 G_STMT_START{                                           \
48         if (!VFS_METHOD_HAS_FUNC(vfs_method, what))                     \
49                 return GNOME_VFS_ERROR_NOT_SUPPORTED;   \
50 }G_STMT_END
51
52 \f
53 static GnomeVFSDirectoryHandle *
54 gnome_vfs_directory_handle_new (GnomeVFSURI *uri,
55                                 GnomeVFSMethodHandle *method_handle,
56                                 GnomeVFSFileInfoOptions options)
57 {
58         GnomeVFSDirectoryHandle *new;
59
60         g_return_val_if_fail (uri != NULL, NULL);
61         g_return_val_if_fail (method_handle != NULL, NULL);
62
63         new = g_new (GnomeVFSDirectoryHandle, 1);
64
65         gnome_vfs_uri_ref (uri);
66
67         new->uri = uri;
68         new->method_handle = method_handle;
69         new->options = options;
70
71         return new;
72 }
73
74 static void
75 gnome_vfs_directory_handle_destroy (GnomeVFSDirectoryHandle *handle)
76 {
77         g_return_if_fail (handle != NULL);
78
79         gnome_vfs_uri_unref (handle->uri);
80
81         g_free (handle);
82 }
83
84 \f
85 static GnomeVFSResult
86 open_from_uri (GnomeVFSDirectoryHandle **handle,
87                GnomeVFSURI *uri,
88                GnomeVFSFileInfoOptions options,
89                GnomeVFSContext *context)
90 {
91         GnomeVFSMethodHandle *method_handle;
92         GnomeVFSResult result;
93
94         CHECK_IF_SUPPORTED (uri->method, open_directory);
95
96
97         result = uri->method->open_directory (uri->method, 
98                                               &method_handle, 
99                                               uri,
100                                               options, 
101                                               context);
102         if (result != GNOME_VFS_OK) {
103                 return result;
104         }
105
106         *handle = gnome_vfs_directory_handle_new (uri,
107                                                   method_handle,
108                                                   options);
109
110         return GNOME_VFS_OK;
111 }
112
113 static GnomeVFSResult
114 open (GnomeVFSDirectoryHandle **handle,
115       const gchar *text_uri,
116       GnomeVFSFileInfoOptions options,
117       GnomeVFSContext *context)
118 {
119         GnomeVFSURI *uri;
120         GnomeVFSResult result;
121
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);
124
125         uri = gnome_vfs_uri_new (text_uri);
126         if (uri == NULL)
127                 return GNOME_VFS_ERROR_INVALID_URI;
128
129         result = open_from_uri (handle, uri, options,
130                                 context);
131
132         gnome_vfs_uri_unref (uri);
133
134         return result;
135 }
136
137 /**
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
142  * 
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.
146  * 
147  * Returns: An integer representing the result of the operation
148  **/
149 GnomeVFSResult
150 gnome_vfs_directory_open (GnomeVFSDirectoryHandle **handle,
151                           const gchar *text_uri,
152                           GnomeVFSFileInfoOptions options)
153 {
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);
156
157         return open (handle, text_uri, options, NULL);
158 }
159
160 /**
161  * gnome_vfs_directory_open_from_uri
162  * @handle: A pointer to a pointer to a GnomeVFSDirectoryHandle object
163  * @uri: URI to open
164  * @options: Options for reading file information
165  * 
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.
169  * 
170  * Returns: An integer representing the result of the operation.
171  **/
172 GnomeVFSResult
173 gnome_vfs_directory_open_from_uri (GnomeVFSDirectoryHandle **handle,
174                                    GnomeVFSURI *uri,
175                                    GnomeVFSFileInfoOptions options)
176 {
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);
179
180         return open_from_uri (handle, uri, options, NULL);
181 }
182
183 GnomeVFSResult
184 gnome_vfs_directory_open_from_uri_cancellable (GnomeVFSDirectoryHandle **handle,
185                                    GnomeVFSURI *uri,
186                                    GnomeVFSFileInfoOptions options,
187                                    GnomeVFSContext *context)
188 {
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);
191
192         return open_from_uri (handle, uri, options, context);
193 }
194
195 /**
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
200  * 
201  * Read the next directory entry from @handle.
202  * 
203  * Returns: An integer value representing the result of the operation.
204  **/
205 GnomeVFSResult
206 gnome_vfs_directory_read_next (GnomeVFSDirectoryHandle *handle,
207                                GnomeVFSFileInfo *file_info)
208 {
209         CHECK_IF_SUPPORTED (handle->uri->method, read_directory);
210
211         gnome_vfs_file_info_clear (file_info);
212         return handle->uri->method->read_directory (handle->uri->method,
213                                                     handle->method_handle,
214                                                     file_info, NULL);
215 }
216
217 GnomeVFSResult 
218 gnome_vfs_directory_read_next_cancellable (GnomeVFSDirectoryHandle *handle,
219                                            GnomeVFSFileInfo *file_info,
220                                            GnomeVFSContext *context)
221 {
222         CHECK_IF_SUPPORTED (handle->uri->method, read_directory);
223
224         gnome_vfs_file_info_clear (file_info);
225         return handle->uri->method->read_directory (handle->uri->method,
226                                                     handle->method_handle,
227                                                     file_info, context);
228 }
229
230
231 /**
232  * gnome_vfs_directory_close:
233  * @handle: A directory handle.
234  * 
235  * Close @handle.
236  * 
237  * Returns: An integer representing the result of the operation.
238  */
239 GnomeVFSResult
240 gnome_vfs_directory_close (GnomeVFSDirectoryHandle *handle)
241 {
242         GnomeVFSResult result;
243
244         CHECK_IF_SUPPORTED (handle->uri->method, close_directory);
245
246         result = handle->uri->method->close_directory (handle->uri->method,
247                                                        handle->method_handle,
248                                                        NULL);
249
250         gnome_vfs_directory_handle_destroy (handle);
251
252         return result;
253 }
254
255 \f
256 struct _DirectoryReference {
257         ino_t inode;
258         dev_t device;
259 };
260 typedef struct _DirectoryReference DirectoryReference;
261
262 static GList *
263 prepend_reference (GList *reference_list,
264                    GnomeVFSFileInfo *info)
265 {
266         DirectoryReference *reference;
267
268         reference = g_new (DirectoryReference, 1);
269         reference->device = info->device;
270         reference->inode = info->inode;
271
272         return g_list_prepend (reference_list, reference);
273 }
274
275 static GList *
276 remove_first_reference (GList *reference_list)
277 {
278         GList *first;
279
280         if (reference_list == NULL)
281                 return NULL;
282
283         first = reference_list;
284         g_free (first->data);
285
286         reference_list = g_list_remove_link (reference_list, first);
287         g_list_free (first);
288
289         return reference_list;
290 }
291
292 static gboolean
293 lookup_ancestor (GList *ancestors,
294                  gboolean inode_and_device_are_valid,
295                  ino_t inode,
296                  dev_t device)
297 {
298         GList *p;
299
300         if (!inode_and_device_are_valid) {
301                 return g_list_length (ancestors) >= VFS_MAXIMUM_SYMBOLIC_LINK_DEPTH;
302         }
303
304         for (p = ancestors; p != NULL; p = p->next) {
305                 DirectoryReference *reference;
306
307                 reference = p->data;
308                 if (reference->inode == inode && reference->device == device)
309                         return TRUE;
310         }
311
312         return FALSE;
313 }
314
315 static GnomeVFSResult
316 directory_visit_internal (GnomeVFSURI *uri,
317                           const gchar *prefix,
318                           GList *ancestor_references, /* DirectoryReference */
319                           GnomeVFSFileInfoOptions info_options,
320                           GnomeVFSDirectoryVisitOptions visit_options,
321                           GnomeVFSDirectoryVisitFunc callback,
322                           gpointer data)
323 {
324         GnomeVFSFileInfo *info;
325         GnomeVFSDirectoryHandle *handle;
326         GnomeVFSResult result;
327         gboolean stop;
328
329         /* The first time, initialize the ancestor list with this
330            directory.  */
331         if (prefix == NULL) {
332                 GnomeVFSFileInfo *info;
333
334                 info = gnome_vfs_file_info_new ();
335                 result = gnome_vfs_get_file_info_uri (uri, info,
336                                                       info_options);
337                 if (result != GNOME_VFS_OK) {
338                         gnome_vfs_file_info_unref (info);
339                         return result;
340                 }
341
342                 if (info->type != GNOME_VFS_FILE_TYPE_DIRECTORY) {
343                         gnome_vfs_file_info_unref (info);
344                         return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
345                 }
346
347                 ancestor_references = prepend_reference (ancestor_references,
348                                                          info);
349                 gnome_vfs_file_info_unref (info);
350         }
351
352         result = gnome_vfs_directory_open_from_uri (&handle, uri, info_options);
353         if (result != GNOME_VFS_OK)
354                 return result;
355
356         info = gnome_vfs_file_info_new ();
357
358         stop = FALSE;
359         while (! stop) {
360                 gchar *rel_path;
361                 gboolean recurse;
362                 gboolean recursing_will_loop;
363
364                 result = gnome_vfs_directory_read_next (handle, info);
365                 if (result != GNOME_VFS_OK)
366                         break;
367
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);
373                         continue;
374                 }
375
376                 if (prefix == NULL)
377                         rel_path = g_strdup (info->name);
378                 else
379                         rel_path = g_strconcat (prefix, info->name, NULL);
380
381                 if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY
382                     && (visit_options & GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK))
383                         recursing_will_loop
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);
388                 else
389                         recursing_will_loop = FALSE;
390
391                 recurse = FALSE;
392                 stop = ! (* callback) (rel_path, info, recursing_will_loop,
393                                        data, &recurse);
394
395                 if (! stop
396                     && recurse
397                     && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
398                         GnomeVFSURI *new_uri;
399                         gchar *new_prefix;
400
401                         if (prefix == NULL)
402                                 new_prefix = g_strconcat (info->name, "/",
403                                                           NULL);
404                         else
405                                 new_prefix = g_strconcat (prefix, info->name,
406                                                           "/", NULL);
407
408                         new_uri = gnome_vfs_uri_append_file_name (uri, info->name);
409
410
411                         if (GNOME_VFS_FILE_INFO_LOCAL (info))
412                                 ancestor_references = prepend_reference
413                                         (ancestor_references, info);
414
415                         result = directory_visit_internal (new_uri,
416                                                            new_prefix,
417                                                            ancestor_references,
418                                                            info_options,
419                                                            visit_options,
420                                                            callback, data);
421
422                         if (GNOME_VFS_FILE_INFO_LOCAL (info))
423                                 ancestor_references = remove_first_reference
424                                         (ancestor_references);
425
426                         if (result != GNOME_VFS_OK)
427                                 stop = TRUE;
428
429                         gnome_vfs_uri_unref (new_uri);
430                         g_free (new_prefix);
431                 }
432
433                 g_free (rel_path);
434
435                 gnome_vfs_file_info_clear (info);
436
437                 if (stop)
438                         break;
439         }
440
441         gnome_vfs_directory_close (handle);
442         gnome_vfs_file_info_unref (info);
443
444         /* The first time, we are responsible for de-allocating the directory
445            reference we have added by ourselves.  */
446         if (prefix == NULL)
447                 ancestor_references
448                         = remove_first_reference (ancestor_references);
449
450         if (result == GNOME_VFS_ERROR_EOF)
451                 return GNOME_VFS_OK;
452         else
453                 return result;
454 }
455
456 /**
457  * gnome_vfs_directory_visit_uri
458  * @uri: URI to start from
459  * @info_options: Options specifying what kind of file information must be
460  * retrieved
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
464  * 
465  * Visit @uri, retrieving information as specified by @info_options. 
466  * 
467  * Returns: A result code indicating whether the operation succeeded. 
468  *
469  */
470 GnomeVFSResult
471 gnome_vfs_directory_visit_uri (GnomeVFSURI *uri,
472                                GnomeVFSFileInfoOptions info_options,
473                                GnomeVFSDirectoryVisitOptions visit_options,
474                                GnomeVFSDirectoryVisitFunc callback,
475                                gpointer data)
476 {
477         g_return_val_if_fail (uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
478
479         return directory_visit_internal (uri, NULL, NULL,
480                                          info_options,
481                                          visit_options, callback, data);
482 }
483
484 /**
485  * gnome_vfs_directory_visit:
486  * @uri: URI to start from
487  * @info_options: Options specifying what kind of file information must be
488  * retrieved
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
492  * 
493  * Visit @uri, retrieving information as specified by @info_options.
494  * 
495  * Return value: 
496  **/
497 GnomeVFSResult
498 gnome_vfs_directory_visit (const gchar *text_uri,
499                            GnomeVFSFileInfoOptions info_options,
500                            GnomeVFSDirectoryVisitOptions visit_options,
501                            GnomeVFSDirectoryVisitFunc callback,
502                            gpointer data)
503 {
504         GnomeVFSURI *uri;
505         GnomeVFSResult result;
506
507         g_return_val_if_fail (text_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
508
509         uri = gnome_vfs_uri_new (text_uri);
510         if (uri == NULL) {
511                 return GNOME_VFS_ERROR_INVALID_URI;
512         }
513
514         result = directory_visit_internal (uri, NULL, NULL,
515                                            info_options,
516                                            visit_options, callback, data);
517
518         gnome_vfs_uri_unref (uri);
519
520         return result;
521 }
522
523 /**
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.
532  *
533  * Fetches information about a list of files in a base URI @uri.
534  *
535  * Return value: a GnomeVFSResult indication the success of the operation
536  **/
537 GnomeVFSResult
538 gnome_vfs_directory_visit_files_at_uri (GnomeVFSURI *uri,
539                                         GList *file_list,
540                                         GnomeVFSFileInfoOptions info_options,
541                                         GnomeVFSDirectoryVisitOptions visit_options,
542                                         GnomeVFSDirectoryVisitFunc callback,
543                                         gpointer data)
544 {
545         GnomeVFSFileInfo *info;
546         GnomeVFSResult result;
547         GList *p;
548
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);
551
552         info = gnome_vfs_file_info_new ();
553         result = GNOME_VFS_OK;
554
555         for (p = file_list; p != NULL; p = p->next) {
556                 GnomeVFSURI *file_uri;
557                 gboolean recurse;
558                 gboolean stop;
559
560                 file_uri = gnome_vfs_uri_append_file_name (uri, p->data);
561                 gnome_vfs_get_file_info_uri (file_uri, 
562                                              info, 
563                                              info_options);
564
565                 recurse = FALSE;
566                 stop = ! (* callback) (info->name, info, FALSE, data,
567                                        &recurse);
568
569                 if (! stop
570                     && recurse
571                     && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
572                         result = gnome_vfs_directory_visit_uri
573                                 (uri, 
574                                  info_options,
575                                  visit_options,
576                                  callback, 
577                                  data);
578
579                 gnome_vfs_uri_unref (file_uri);
580
581                 if (result != GNOME_VFS_OK || stop)
582                         break;
583         }
584
585         gnome_vfs_file_info_unref (info);
586         return GNOME_VFS_OK;
587 }
588
589 /**
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.
598  *
599  * Fetches information about a list of files in a base URI @uri.
600  *
601  * Return value: a GnomeVFSResult indication the success of the operation
602  **/
603 GnomeVFSResult
604 gnome_vfs_directory_visit_files (const gchar *text_uri,
605                                  GList *file_list,
606                                  GnomeVFSFileInfoOptions info_options,
607                                  GnomeVFSDirectoryVisitOptions visit_options,
608                                  GnomeVFSDirectoryVisitFunc callback,
609                                  gpointer data)
610 {
611         GnomeVFSURI *uri;
612         GnomeVFSResult result;
613
614         uri = gnome_vfs_uri_new (text_uri);
615
616         result = gnome_vfs_directory_visit_files_at_uri (uri, file_list,
617                                                          info_options,
618                                                          visit_options,
619                                                          callback,
620                                                          data);
621         gnome_vfs_uri_unref (uri);
622
623         return result;
624 }
625
626 static GnomeVFSResult
627 load_from_handle (GList **list,
628                   GnomeVFSDirectoryHandle *handle)
629 {
630         GnomeVFSResult result;
631         GnomeVFSFileInfo *info;
632
633         *list = NULL;
634
635         for (;;) {
636                 info = gnome_vfs_file_info_new ();
637                 result = gnome_vfs_directory_read_next (handle, info);
638                 if (result != GNOME_VFS_OK)
639                         break;
640                 *list = g_list_prepend (*list, info);
641         }
642
643         *list = g_list_reverse (*list);
644         
645         gnome_vfs_file_info_unref (info);
646
647         if (result != GNOME_VFS_ERROR_EOF) {
648                 gnome_vfs_file_info_list_free (*list);
649                 *list = NULL;
650         }
651
652         return GNOME_VFS_OK;
653 }
654
655 /**
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 
660  * 
661  * Load a directory from @text_uri with the specified @options
662  * into a list.
663  * 
664  * Return value: An integer representing the result of the operation.
665  **/
666 GnomeVFSResult 
667 gnome_vfs_directory_list_load (GList **list,
668                                const gchar *text_uri,
669                                GnomeVFSFileInfoOptions options)
670 {
671         GnomeVFSDirectoryHandle *handle;
672         GnomeVFSResult result;
673
674         result = gnome_vfs_directory_open (&handle, text_uri, options);
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