everything in its own place

This commit is contained in:
2020-02-14 23:12:10 -08:00
parent 769de599fc
commit 81f4a77333
20 changed files with 0 additions and 192 deletions

121
libdirutils/dirlist.c Normal file
View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* ---------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kst/dirlist.h"
/*
* create and initialise the directory list.
*/
struct tq_dirlst
*dirlst_create(const char *init_path, size_t init_path_sz)
{
struct tq_dirlst *path_lst;
struct dirlst *elm;
if (init_path_sz > FILENAME_MAX) {
return NULL;
}
path_lst = calloc(1, sizeof(struct tq_dirlst));
if (NULL == path_lst) {
return path_lst;
}
elm = calloc(1, sizeof(struct dirlst));
if (NULL == elm) {
free(path_lst);
return NULL;
}
memcpy(elm->path, init_path, init_path_sz);
TAILQ_INIT(path_lst);
TAILQ_INSERT_HEAD(path_lst, elm, dirs);
return path_lst;
}
/*
* add a new directory to the list.
*/
int
dirlst_push(struct tq_dirlst *lst, const char *new_dir, size_t new_dir_sz)
{
struct dirlst *elm;
if (new_dir_sz > FILENAME_MAX) {
return EXIT_FAILURE;
}
elm = calloc(1, sizeof(struct dirlst));
if (NULL == elm) {
return EXIT_FAILURE;
}
strncpy(elm->path, new_dir, FILENAME_MAX);
TAILQ_INSERT_HEAD(lst, elm, dirs);
return EXIT_SUCCESS;
}
/*
* remove the first directory in the list and return it
*/
struct dirlst
*dirlst_pop(struct tq_dirlst *lst)
{
struct dirlst *elm;
if (TAILQ_EMPTY(lst)) {
return NULL;
}
elm = TAILQ_FIRST(lst);
TAILQ_REMOVE(lst, elm, dirs);
return elm;
}
int
dirlst_destroy(struct tq_dirlst **lstp)
{
struct dirlst *elm;
while ((elm = TAILQ_FIRST(*lstp))) {
TAILQ_REMOVE(*lstp, elm, dirs);
free(elm);
}
if (!TAILQ_EMPTY(*lstp)) {
return EXIT_FAILURE;
}
free(*lstp);
*lstp = NULL;
return EXIT_SUCCESS;
}

212
libdirutils/dirlist_test.c Normal file
View File

