-fix ftbfs
[oweals/gnunet.git] / src / util / disk_iterator.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001--2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file util/disk_iterator.c
22  * @brief asynchronous iteration over a directory
23  * @author Christian Grothoff
24  * @author Nils Durner
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "disk.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
31
32 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
33
34 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
35
36
37 /**
38  * Opaque handle used for iterating over a directory.
39  */
40 struct GNUNET_DISK_DirectoryIterator
41 {
42
43   /**
44    * Function to call on directory entries.
45    */
46   GNUNET_DISK_DirectoryIteratorCallback callback;
47
48   /**
49    * Closure for @e callback.
50    */
51   void *callback_cls;
52
53   /**
54    * Reference to directory.
55    */
56   DIR *directory;
57
58   /**
59    * Directory name.
60    */
61   char *dirname;
62
63   /**
64    * Next filename to process.
65    */
66   char *next_name;
67
68   /**
69    * Our priority.
70    */
71   enum GNUNET_SCHEDULER_Priority priority;
72
73 };
74
75
76 /**
77  * Task used by the directory iterator.
78  */
79 static void
80 directory_iterator_task (void *cls,
81                          const struct GNUNET_SCHEDULER_TaskContext *tc)
82 {
83   struct GNUNET_DISK_DirectoryIterator *iter = cls;
84   char *name;
85
86   name = iter->next_name;
87   GNUNET_assert (name != NULL);
88   iter->next_name = NULL;
89   iter->callback (iter->callback_cls, iter, name, iter->dirname);
90   GNUNET_free (name);
91 }
92
93
94 /**
95  * This function must be called during the DiskIteratorCallback
96  * (exactly once) to schedule the task to process the next
97  * filename in the directory (if there is one).
98  *
99  * @param iter opaque handle for the iterator
100  * @param can set to #GNUNET_YES to terminate the iteration early
101  * @return #GNUNET_YES if iteration will continue,
102  *         #GNUNET_NO if this was the last entry (and iteration is complete),
103  *         #GNUNET_SYSERR if abort was YES
104  */
105 int
106 GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator *iter,
107                                      int can)
108 {
109   struct dirent *finfo;
110
111   GNUNET_assert (iter->next_name == NULL);
112   if (can == GNUNET_YES)
113   {
114     CLOSEDIR (iter->directory);
115     GNUNET_free (iter->dirname);
116     GNUNET_free (iter);
117     return GNUNET_SYSERR;
118   }
119   while (NULL != (finfo = READDIR (iter->directory)))
120   {
121     if ((0 == strcmp (finfo->d_name, ".")) ||
122         (0 == strcmp (finfo->d_name, "..")))
123       continue;
124     GNUNET_asprintf (&iter->next_name, "%s%s%s", iter->dirname,
125                      DIR_SEPARATOR_STR, finfo->d_name);
126     break;
127   }
128   if (finfo == NULL)
129   {
130     GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
131     return GNUNET_NO;
132   }
133   GNUNET_SCHEDULER_add_with_priority (iter->priority, &directory_iterator_task,
134                                       iter);
135   return GNUNET_YES;
136 }
137
138
139 /**
140  * Scan a directory for files using the scheduler to run a task for
141  * each entry.  The name of the directory must be expanded first (!).
142  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
143  * may provide a simpler API.
144  *
145  * @param prio priority to use
146  * @param dir_name the name of the directory
147  * @param callback the method to call for each file
148  * @param callback_cls closure for @a callback
149  * @return #GNUNET_YES if directory is not empty and @a callback
150  *         will be called later, #GNUNET_NO otherwise, #GNUNET_SYSERR on error.
151  */
152 int
153 GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
154                                       const char *dir_name,
155                                       GNUNET_DISK_DirectoryIteratorCallback
156                                       callback, void *callback_cls)
157 {
158   struct GNUNET_DISK_DirectoryIterator *di;
159
160   di = GNUNET_new (struct GNUNET_DISK_DirectoryIterator);
161   di->callback = callback;
162   di->callback_cls = callback_cls;
163   di->directory = OPENDIR (dir_name);
164   if (di->directory == NULL)
165   {
166     GNUNET_free (di);
167     callback (callback_cls, NULL, NULL, NULL);
168     return GNUNET_SYSERR;
169   }
170   di->dirname = GNUNET_strdup (dir_name);
171   di->priority = prio;
172   return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
173 }
174
175 /* end of disk_iterator */