/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, RedJack, LLC. * All rights reserved. * * Please see the COPYING file in this distribution for license * details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include "libcork/core/attributes.h" #include "libcork/core/error.h" #include "libcork/core/types.h" #include "libcork/ds/buffer.h" #include "libcork/helpers/errors.h" #include "libcork/helpers/posix.h" #include "libcork/os/files.h" static int cork_walk_one_directory(struct cork_dir_walker *w, struct cork_buffer *path, size_t root_path_size) { DIR *dir = NULL; struct dirent *entry; size_t dir_path_size; rip_check_posix(dir = opendir(path->buf)); cork_buffer_append(path, "/", 1); dir_path_size = path->size; errno = 0; while ((entry = readdir(dir)) != NULL) { struct stat info; /* Skip the "." and ".." entries */ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } /* Stat the directory entry */ cork_buffer_append_string(path, entry->d_name); ei_check_posix(stat(path->buf, &info)); /* If the entry is a subdirectory, recurse into it. */ if (S_ISDIR(info.st_mode)) { int rc = cork_dir_walker_enter_directory (w, path->buf, path->buf + root_path_size, path->buf + dir_path_size); if (rc != CORK_SKIP_DIRECTORY) { ei_check(cork_walk_one_directory(w, path, root_path_size)); ei_check(cork_dir_walker_leave_directory (w, path->buf, path->buf + root_path_size, path->buf + dir_path_size)); } } else if (S_ISREG(info.st_mode)) { ei_check(cork_dir_walker_file (w, path->buf, path->buf + root_path_size, path->buf + dir_path_size)); } /* Remove this entry name from the path buffer. */ cork_buffer_truncate(path, dir_path_size); /* We have to reset errno to 0 because of the ambiguous way * readdir uses a return value of NULL. Other functions may * return normally yet set errno to a non-zero value. dlopen * on Mac OS X is an ogreish example. Since an error readdir * is indicated by returning NULL and setting errno to indicate * the error, then we need to reset it to zero before each call. * We shall assume, perhaps to our great misery, that functions * within this loop do proper error checking and act accordingly. */ errno = 0; } /* Check errno immediately after the while loop terminates */ if (CORK_UNLIKELY(errno != 0)) { cork_system_error_set(); goto error; } /* Remove the trailing '/' from the path buffer. */ cork_buffer_truncate(path, dir_path_size - 1); rii_check_posix(closedir(dir)); return 0; error: if (dir != NULL) { rii_check_posix(closedir(dir)); } return -1; } int cork_walk_directory(const char *path, struct cork_dir_walker *w) { int rc; char *p; struct cork_buffer buf = CORK_BUFFER_INIT(); /* Seed the buffer with the directory's path, ensuring that there's no * trailing '/' */ cork_buffer_append_string(&buf, path); p = buf.buf; while (p[buf.size-1] == '/') { buf.size--; p[buf.size] = '\0'; } rc = cork_walk_one_directory(w, &buf, buf.size + 1); cork_buffer_done(&buf); return rc; }