@@ -0,0 +1,212 @@
/*
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* ---------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <kst/dirlist.h>
/*
* helper that, given a list and the expected key, determines whether
* the the popped path matches. Return values use if semantics: success
* is 1, failure is 0. It is up to the caller to ensure that keylen is
* in the acceptable range (FILENAME_MAX) for tests that are to be
* successful.
*/
int
test_pop_helper(struct tq_dirlst *lst, const char *key, size_t keylen)
{
struct dirlst *elm;
int match;
if (keylen > FILENAME_MAX)
return 0;
elm = dirlst_pop(lst);
if (NULL == elm)
return 0;
match = strncmp(elm->path, key, keylen);
free(elm);
return 0 == match;
}
void
test_dirlst_create(void)
{
struct tq_dirlst *lst;
lst = dirlst_create((const char *)"foo", 3);
CU_ASSERT(lst != NULL);
CU_ASSERT(EXIT_SUCCESS == dirlst_destroy(&lst));
}
void
test_dirlst_single(void)
{
struct tq_dirlst *lst;
struct dirlst *elm;
int n_elms, ret;
lst = dirlst_create((const char *)"foo", 3);
ret = dirlst_push(lst, (const char *)"bar", 3);
CU_ASSERT(EXIT_SUCCESS == ret);
n_elms = 0;
TAILQ_FOREACH(elm, lst, dirs)
n_elms++;
CU_ASSERT(2 == n_elms);
CU_ASSERT(test_pop_helper(lst, "bar", 3));
n_elms = 0;
TAILQ_FOREACH(elm, lst, dirs)
n_elms++;
CU_ASSERT(1 == n_elms);
CU_ASSERT(EXIT_SUCCESS == dirlst_destroy(&lst));
}
/*
* ensure an invalid path_len aborts the push
*/
void
test_dirlst_bad(void)
{
struct tq_dirlst *lst;
lst = dirlst_create((const char *)"foo", 3);
CU_ASSERT(EXIT_SUCCESS == dirlst_push(lst, "bar", 3));
CU_ASSERT(EXIT_FAILURE == dirlst_push(lst, "baz", FILENAME_MAX * 2));
CU_ASSERT(test_pop_helper(lst, "bar", 3));
CU_ASSERT(EXIT_SUCCESS == dirlst_destroy(&lst));
}
void
test_dirlst_multi(void)
{
char testkeys[][4] = {"bar", "baz", "quux", ""};
int testkeylen[4];
struct tq_dirlst *lst;
struct dirlst *elm;
int i, n_elms, ret;
lst = dirlst_create((const char *)"foo", 3);
for(i = 0; i < 3; ++i) {
testkeylen[i] = strlen(testkeys[i]);
ret = dirlst_push(lst, testkeys[i], testkeylen[i]);
CU_ASSERT(EXIT_SUCCESS == ret);
}
n_elms = 0;
TAILQ_FOREACH(elm, lst, dirs)
n_elms++;
CU_ASSERT(4 == n_elms);
for (i = 2; i >= 0; i--)
CU_ASSERT(test_pop_helper(lst, testkeys[i], testkeylen[i]));
CU_ASSERT(test_pop_helper(lst, "foo", 3));
CU_ASSERT(EXIT_SUCCESS == dirlst_destroy(&lst));
}
/*
* Stubs required by the test suite, but for which no functionality is
* required in this code. init_test is called each time a test is run,
* and cleanup is run after every test.
*/
int init_test(void)
{
return 0;
}
int cleanup_test(void)
{
return 0;
}
/*
* fireball is the code called when adding test fails: cleanup the test
* registry and exit.
*/
void
fireball(void)
{
CU_cleanup_registry();
exit(CU_get_error());
}
/*
* The main function sets up the test suite, registers the test cases,
* runs through them, and hopefully doesn't explode.
*/
int
main(void)
{
CU_pSuite tsuite = NULL;
unsigned int fails;
printf("\n\n[+] running tests for dirutils\n");
if (!(CUE_SUCCESS == CU_initialize_registry())) {
errx(EX_CONFIG, "failed to initialise test registry");
return EXIT_FAILURE;
}
tsuite = CU_add_suite("dirlist_test", init_test, cleanup_test);
if (NULL == tsuite)
fireball();
if (NULL == CU_add_test(tsuite, "dirlst create", test_dirlst_create))
fireball();
if (NULL == CU_add_test(tsuite, "single push/pop", test_dirlst_single))
fireball();
if (NULL == CU_add_test(tsuite, "multi push/pop", test_dirlst_multi))
fireball();
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
fails = CU_get_number_of_tests_failed();
warnx("%u tests failed", fails);
CU_cleanup_registry();
return fails;
}
/*
* This is an empty test provided for reference.
*/
void
empty_test()
{
CU_ASSERT(1 == 0);
}

