diff --git a/Makefile b/Makefile index 194d9e5..ad965bf 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ DISTFILES = README LICENSE HEADERS = lite.h OBJS := chomp.o copyfile.o dir.o fexist.o fisdir.o fmode.o fsendfile.o OBJS += ifconfig.o makepath.o pidfile.o pidfilefn.o rsync.o -OBJS += strlcpy.o strlcat.o strtonum.o tempfile.o +OBJS += strlcpy.o strlcat.o strtonum.o tempfile.o tree.o DEPS := $(OBJS:.o=.d) JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.* @@ -61,6 +61,7 @@ test: clean $(MAKE) -f pidfilefn.mk $(MAKE) -f rsync.mk $(MAKE) -f tempfile.mk + $(MAKE) -f tree.mk $(OBJS): Makefile diff --git a/README.md b/README.md index 5cea76b..1dddcfa 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,14 @@ Generic Functions Secure replacement for `tmpfile()`. Creates an invisible temporary file in `/tmp` that is removed when the returned `FILE` pointer is - closed. **Note:** Requires Linux v3.11, or later. + closed. + + **Note:** Requires Linux v3.11, or later, will fall back to the old + and unsafe `tmpfile()` on older systems. + +- `tree()` + + Very simple `/bin/tree` replacement. OpenBSD Functions diff --git a/lite.h b/lite.h index 6cdd4fc..c7225f2 100644 --- a/lite.h +++ b/lite.h @@ -69,6 +69,8 @@ size_t strlcat (char *dst, const char *src, size_t siz); long long strtonum (const char *numstr, long long minval, long long maxval, const char **errstrp); #endif +int tree(char *path, int show_perms); + #ifndef touch # define touch(x) do { if (mknod((x), S_IFREG|0644, 0) && errno != EEXIST) warn("Failed creating %s", x); } while (0) #endif diff --git a/tree.c b/tree.c new file mode 100644 index 0000000..8bb6332 --- /dev/null +++ b/tree.c @@ -0,0 +1,154 @@ +/* Simple /bin/tree replacement + * + * Copyright (c) 2015 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 +#include + +static int all = 0; + +static int filter(const struct dirent *entry) +{ + /* Skip current dir ".", and prev dir "..", from list of files */ + if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') || + (2 == strlen(entry->d_name) && !strcmp(entry->d_name, ".."))) + return 0; + + if (!all && entry->d_name[0] == '.') + return 0; + + return 1; +} + +static void get_perms(struct stat *st, char *buf, size_t len) +{ + mode_t m = st->st_mode; + +// printf("%#o\n", m); + snprintf(buf, len, "[%c%c%c%c%c%c%c%c%c%c] ", + S_ISCHR(m) ? 'c' : S_ISBLK(m) ? 'b' : S_ISFIFO(m) ? 'p' : S_ISLNK(m) ? 'l' : S_ISSOCK(m) ? 's' : '-', + (m & S_IRUSR) ? 'r' : '-', + (m & S_IWUSR) ? 'w' : '-', + (m & S_ISUID ) ? 's' : (m & S_IXUSR) ? 'x' : '-', + (m & S_IRGRP) ? 'r' : '-', + (m & S_IWGRP) ? 'w' : '-', + (m & S_ISGID) ? 's' : (m & S_IXGRP) ? 'x' : '-', + (m & S_IROTH) ? 'r' : '-', + (m & S_IWOTH) ? 'w' : '-', + (m & S_IXOTH) ? 'x' : '-' + ); +} + +static int descend(char *path, int perms, char *pfx) +{ + int result = 0; + struct stat st; + + if (-1 == lstat(path, &st)) + return 1; + + if ((st.st_mode & S_IFMT) == S_IFDIR) { + int i, n; + struct dirent **namelist = NULL; + + n = scandir(path, &namelist, filter, alphasort); + if (n) { + for (i = 0; i < n; i++) { + char t = ' ', p[14] = "", s[256] = ""; + char buf[256]; + char dir[80]; + + if (i + 1 == n) { + printf("%s `- ", pfx); + snprintf(dir, sizeof(dir), "%s ", pfx); + } else { + printf("%s|-- ", pfx); + snprintf(dir, sizeof(dir), "%s| ", pfx); + } + + snprintf(buf, sizeof(buf), "%s/%s", path, namelist[i]->d_name); + if (!lstat(buf, &st)) { + if (perms) { +// printf("buf: %s --> ", buf); + get_perms(&st, p, sizeof(p)); + } + if ((st.st_mode & S_IFMT) == S_IFDIR) + t = '/'; + if (S_ISLNK(st.st_mode)) { + snprintf(s, sizeof(s), "-> "); + if (-1 == readlink(buf, &s[3], sizeof(s) - 3)) + s[0] = 0; + } + } + + printf("%s%s%c%s\n", p, namelist[i]->d_name, t, s); + if (t == '/') + result += descend(buf, perms, dir); + + free(namelist[i]); + } + + free(namelist); + } + } else { + errno = ENOTDIR; + result = -1; + } + + return result; +} + +int tree(char *path, int show_perms) +{ + printf("[%s]\n", path); + return descend(path, show_perms, ""); +} + +#ifdef UNITTEST +#include "lite.h" + +int main(void) +{ + int i, result = 0; + struct { char *path; int fail; } arr[] = { + { "/etc/passwd", -1 }, + { "/dev", 0 }, + { "/sbin", 0 }, + }; + + for (i = 0; i < NELEMS(arr); i++) { + if (tree(arr[i].path, 1) != arr[i].fail) + result++; + } + + return result; +} +#endif + +/** + * Local Variables: + * compile-command: "make V=1 -f tree.mk" + * version-control: t + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/tree.mk b/tree.mk new file mode 100644 index 0000000..2637b03 --- /dev/null +++ b/tree.mk @@ -0,0 +1,5 @@ +OBJS = +ASROOT = 1 +TARGET = clean $(OBJS) tree.test + +include rules.mk