headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / libroot / posix / dirent.c
blob198c23d9bdb15e794fb677d5f90614d0c793405a
1 /*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
8 #include <dirent.h>
9 #include <dirent_private.h>
11 #include <errno.h>
12 #include <limits.h>
13 #include <stdlib.h>
14 #include <string.h>
16 #include <errno_private.h>
17 #include <syscalls.h>
18 #include <syscall_utils.h>
21 #define DIR_BUFFER_SIZE 4096
24 struct __DIR {
25 int fd;
26 short next_entry;
27 unsigned short entries_left;
28 long seek_position;
29 long current_position;
30 struct dirent first_entry;
34 static int
35 do_seek_dir(DIR* dir)
37 if (dir->seek_position == dir->current_position)
38 return 0;
40 // If the seek position lies before the current position (the usual case),
41 // rewind to the beginning.
42 if (dir->seek_position < dir->current_position) {
43 status_t status = _kern_rewind_dir(dir->fd);
44 if (status < 0) {
45 __set_errno(status);
46 return -1;
49 dir->current_position = 0;
50 dir->entries_left = 0;
53 // Now skip entries until we have reached seek_position.
54 while (dir->seek_position > dir->current_position) {
55 ssize_t count;
56 long toSkip = dir->seek_position - dir->current_position;
57 if (toSkip == dir->entries_left) {
58 // we have to skip exactly all of the currently buffered entries
59 dir->current_position = dir->seek_position;
60 dir->entries_left = 0;
61 return 0;
64 if (toSkip < dir->entries_left) {
65 // we have to skip only some of the buffered entries
66 for (; toSkip > 0; toSkip--) {
67 struct dirent* entry = (struct dirent*)
68 ((uint8*)&dir->first_entry + dir->next_entry);
69 dir->entries_left--;
70 dir->next_entry += entry->d_reclen;
73 dir->current_position = dir->seek_position;
74 return 0;
77 // we have to skip more than the currently buffered entries
78 dir->current_position += dir->entries_left;
79 dir->entries_left = 0;
81 count = _kern_read_dir(dir->fd, &dir->first_entry,
82 (char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX);
83 if (count <= 0) {
84 if (count < 0)
85 __set_errno(count);
87 // end of directory
88 return -1;
91 dir->next_entry = 0;
92 dir->entries_left = count;
95 return 0;
99 // #pragma mark - private API
102 DIR*
103 __create_dir_struct(int fd)
105 /* allocate the memory for the DIR structure */
107 DIR* dir = (DIR*)malloc(DIR_BUFFER_SIZE);
108 if (dir == NULL) {
109 __set_errno(B_NO_MEMORY);
110 return NULL;
113 dir->fd = fd;
114 dir->entries_left = 0;
115 dir->seek_position = 0;
116 dir->current_position = 0;
118 return dir;
122 // #pragma mark - public API
125 DIR*
126 fdopendir(int fd)
128 DIR* dir;
130 // Since our standard file descriptors can't be used as directory file
131 // descriptors, we have to open a fresh one explicitly.
132 int dirFD = _kern_open_dir(fd, NULL);
133 if (dirFD < 0) {
134 __set_errno(dirFD);
135 return NULL;
138 // Since applications are allowed to use the file descriptor after a call
139 // to fdopendir() without changing its state (like for other *at()
140 // functions), we cannot close it now.
141 // We dup2() the new FD to the previous location instead.
142 if (dup2(dirFD, fd) == -1)
143 close(fd);
144 else {
145 close(dirFD);
146 dirFD = fd;
147 fcntl(dirFD, F_SETFD, FD_CLOEXEC);
148 // reset close-on-exec which is cleared by dup()
151 dir = __create_dir_struct(dirFD);
152 if (dir == NULL) {
153 close(dirFD);
154 return NULL;
157 return dir;
161 DIR*
162 opendir(const char* path)
164 DIR* dir;
166 int fd = _kern_open_dir(-1, path);
167 if (fd < 0) {
168 __set_errno(fd);
169 return NULL;
172 // allocate the DIR structure
173 if ((dir = __create_dir_struct(fd)) == NULL) {
174 _kern_close(fd);
175 return NULL;
178 return dir;
183 closedir(DIR* dir)
185 int status;
187 if (dir == NULL) {
188 __set_errno(B_BAD_VALUE);
189 return -1;
192 status = _kern_close(dir->fd);
194 free(dir);
196 RETURN_AND_SET_ERRNO(status);
200 struct dirent*
201 readdir(DIR* dir)
203 ssize_t count;
205 if (dir->seek_position != dir->current_position) {
206 if (do_seek_dir(dir) != 0)
207 return NULL;
210 if (dir->entries_left > 0) {
211 struct dirent *dirent
212 = (struct dirent *)((uint8 *)&dir->first_entry + dir->next_entry);
214 dir->entries_left--;
215 dir->next_entry += dirent->d_reclen;
216 dir->seek_position++;
217 dir->current_position++;
219 return dirent;
222 // we need to retrieve new entries
224 count = _kern_read_dir(dir->fd, &dir->first_entry,
225 (char*)dir + DIR_BUFFER_SIZE - (char*)&dir->first_entry, USHRT_MAX);
226 if (count <= 0) {
227 if (count < 0)
228 __set_errno(count);
230 // end of directory
231 return NULL;
234 dir->entries_left = count - 1;
235 dir->next_entry = dir->first_entry.d_reclen;
236 dir->seek_position++;
237 dir->current_position++;
239 return &dir->first_entry;
244 readdir_r(DIR* dir, struct dirent* entry, struct dirent** _result)
246 ssize_t count;
248 if (dir->seek_position != dir->current_position) {
249 if (do_seek_dir(dir) != 0)
250 return -1;
253 if (dir->entries_left > 0) {
254 *_result
255 = (struct dirent *)((uint8 *)&dir->first_entry + dir->next_entry);
257 dir->entries_left--;
258 dir->next_entry += (*_result)->d_reclen;
259 dir->seek_position++;
260 dir->current_position++;
262 return 0;
265 count = _kern_read_dir(dir->fd, entry, sizeof(struct dirent)
266 + B_FILE_NAME_LENGTH, 1);
267 if (count < B_OK)
268 return count;
270 if (count == 0) {
271 // end of directory
272 *_result = NULL;
273 } else {
274 *_result = entry;
275 dir->entries_left = count - 1;
276 dir->next_entry = dir->first_entry.d_reclen;
277 dir->seek_position++;
278 dir->current_position++;
281 return 0;
285 void
286 rewinddir(DIR* dir)
288 dir->seek_position = 0;
292 void
293 seekdir(DIR* dir, long int position)
295 dir->seek_position = position;
299 long int
300 telldir(DIR* dir)
302 return dir->seek_position;
307 dirfd(DIR* dir)
309 return dir->fd;
314 alphasort(const struct dirent** entry1, const struct dirent** entry2)
316 return strcmp((*entry1)->d_name, (*entry2)->d_name);
321 scandir(const char* path, struct dirent*** _entryArray,
322 int (*selectFunc)(const struct dirent*),
323 int (*compareFunc)(const struct dirent** entry1,
324 const struct dirent** entry2))
326 struct dirent** array = NULL;
327 size_t arrayCapacity = 0;
328 size_t arrayCount = 0;
330 DIR* dir = opendir(path);
331 if (dir == NULL)
332 return -1;
334 while (true) {
335 struct dirent* copiedEntry;
337 struct dirent* entry = readdir(dir);
338 if (entry == NULL)
339 break;
341 // Check whether or not we should include this entry
342 if (selectFunc != NULL && !selectFunc(entry))
343 continue;
345 copiedEntry = malloc(entry->d_reclen);
346 if (copiedEntry == NULL)
347 goto error;
349 memcpy(copiedEntry, entry, entry->d_reclen);
351 // Put it into the array
353 if (arrayCount == arrayCapacity) {
354 struct dirent** newArray;
356 // Enlarge array
357 if (arrayCapacity == 0)
358 arrayCapacity = 64;
359 else
360 arrayCapacity *= 2;
362 newArray = realloc(array, arrayCapacity * sizeof(void*));
363 if (newArray == NULL) {
364 free(copiedEntry);
365 goto error;
368 array = newArray;
371 array[arrayCount++] = copiedEntry;
374 closedir(dir);
376 if (arrayCount > 0 && compareFunc != NULL) {
377 qsort(array, arrayCount, sizeof(void*),
378 (int (*)(const void*, const void*))compareFunc);
381 *_entryArray = array;
382 return arrayCount;
384 error:
385 closedir(dir);
387 while (arrayCount-- > 0)
388 free(array[arrayCount]);
389 free(array);
391 return -1;