192
libdirutils/dirutils.c Normal file
View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* ---------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "kst/dirutils.h"
#include "kst/dirlist.h"
static int _parent_exists(const char *);
static int _rmdirs(const char *);
/*
* Determines whether a directory exists.
*/
EXISTS_STATUS
path_exists(const char *path)
{
struct stat st;
int rv;
rv = stat(path, &st);
if (rv == -1) {
switch (errno) {
case EACCES:
return EXISTS_NOPERM;
break;
case ENOENT:
return EXISTS_NOENT;
break;
default:
return EXISTS_ERROR;
}
}
if (st.st_mode & S_IFDIR) {
return EXISTS_DIR;
}
else if (st.st_mode & S_IFREG) {
return EXISTS_FILE;
}
else {
return EXISTS_OTHER;
}
}
/*
* create a path and any directories above it required
*/
int
makedirs(const char *path)
{
struct tq_dirlst *lst;
struct dirlst *elm;
size_t path_sz;
char *dnam_p, *curpath;
path_sz = strlen(path);
lst = dirlst_create(path, path_sz);
if (NULL == lst)
return EXIT_FAILURE;
curpath = strdup(path);
while (!_parent_exists(curpath)) {
dnam_p = dirname(curpath);
curpath = strdup(dnam_p);
dirlst_push(lst, curpath, strlen(curpath));
}
free(curpath);
while (NULL != (elm = dirlst_pop(lst))) {
if (-1 == mkdir(elm->path, 0777)) {
free(elm);
return EXIT_FAILURE;
}
free(elm);
}
return dirlst_destroy(&lst);
}
/*
* remove a directory and any subdirectories
*/
int
rmdirs(const char *path)
{
return _rmdirs(path);
}
/*
* Determine whether the parent directory exists, and return true if it
* does.
*/
int
_parent_exists(const char *path)
{
char *name_buf;
char *dnam_p;
name_buf = strdup(path);
dnam_p = dirname(name_buf);
if (EXISTS_DIR != path_exists(dnam_p)) {
return 0;
}
else {
return 1;
}
}
/*
* remove a directory, all files in it, and all subdirectories.
*/
int
_rmdirs(const char *path)
{
char child[FILENAME_MAX + 1];
struct dirent *dp;
DIR *dirp;
int fail = EXIT_FAILURE;
if (NULL == (dirp = opendir(path))) {
return EXIT_FAILURE;
}
while (NULL != (dp = readdir(dirp))) {
if (0 == strncmp("..", dp->d_name, 3)) {
continue;
}
if (0 == strncmp(".", dp->d_name, 2)) {
continue;
}
snprintf(child, FILENAME_MAX, "%s/%s", path, dp->d_name);
if (DT_DIR == dp->d_type) {
fail = _rmdirs(child);
if (EXIT_FAILURE == fail) {
break;
}
}
else {
fail = unlink(child);
if (-1 == fail) {
fail = EXIT_FAILURE;
break;
}
}
}
if (-1 == closedir(dirp))
return EXIT_FAILURE;
if (EXIT_FAILURE == fail) {
return EXIT_FAILURE;
}
else if (-1 == rmdir(path)) {
return EXIT_FAILURE;
}
else {
return EXIT_SUCCESS;
}
}

272
libdirutils/dirutils_test.c Normal file
View File

