From 1561e7567e078b5bd2c6a660b26719d8f65fa769 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Thu, 9 Jul 2015 00:17:56 +0200 Subject: [PATCH] Add recursive mkpath() and makepath(), works like mkdir -p Signed-off-by: Joachim Nilsson --- Makefile | 2 +- README.md | 8 ++++ lite.h | 3 ++ makepath.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 makepath.c diff --git a/Makefile b/Makefile index 147409f..a1cb13d 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ endif DISTFILES = README LICENSE HEADERS = lite.h OBJS := chomp.o copyfile.o dir.o fexist.o fisdir.o fmode.o fsendfile.o -OBJS += rsync.o strlcpy.o strlcat.o strtonum.o tempfile.o +OBJS += makepath.o rsync.o strlcpy.o strlcat.o strtonum.o tempfile.o DEPS := $(OBJS:.o=.d) JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.* diff --git a/README.md b/README.md index bed2322..3722587 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,14 @@ Generic Functions `dst` is allowed to be `NULL` to be able to read and discard `len` bytes from `src`. +- `makepath(dir)` + + Create all components of the specified directory. + +- `mkpath(dir, mode)` + + Like `makepath()`, but also takes a `mode_t` permission mode argument. + - `movefile(src, dst)` Like `copyfile()`, but renames `src` to `dst`, or recreates symlink diff --git a/lite.h b/lite.h index dc648ab..d49f74f 100644 --- a/lite.h +++ b/lite.h @@ -45,6 +45,9 @@ int movefile (char *src, char *dst); int fcopyfile (FILE *src, FILE *dst); size_t fsendfile (FILE *src, FILE *dst, size_t len); +int mkpath (char *dir, mode_t mode); +int makepath (char *dir); + int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip); int rsync (char *src, char *dst, int delete, int (*filter) (const char *file)); diff --git a/makepath.c b/makepath.c new file mode 100644 index 0000000..7cbd6cc --- /dev/null +++ b/makepath.c @@ -0,0 +1,120 @@ +/* mkpath() -- Create all components leading up to a given directory + * + * Copyright (c) 2013 Joachim Nilsson + * + * Permission to use, copy, modify, and/or 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 +#include +#include +#include +#include +#include +#include + +int mkpath(char *dir, mode_t mode) +{ + if (!dir) { + errno = EINVAL; + return 1; + } + + if (strlen(dir) == 1 && dir[0] == '/') + return 0; + + mkpath(dirname(strdupa(dir)), mode); + + return mkdir(dir, mode); +} + +/** + * makepath - Create all components of the specified directory. + * @dir: Directory to create. + * + * Returns: + * POSIX OK (0) on success, otherwise -1 and errno set appropriately. + * This function returns EINVAL on bad argument, or ENOMEM when it + * fails allocating temporary memory. For other error codes see the + * mkdir() syscall description. + */ +int makepath(char *dir) +{ + return mkpath(dir, 0777); +} + +/********************************* UNIT TESTS ************************************/ +#ifdef UNITTEST +#include + +int checkpath(char *dir) +{ + char tmp[256]; + struct stat sb; + + snprintf(tmp, sizeof(tmp), "ls -ld %s", dir); + system(tmp); + + if (!stat(dir, &sb) && S_ISDIR(sb.st_mode)) + return 0; + + errno = ENOTDIR; + return 1; +} + +int test_makepath(char *dir) +{ + int ret = makepath(dir); + + if (!ret) + ret = checkpath(dir); + if (ret) + perror("Failed"); + + return ret; +} + +int main(void) +{ + int i, ret = 0; + char *list[] = { + "/tmp/tok/", + "/tmp/tok2", + "/tmp/ab", + "/tmp/b", + "/tmp/a/", + "/tmp/a/b", + "/tmp/a/c/", + NULL + }; + + fprintf(stderr, "Testing makepath() ...\n"); + for (i = 0; list[i] && !ret; i++) + ret = test_makepath(list[i]); + + fprintf(stderr, "\nCleaning up ...\n"); + for (i = 0; list[i]; i++) + rmdir(list[i]); + + return ret; +} +#endif /* UNITTEST */ + +/** + * Local Variables: + * compile-command: "gcc -g -o unittest -D_GNU_SOURCE -DUNITTEST makepath.c && ./unittest" + * version-control: t + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */