Dump some old code up in here.
This commit is contained in:
36
src/Makefile
Normal file
36
src/Makefile
Normal file
@@ -0,0 +1,36 @@
|
||||
BINS := srm
|
||||
EDS := ke kte
|
||||
LIBS := libdirutils.a libiniparser.a
|
||||
|
||||
LDFLAGS :=
|
||||
CFLAGS := -pedantic -Wall -Werror -Wextra -O0 -std=c99 -g -I../include/
|
||||
|
||||
.PHONY: all
|
||||
all: $(BINS) $(EDS) $(LIBS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(BINS) $(EDS) $(LIBS) *.o *.core
|
||||
|
||||
|
||||
ke kte:
|
||||
cd ../$@ && make && cp $@ ../src
|
||||
|
||||
srm: srm.c
|
||||
$(CC) $(CFLAGS) -o $@ srm.c
|
||||
|
||||
libiniparser.a: iniparser.o
|
||||
$(AR) -rcs $@ iniparser.o
|
||||
|
||||
iniparser-test: iniparser_test.c libiniparser.a
|
||||
$(CC) $(CFLAGS) -o $@ iniparser_test.c libiniparser.a
|
||||
|
||||
libdirutils.a: dirlist.o dirutils.o dirwalk.o
|
||||
$(AR) -rcs $@ dirlist.o dirutils.o dirwalk.o
|
||||
|
||||
dirlist-test: dirlist_test.c libdirutils.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ dirlist_test.c libdirutils.a
|
||||
|
||||
dirutils-test: dirutils_test.c libdirutils.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ dirutils_test.c libdirutils.a
|
||||
|
||||
121
src/dirlist.c
Normal file
121
src/dirlist.c
Normal 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
src/dirlist_test.c
Normal file
212
src/dirlist_test.c
Normal 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 <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(TEST_SUITE, 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
src/dirutils.c
Normal file
192
src/dirutils.c
Normal 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
267
src/dirutils_test.c
Normal file
267
src/dirutils_test.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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 <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 != 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(TEST_SUITE, 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
src/dirwalk.c
Normal file
163
src/dirwalk.c
Normal 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;
|
||||
}
|
||||
|
||||
280
src/iniparser.c
Normal file
280
src/iniparser.c
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Kyle Isom <kyle@tyrfingr.is>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <regex.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kst/iniparser.h"
|
||||
|
||||
|
||||
#define SECTION_STRING "^\\[ *([a-zA-Z0-9_-]+) *\\]$"
|
||||
#define SKIPLINE_STRING "^[ \t]*$"
|
||||
#define COMMENT_STRING "^[ \t]*[#;]"
|
||||
#define KEYVALUE_STRING "^[ \t]*([a-zA-Z0-9_-]+)[ \t]*=(.*)$"
|
||||
|
||||
|
||||
static regex_t section_regex;
|
||||
static regex_t skipline_regex;
|
||||
static regex_t comment_regex;
|
||||
static regex_t keyvalue_regex;
|
||||
|
||||
|
||||
|
||||
void
|
||||
iniparser_line_init(iniparser_line_s *line)
|
||||
{
|
||||
line->is_section = 0;
|
||||
line->name = NULL;
|
||||
line->value = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
iniparser_line_destroy(iniparser_line_s *line)
|
||||
{
|
||||
free(line->name);
|
||||
free(line->value);
|
||||
line->name = NULL;
|
||||
line->value = NULL;
|
||||
line->is_section = 0;
|
||||
line->is_set = 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iniparser_open(const char *path, iniparser_file_s **file)
|
||||
{
|
||||
iniparser_file_s *f = NULL;
|
||||
int allocated = 0;
|
||||
int ret = -1;
|
||||
|
||||
/*
|
||||
* If file is NULL, we can allocate space for it.
|
||||
*/
|
||||
if (NULL == file) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
f = *file;
|
||||
if (NULL == f) {
|
||||
if (NULL == (f = calloc(1, sizeof(*f)))) {
|
||||
return -1;
|
||||
}
|
||||
allocated = 1;
|
||||
}
|
||||
|
||||
f->source = fopen(path, "r");
|
||||
if (NULL == f->source) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
f->lineptr = NULL;
|
||||
f->linelen = 0;
|
||||
f->readlen = 0;
|
||||
*file = f;
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
if (allocated && ret) {
|
||||
free(f);
|
||||
*file = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iniparser_close(iniparser_file_s *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (NULL != file->lineptr) {
|
||||
free(file->lineptr);
|
||||
file->lineptr = NULL;
|
||||
}
|
||||
|
||||
file->linelen = 0;
|
||||
file->readlen = 0;
|
||||
ret = fclose(file->source);
|
||||
file->source = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
extract_match(const char *line, regmatch_t *pmatch)
|
||||
{
|
||||
ssize_t len;
|
||||
regoff_t eo = pmatch->rm_eo;
|
||||
regoff_t so = pmatch->rm_so;
|
||||
char *s = NULL;
|
||||
|
||||
/* Technically, these are signed. They should be checked. */
|
||||
if ((pmatch->rm_eo < 0) || (pmatch->rm_eo < 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* trim space from the end. */
|
||||
while ((eo > pmatch->rm_so) && (0x20 == line[eo-1])) {
|
||||
eo--;
|
||||
}
|
||||
|
||||
/* trim space from the beginning. */
|
||||
while ((so < eo) && (0x20 == line[so])) {
|
||||
so++;
|
||||
}
|
||||
|
||||
len = eo - so;
|
||||
|
||||
if (len <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (NULL == (s = calloc((size_t)len+1, sizeof(*s)))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(s, line + so, (size_t)len);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iniparser_readline(iniparser_file_s *file, iniparser_line_s *line)
|
||||
{
|
||||
/*
|
||||
* The longest matching regex has two matches.
|
||||
*/
|
||||
regmatch_t pmatch[3];
|
||||
|
||||
iniparser_line_destroy(line);
|
||||
if (NULL != file->lineptr) {
|
||||
free(file->lineptr);
|
||||
file->lineptr = NULL;
|
||||
file->linelen = 0;
|
||||
file->readlen = 0;
|
||||
}
|
||||
|
||||
file->readlen = getline(&(file->lineptr), &(file->linelen),
|
||||
file->source);
|
||||
if (file->readlen < 1) {
|
||||
if (0 != feof(file->source)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0xa == file->lineptr[file->readlen-1]) {
|
||||
file->lineptr[file->readlen-1] = 0;
|
||||
}
|
||||
|
||||
if (REG_NOMATCH != regexec(§ion_regex, file->lineptr, 2, pmatch, 0)) {
|
||||
line->is_section = 1;
|
||||
line->name = extract_match(file->lineptr, &pmatch[1]);
|
||||
if (NULL == line->name) {
|
||||
return -1;
|
||||
}
|
||||
line->is_set = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If a skipline or comment is detected, skip to the next line. */
|
||||
if (REG_NOMATCH != regexec(&skipline_regex, file->lineptr,
|
||||
0, NULL, 0)) {
|
||||
return iniparser_readline(file, line);
|
||||
}
|
||||
|
||||
if (REG_NOMATCH != regexec(&comment_regex, file->lineptr,
|
||||
0, NULL, 0)) {
|
||||
|
||||
return iniparser_readline(file, line);
|
||||
}
|
||||
|
||||
/* Try to parse a key-value pair. */
|
||||
if (REG_NOMATCH != regexec(&keyvalue_regex, file->lineptr,
|
||||
3, pmatch, 0)) {
|
||||
line->is_section = 0;
|
||||
line->name = extract_match(file->lineptr, &pmatch[1]);
|
||||
line->value = extract_match(file->lineptr, &pmatch[2]);
|
||||
if (NULL == line->name) {
|
||||
return -1;
|
||||
}
|
||||
line->is_set = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reaching this point means there's an invalid line in the file. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
iniparser_destroy()
|
||||
{
|
||||
regfree(§ion_regex);
|
||||
regfree(&skipline_regex);
|
||||
regfree(&comment_regex);
|
||||
regfree(&keyvalue_regex);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iniparser_init()
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
ret = regcomp(§ion_regex, SECTION_STRING, REG_EXTENDED);
|
||||
if (0 != ret) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = regcomp(&skipline_regex, SKIPLINE_STRING, REG_NOSUB|REG_EXTENDED);
|
||||
if (0 != ret) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = regcomp(&comment_regex, COMMENT_STRING, REG_NOSUB|REG_EXTENDED);
|
||||
if (0 != ret) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = regcomp(&keyvalue_regex, KEYVALUE_STRING, REG_EXTENDED);
|
||||
if (0 != ret) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
if (ret) {
|
||||
iniparser_destroy();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
96
src/iniparser_test.c
Normal file
96
src/iniparser_test.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Kyle Isom <kyle@tyrfingr.is>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "kst/iniparser.h"
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
iniparser_file_s *file = NULL;
|
||||
iniparser_line_s line;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = iniparser_init();
|
||||
if (0 != ret) {
|
||||
fprintf(stderr, "init failed: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
printf("Processing %s\n", argv[i]);
|
||||
ret = iniparser_open(argv[i], &file);
|
||||
if (0 != ret) {
|
||||
perror("_open");
|
||||
fprintf(stderr, "retval: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
iniparser_line_init(&line);
|
||||
|
||||
while (1) {
|
||||
ret = iniparser_readline(file, &line);
|
||||
/* -1 is returned on error. */
|
||||
if (-1 == ret) {
|
||||
perror("_readline");
|
||||
fprintf(stderr, "retval: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
/* 1 means EOF. */
|
||||
else if (1 == ret) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.is_section) {
|
||||
printf("Now in section '%s'\n", line.name);
|
||||
}
|
||||
else {
|
||||
printf("Read key '%s' with value '%s'\n",
|
||||
line.name, line.value);
|
||||
}
|
||||
|
||||
iniparser_line_destroy(&line);
|
||||
}
|
||||
|
||||
iniparser_close(file);
|
||||
free(file);
|
||||
file = NULL;
|
||||
iniparser_line_destroy(&line);
|
||||
}
|
||||
|
||||
exit:
|
||||
iniparser_line_destroy(&line);
|
||||
if (argc > 0) {
|
||||
iniparser_destroy();
|
||||
}
|
||||
|
||||
return ret==0;
|
||||
}
|
||||
370
src/srm.c
Normal file
370
src/srm.c
Normal file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 <dirent.h>
|
||||
#include <err.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define DEFAULT_PASSES 3
|
||||
#define DEV_RANDOM "/dev/urandom"
|
||||
#define MAX_CHUNK 4096
|
||||
#define SRM_VERSION "1.3.1"
|
||||
|
||||
|
||||
static int do_wipe(char *, size_t, int);
|
||||
static int wipe(char *);
|
||||
static int rmdirs(const char *, size_t);
|
||||
static void usage(void);
|
||||
static void version(void);
|
||||
|
||||
extern char *__progname;
|
||||
|
||||
|
||||
/*
|
||||
* overwrite a file with one or more passes of random data, then unlink it.
|
||||
*/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
size_t passes, tmp_passes, wiped, failed, i;
|
||||
char **file_ptr, **wipe_success, **wipe_fail;
|
||||
int retval, opt, verbose, recur;
|
||||
|
||||
passes = DEFAULT_PASSES;
|
||||
retval = EX_DATAERR;
|
||||
wiped = 0;
|
||||
failed = 0;
|
||||
verbose = 0;
|
||||
recur = 0;
|
||||
|
||||
if (argc == 1)
|
||||
usage();
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, "hn:rvV"))) {
|
||||
switch( opt ) {
|
||||
case 'h':
|
||||
usage();
|
||||
break; /* don't technically need it but meh */
|
||||
case 'n':
|
||||
tmp_passes = atoi(optarg);
|
||||
passes = tmp_passes > 0 ? tmp_passes : passes;
|
||||
break;
|
||||
case 'r':
|
||||
recur = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'V':
|
||||
version();
|
||||
exit(EX_OK);
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
file_ptr = argv;
|
||||
wipe_success = calloc(argc, sizeof(char *));
|
||||
wipe_fail = calloc(argc, sizeof(char *));
|
||||
|
||||
|
||||
while (NULL != *file_ptr) {
|
||||
printf("%s: ", *file_ptr);
|
||||
fflush(stdout);
|
||||
|
||||
if (EX_OK != do_wipe(*file_ptr, passes, recur)) {
|
||||
wipe_fail[failed] = strdup(*file_ptr);
|
||||
failed++;
|
||||
} else {
|
||||
wipe_success[wiped] = strdup(*file_ptr);
|
||||
wiped++;
|
||||
printf(" OK!");
|
||||
}
|
||||
file_ptr++;
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
if (0 < wiped) {
|
||||
printf("success: ");
|
||||
for( i = 0; i < wiped; ++i ) {
|
||||
printf("%s ", wipe_success[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (0 < failed) {
|
||||
printf("failures: ");
|
||||
for( i = 0; i < failed; ++i ) {
|
||||
printf("%s ", wipe_fail[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* free allocated memory */
|
||||
for (i = 0; i < failed; ++i) {
|
||||
free(wipe_fail[i]);
|
||||
wipe_fail[i] = NULL;
|
||||
}
|
||||
free(wipe_fail);
|
||||
wipe_fail = NULL;
|
||||
|
||||
for (i = 0; i < wiped; ++i) {
|
||||
free(wipe_success[i]);
|
||||
wipe_success[i] = NULL;
|
||||
}
|
||||
free(wipe_success);
|
||||
wipe_success = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* takes a filename and the number of passes to wipe the file
|
||||
*/
|
||||
int
|
||||
do_wipe(char *filename, size_t passes, int recur)
|
||||
{
|
||||
struct stat sb;
|
||||
size_t filesize, i;
|
||||
int retval;
|
||||
|
||||
retval = EX_IOERR;
|
||||
if (-1 == stat(filename, &sb)) {
|
||||
warn("%s", filename);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (recur && sb.st_mode & S_IFDIR) {
|
||||
if (EX_OK != rmdirs(filename, passes)) {
|
||||
printf("!");
|
||||
return retval;
|
||||
} else {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
retval = EX_OK;
|
||||
}
|
||||
|
||||
} else {
|
||||
filesize = sb.st_size;
|
||||
|
||||
for (i = 0; i < passes; ++i) {
|
||||
if (EX_OK != wipe(filename)) {
|
||||
printf("!");
|
||||
return retval;
|
||||
} else if (-1 == stat(filename, &sb)) {
|
||||
printf("!");
|
||||
return retval;
|
||||
} else if (filesize != (size_t)sb.st_size) {
|
||||
printf("!");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 == truncate(filename, (off_t)0))
|
||||
warn("%s", filename);
|
||||
|
||||
if (-1 == unlink(filename)) {
|
||||
warn("%s", filename);
|
||||
} else {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
retval = EX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* takes a filename and attempts to overwrite it with random data
|
||||
*/
|
||||
int
|
||||
wipe(char *filename)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
size_t chunk, filesize, rdsz, wiped, wrsz;
|
||||
FILE *devrandom, *target;
|
||||
int retval;
|
||||
char *rdata;
|
||||
|
||||
retval = EX_IOERR;
|
||||
wiped = 0;
|
||||
|
||||
if (-1 == stat(filename, &sb))
|
||||
return retval;
|
||||
|
||||
filesize = sb.st_size;
|
||||
|
||||
/*
|
||||
* open devrandom first: if this fails, we don't want to touch the
|
||||
* target file.
|
||||
*/
|
||||
devrandom = fopen(DEV_RANDOM, "r");
|
||||
if (NULL == devrandom || -1 == ferror(devrandom)) {
|
||||
warn("failed to open PRNG %s!", DEV_RANDOM);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* for security purposes, we want to make sure to actually overwrite
|
||||
* the file. r+ gives us read/write but more importantly, sets the
|
||||
* write stream at the beginning of the file. a side note is that when
|
||||
* overwriting a file, its size will never seem to change.
|
||||
*/
|
||||
target = fopen(filename, "r+");
|
||||
if (NULL == target || -1 == ferror(target)) {
|
||||
warn("failed to open %s", filename);
|
||||
fclose(devrandom);
|
||||
return retval;
|
||||
}
|
||||
|
||||
rewind(target);
|
||||
|
||||
/*
|
||||
* wait to calloc until we really need the data - makes cleaning up less
|
||||
* tricky.
|
||||
*/
|
||||
rdata = calloc(MAX_CHUNK, sizeof(char));
|
||||
if (NULL == rdata) {
|
||||
warn("could not allocate random data memory");
|
||||
fclose(devrandom);
|
||||
fclose(target);
|
||||
return retval;
|
||||
}
|
||||
|
||||
while (wiped < filesize) {
|
||||
chunk = filesize - wiped;
|
||||
chunk = chunk > MAX_CHUNK ? MAX_CHUNK : chunk;
|
||||
|
||||
rdsz = fread( rdata, sizeof(char), chunk, devrandom );
|
||||
wrsz = fwrite( rdata, sizeof(char), chunk, target );
|
||||
|
||||
if (-1 == stat(filename, &sb)) {
|
||||
warn(" stat on %s failed", filename);
|
||||
break;
|
||||
}
|
||||
if ((rdsz != wrsz) || (filesize != (unsigned int)sb.st_size)) {
|
||||
warn("invalid read/write size");
|
||||
break;
|
||||
}
|
||||
|
||||
wiped += chunk;
|
||||
}
|
||||
|
||||
if ((0 != fclose(devrandom)) || (0 != fclose(target)))
|
||||
warn("%s", filename);
|
||||
else
|
||||
retval = EX_OK;
|
||||
|
||||
free(rdata);
|
||||
rdata = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* remove a directory, all files in it, and all subdirectories.
|
||||
*/
|
||||
int
|
||||
rmdirs(const char *path, size_t passes)
|
||||
{
|
||||
char child[FILENAME_MAX + 1];
|
||||
struct dirent *dp;
|
||||
DIR *dirp;
|
||||
int fail = 0;
|
||||
|
||||
if (NULL == (dirp = opendir(path)))
|
||||
return EX_IOERR;
|
||||
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, passes);
|
||||
if (EX_IOERR == fail)
|
||||
break;
|
||||
} else {
|
||||
fail = do_wipe(child, passes, 1);
|
||||
if (EX_IOERR == fail)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (-1 == closedir(dirp))
|
||||
return EX_IOERR;
|
||||
if (EX_IOERR == fail)
|
||||
return EX_IOERR;
|
||||
|
||||
if (-1 == rmdir(path))
|
||||
return EX_IOERR;
|
||||
else
|
||||
return EX_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* print a quick usage message
|
||||
*/
|
||||
void
|
||||
usage()
|
||||
{
|
||||
version();
|
||||
printf("usage: %s [-v] [-n number] files\n", __progname);
|
||||
printf(" -n passes specify number of passes\n");
|
||||
printf(" (default is %d passes)\n", DEFAULT_PASSES);
|
||||
printf(" -r recursive delete\n");
|
||||
printf(" -v display list of failures and wiped files"
|
||||
" after wiping\n"
|
||||
" (verbose mode).\n");
|
||||
printf(" -V print version information.");
|
||||
|
||||
printf("\n");
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* print program version information
|
||||
*/
|
||||
void
|
||||
version()
|
||||
{
|
||||
printf("%s\n", SRM_VERSION);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user