@@ -0,0 +1,272 @@
/*
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* ---------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <kst/dirutils.h>
static int test_write_file_helper(const char *, const char *);
static int test_touch_file_helper(const char *);
/*
* test the use of the exists function
*/
void
test_exists(void)
{
char testdir[] = "testdata/testdir";
char testfil[] = "testdata/testfile";
char testnot[] = "testdata/nosuchfile";
EXISTS_STATUS ftype;
ftype = path_exists(testdir);
CU_ASSERT(EXISTS_DIR == ftype);
ftype = path_exists(testfil);
CU_ASSERT(EXISTS_FILE == ftype);
ftype = path_exists(testnot);
CU_ASSERT(EXISTS_NOENT == ftype);
}
void
test_makedirs(void)
{
char testpath[] = "testdata/foo/bar/baz\0";
/*
* use the system to ensure we have a clean slate for this test
*/
if (EXISTS_DIR == path_exists(testpath))
system("rm -fr testdata/foo/");
CU_ASSERT(EXIT_SUCCESS == makedirs(testpath));
CU_ASSERT(EXISTS_DIR == path_exists(testpath));
/*
* we can't guarantee rmdirs yet; this ensures a clean slate.
*/
system("rm -r testdata/foo/");
}
void
test_empty_rmdirs(void)
{
char testpath[20] = "testdata/foo";
char cmd[FILENAME_MAX];
int rv;
memset(cmd, 0x0, FILENAME_MAX);
snprintf(cmd, FILENAME_MAX, "mkdir -p %s/bar/baz", testpath);
system(cmd);
rv = rmdirs(testpath);
if (EXIT_FAILURE == rv) {
printf("\n");
warn("rmdirs");
system("rm -fr testdata/foo/");
}
CU_ASSERT(EXIT_SUCCESS == rv);
CU_ASSERT(EXISTS_NOENT == path_exists(testpath));
/*
* we can't guarantee rmdirs yet; this ensures a clean slate.
*/
}
void
test_rmdirs_simple(void)
{
char testpath[] = "testdata/foo";
char cmd[FILENAME_MAX];
int rv;
memset(cmd, 0x0, FILENAME_MAX);
snprintf(cmd, FILENAME_MAX, "mkdir -p %s/bar/baz", testpath);
system(cmd);
memset(cmd, 0x0, FILENAME_MAX);
snprintf(cmd, FILENAME_MAX, "touch %s/bar/quux", testpath);
system(cmd);
rv = rmdirs(testpath);
if (EXIT_FAILURE == rv) {
printf("\n");
warn("rmdirs");
/*
* we can't guarantee rmdirs yet; this ensures a clean slate.
*/
system("rm -r testdata/foo/ 2>/dev/null");
}
CU_ASSERT(EXIT_SUCCESS == rv);
CU_ASSERT(EXISTS_NOENT == path_exists(testpath));
}
void
test_dirutils(void)
{
char testpath[] = "testdata/dirutils";
char tmp_path[FILENAME_MAX + 1];
char lnk_path[FILENAME_MAX + 1];
/* set up directory structure */
CU_ASSERT(EXISTS_NOENT == path_exists(testpath));
snprintf(tmp_path, FILENAME_MAX, "%s/foo/bar", testpath);
CU_ASSERT(EXIT_SUCCESS == makedirs(tmp_path));
CU_ASSERT(EXISTS_DIR == path_exists(tmp_path));
snprintf(tmp_path, FILENAME_MAX, "%s/foo/baz", testpath);
CU_ASSERT(EXIT_SUCCESS == makedirs(tmp_path));
CU_ASSERT(EXISTS_DIR == path_exists(tmp_path));
/* add a few files */
snprintf(tmp_path, FILENAME_MAX, "%s/foo/quux", testpath);
CU_ASSERT(EXIT_SUCCESS == test_touch_file_helper(tmp_path));
CU_ASSERT(EXISTS_FILE == path_exists(tmp_path));
snprintf(tmp_path, FILENAME_MAX, "%s/foo/bar/xyzzy", testpath);
snprintf(lnk_path, FILENAME_MAX, "%s/foo/baz/xyzzy", testpath);
CU_ASSERT(EXIT_SUCCESS == test_write_file_helper(tmp_path,
"some data should go here"));
CU_ASSERT(EXISTS_FILE == path_exists(tmp_path));
CU_ASSERT(-1 != link(tmp_path, lnk_path));
CU_ASSERT(EXISTS_FILE == path_exists(lnk_path));
snprintf(tmp_path, FILENAME_MAX, "%s/foo/bar/thud", testpath);
snprintf(lnk_path, FILENAME_MAX, "%s/foo/baz/fred", testpath);
CU_ASSERT(EXIT_SUCCESS == test_write_file_helper(tmp_path,
"we want something for the link"));
CU_ASSERT(EXISTS_FILE == path_exists(tmp_path));
CU_ASSERT(-1 != symlink(tmp_path, lnk_path));
CU_ASSERT_FATAL(EXIT_SUCCESS == rmdirs(testpath));
CU_ASSERT_FATAL(EXISTS_NOENT == path_exists(testpath));
}
/*
* utility function to touch a file
*/
static int
test_write_file_helper(const char *path, const char *data)
{
ssize_t wrsz;
size_t data_len;
int fail, fd;
fail = EXIT_SUCCESS;
data_len = strlen(data);
fd = open(path, O_WRONLY|O_CREAT, S_IRUSR| S_IWUSR);
if (-1 == fd) {
return EXIT_FAILURE;
}
wrsz = write(fd, data, data_len);
if (wrsz == -1) {
fail = EXIT_FAILURE;
} else if ((size_t)wrsz != data_len) {
fail = EXIT_FAILURE;
}
if (-1 == close(fd)) {
fail = EXIT_FAILURE;
}
return fail;
}
static int
test_touch_file_helper(const char *path)
{
return test_write_file_helper(path, "");
}
/*
* Stubs required by the test suite, but for which no functionality is
* required in this code. init_test is called each time a test is run,
* and cleanup is run after every test.
*/
int init_test(void)
{
return 0;
}
int cleanup_test(void)
{
return 0;
}
/*
* fireball is the code called when adding test fails: cleanup the test
* registry and exit.
*/
void
fireball(void)
{
CU_cleanup_registry();
exit(CU_get_error());
}
/*
* The main function sets up the test suite, registers the test cases,
* runs through them, and hopefully doesn't explode.
*/
int
main(void)
{
CU_pSuite tsuite = NULL;
unsigned int fails;
printf("\n\n[+] running tests for dirutils\n");
if (!(CUE_SUCCESS == CU_initialize_registry())) {
errx(EX_CONFIG, "failed to initialise test registry");
return EXIT_FAILURE;
}
tsuite = CU_add_suite("dirutils_test", init_test, cleanup_test);
if (NULL == tsuite)
fireball();
if (NULL == CU_add_test(tsuite, "path_exists", test_exists))
fireball();
if (NULL == CU_add_test(tsuite, "makedirs simple", test_makedirs))
fireball();
if (NULL == CU_add_test(tsuite, "empty dir rmdirs", test_empty_rmdirs))
fireball();
if (NULL == CU_add_test(tsuite, "simple rmdirs", test_rmdirs_simple))
fireball();
if (NULL == CU_add_test(tsuite, "full test", test_dirutils))
fireball();
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
fails = CU_get_number_of_tests_failed();
warnx("%u tests failed", fails);
CU_cleanup_registry();
return fails;
}

