|
|
/* -*- coding: utf-8 -*-
* ---------------------------------------------------------------------- * Copyright © 2012, RedJack, LLC. * All rights reserved. * * Please see the COPYING file in this distribution for license * details. * ---------------------------------------------------------------------- */
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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; }
|