163
libdirutils/dirwalk.c Normal file
View File

@@ -0,0 +1,163 @@
/*
* Copyright (c) 2015 Kyle Isom <kyle@tyrfingr.is>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* ---------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <regex.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "kst/dirutils.h"
const unsigned char FT_ANY = DT_BLK|DT_CHR|DT_DIR|DT_FIFO|DT_LNK|DT_REG|
DT_SOCK|DT_UNKNOWN;
const unsigned char FT_STD = DT_REG|DT_DIR;
/*
* The highest that the DT_* constants are seems to be around 14, so 0x80
* seems like a sane choice.
*/
const unsigned char FT_NODESCEND = 0x80;
static int
walkpath(const char *root, dirwalk_action action, unsigned char mask)
{
char child[FILENAME_MAX+1];
struct dirent *ent;
DIR *dh;
if (NULL == (dh = opendir(root))) {
return -1;
}
while (NULL != (ent = readdir(dh))) {
if (0 == strncmp("..", ent->d_name, 3)) {
continue;
}
if (0 == strncmp(".", ent->d_name, 2)) {
continue;
}
snprintf(child, FILENAME_MAX, "%s/%s", root, ent->d_name);
if (mask | ent->d_type) {
if (-1 == action(child)) {
break;
}
}
if (DT_DIR == ent->d_type && (!(FT_NODESCEND | mask))) {
if (-1 == walkpath(child, action, mask)) {
break;
}
}
}
if (-1 == closedir(dh)) {
return -1;
}
return 0;
}
/*
* Return a valid readdir(3) type from a mode.
*/
static unsigned char
stat_mode_to_dt_type(mode_t mode)
{
if (S_ISDIR(mode)) {
return DT_DIR;
}
if (S_ISREG(mode)) {
return DT_REG;
}
if (S_ISCHR(mode)) {
return DT_CHR;
}
if (S_ISBLK(mode)) {
return DT_BLK;
}
if (S_ISFIFO(mode)) {
return DT_FIFO;
}
if (S_ISLNK(mode)) {
return DT_LNK;
}
if (S_ISSOCK(mode)) {
return DT_SOCK;
}
return DT_UNKNOWN;
}
/*
* walkdir goes through every directory entry under root and calls action on
* every entry matching the mask.
*/
int
walkdir(const char *root, dirwalk_action action, unsigned char mask)
{
struct stat st;
char *rootdup = NULL;
size_t rootlen;
int ret = -1;
unsigned char type;
if (-1 == stat(root, &st)) {
return -1;
}
type = stat_mode_to_dt_type(st.st_mode);
if (mask | type) {
if (0 != (ret = action(root))) {
return ret;
}
}
if (DT_DIR == type) {
rootlen = strnlen(root, FILENAME_MAX);
rootdup = strndup(root, FILENAME_MAX);
if (NULL == rootdup) {
return -1;
}
if ('/' == rootdup[rootlen-1]) {
rootdup[rootlen-1] = 0;
}
ret = walkpath(rootdup, action, mask);
}
free(rootdup);
return ret;
}

42
libdirutils/kst/dirlist.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* ---------------------------------------------------------------------
*/
#ifndef __DIRUTILS_DIRLIST_H
#define __DIRUTILS_DIRLIST_H
#include <sys/queue.h>
#include <stdio.h>
#include <dirent.h>
struct dirlst {
char path[FILENAME_MAX + 1];
TAILQ_ENTRY(dirlst) dirs;
};
TAILQ_HEAD(tq_dirlst, dirlst);
struct tq_dirlst *dirlst_create(const char *, size_t);
int dirlst_push(struct tq_dirlst *, const char *, size_t);
struct dirlst *dirlst_pop(struct tq_dirlst *);
int dirlst_destroy(struct tq_dirlst **);
#endif

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
* OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* ---------------------------------------------------------------------
*/
#ifndef __DIRUTILS_DIRUTILS_H
#define __DIRUTILS_DIRUTILS_H
#include <sys/queue.h>
#include <dirent.h>
#include <stdio.h>
enum E_EXISTS_STATUS {
EXISTS_ERROR,
EXISTS_NOENT,
EXISTS_NOPERM,
EXISTS_DIR,
EXISTS_FILE,
EXISTS_OTHER
};
typedef enum E_EXISTS_STATUS EXISTS_STATUS;
typedef int (*dirwalk_action)(const char *);
extern const unsigned char FT_ANY;
extern const unsigned char FT_STD;
extern const unsigned char FT_NODESCEND;
int makedirs(const char *);
int rmdirs(const char *);
EXISTS_STATUS path_exists(const char *);
int walkdir(const char *, dirwalk_action, unsigned char);
#endif

95
libdirutils/libdirutils.3 Normal file
View File

@@ -0,0 +1,95 @@
.Dd November 20, 2012
.Dt LIBDIRUTILS 3
.Os
.Sh NAME
.Nm libdirutils
.Nd Python-esque file utility functions for C
.Sh SYNOPSIS
.In dirutils.h
.Ft int
.Fo makedirs
.Fa "const char *path"
.Fc
.Ft EXISTS_STATUS
.Fo path_exists
.Fa "const char *path"
.Fc
.Ft int
.Fo rmdirs
.Fa "const char *path"
.Fc
.Ft int
.Fo walkpath
.Fa "const char *root"
.Fa "dirwalk_action action"
.Fa "unsigned char mask"
.Sh DESCRIPTION
.Nm
provides a number of convenience functions for working with files and
directories; as the name implies, it is aimed primarily at directories.
.Nm makedirs
provides functionality similar to
.Ic mkdir -p ,
creating all the parent directories required. They are created with the
caller's umask applied to the mode 0777.
.Nm path_exists
returns an EXISTS_STATUS value (described below) indicated the status
of the file.
.Nm rmdirs
provides functionality similar to
.Ic rm -r .
.Nm walkpath
walks through a path on disk and calls an action on a file if it
matches the file type mask. The mask should include some of the
file types listed in
.Xr readdir 3 ,
such as DT_DIR or DT_REG. The convenience definitions
.Ic FT_ANY
and
.Ic FT_STD
are provided; the former matches any file type and the latter matches
regular files and directories. Note that this will not follow links.
The action is defined in the dirutils.h header as
.Ic typedef int (*dirwalk_action)(const char *) ;
it takes a NULL-terminated path and does what it will with that path.
It should return -1 on failure and 0 on success. Note that in the case
where the path provided to
.Nm walkdir
is not a directory, but its type matches the mask, the action will
still be run on it.
.Sh RETURN VALUES
.Nm makedirs
and
.Nm rmdirs
return EXIT_SUCCESS on success and EXIT_FAILURE on failure. The enum
returned by
.Nm path_exists
is defined in
.Sy dirutils.h
as:
.Bd -literal
enum E_EXISTS_STATUS {
EXISTS_ERROR, /* an error occurred looking at the file */
EXISTS_NOENT, /* the path does not exist */
EXISTS_NOPERM, /* the process does not have appropriate permissions */
EXISTS_DIR, /* the path exists and is a directory */
EXISTS_FILE, /* the path exists and is a regular file */
EXISTS_OTHER /* the path exists and is not a directory or regular */
/* file. */
};
.Ed
.Sh ERRORS
The most common error will be a permissions error; it may be prudent to
compare the value of
.Nm errno
before and after calling the function.
.Sh HISTORY
.Nm
was inspired by similar functions in the Python and Go programming languages.
The author found himself in need of such functions in several projects,
and decided to write a utility library to handle these functions.
.Sh AUTHORS
The
.Nm
library was written by
.An Kyle Isom Aq Mt kyle@tyrfingr.is .