aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/dirtree.c185
-rw-r--r--lib/lib.c17
-rw-r--r--lib/lib.h42
3 files changed, 177 insertions, 67 deletions
diff --git a/lib/dirtree.c b/lib/dirtree.c
index 1993d007..fb74f8d8 100644
--- a/lib/dirtree.c
+++ b/lib/dirtree.c
@@ -6,85 +6,150 @@
#include "toys.h"
-// NOTE: This uses toybuf. Possibly it shouldn't do that.
+// Create a dirtree node from a path, with stat and symlink info.
-// Create a dirtree node from a path.
+struct dirtree *dirtree_add_node(int dirfd, char *name)
+{
+ struct dirtree *dt = NULL;
+ struct stat st;
+ char buf[4096];
+ int len = 0, linklen = 0;
+
+ if (name) {
+ if (fstatat(dirfd, name, &st, AT_SYMLINK_NOFOLLOW)) goto error;
+ if (S_ISLNK(st.st_mode)) {
+ if (0>(linklen = readlinkat(dirfd, name, buf, 4095))) goto error;
+ buf[linklen++]=0;
+ }
+ len = strlen(name);
+ }
+ dt = xzalloc((len = sizeof(struct dirtree)+len+1)+linklen);
+ if (name) {
+ memcpy(&(dt->st), &st, sizeof(struct stat));
+ strcpy(dt->name, name);
+
+ if (linklen) {
+ dt->symlink = memcpy(len+(char *)dt, buf, linklen);
+ dt->data = --linklen;
+ }
+ }
+
+ return dt;
+
+error:
+ perror_msg("%s",name);
+ free(dt);
+ return 0;
+}
-struct dirtree *dirtree_add_node(char *path)
+// Return path to this node.
+
+char *dirtree_path(struct dirtree *node, int *plen)
{
- struct dirtree *dt;
- char *name;
+ char *path;
+ int len;
- // Find last chunk of name.
+ if (!node || !node->name) return xmalloc(*plen);
- for (;;) {
- name = strrchr(path, '/');
+ len = (plen ? *plen : 0) + strlen(node->name)+1;
+ path = dirtree_path(node->parent, &len);
+ len = plen ? *plen : 0;
+ if (len) path[len++]='/';
+ strcpy(path+len, node->name);
- if (!name) name = path;
- else {
- if (*(name+1)) name++;
- else {
- *name=0;
- continue;
- }
- }
- break;
- }
+ return path;
+}
- dt = xzalloc(sizeof(struct dirtree)+strlen(name)+1);
- if (lstat(path, &(dt->st))) {
- error_msg("Skipped '%s'",name);
- free(dt);
- return 0;
- }
- strcpy(dt->name, name);
+// Default callback, filters out "." and "..".
- return dt;
+int dirtree_isdotdot(struct dirtree *catch)
+{
+ // Should we skip "." and ".."?
+ if (catch->name[0]=='.' && (!catch->name[1] ||
+ (catch->name[1]=='.' && !catch->name[2])))
+ return DIRTREE_NOSAVE|DIRTREE_NORECURSE;
+
+ return 0;
}
-// Given a directory (in a writeable PATH_MAX buffer), recursively read in a
-// directory tree.
+// Handle callback for a node in the tree. Returns saved node(s) or NULL.
//
-// If callback==NULL, allocate tree of struct dirtree and
-// return root of tree. Otherwise call callback(node) on each hit, free
+// By default, allocates a tree of struct dirtree, not following symlinks
+// If callback==NULL, or callback always returns 0, allocate tree of struct
+// dirtree and return root of tree. Otherwise call callback(node) on each hit, free
// structures after use, and return NULL.
+//
-struct dirtree *dirtree_read(char *path, struct dirtree *parent,
- int (*callback)(char *path, struct dirtree *node))
+struct dirtree *handle_callback(struct dirtree *new,
+ int (*callback)(struct dirtree *node))
{
- struct dirtree *dtroot = NULL, *this, **ddt = &dtroot;
- DIR *dir;
- int len = strlen(path);
-
- if (!(dir = opendir(path))) perror_msg("No %s", path);
- else for (;;) {
- int norecurse = 0;
- struct dirent *entry = readdir(dir);
- if (!entry) {
- closedir(dir);
- break;
- }
+ int flags;
- // Skip "." and ".."
- if (entry->d_name[0]=='.') {
- if (!entry->d_name[1]) continue;
- if (entry->d_name[1]=='.' && !entry->d_name[2]) continue;
+ if (!callback) callback = dirtree_isdotdot;
+
+ flags = callback(new);
+ if (S_ISDIR(new->st.st_mode)) {
+ if (!(flags & DIRTREE_NORECURSE)) {
+ new->data = openat(new->data, new->name, 0);
+ dirtree_recurse(new, callback);
}
+ new->data = -1;
+ if (flags & DIRTREE_COMEAGAIN) flags = callback(new);
+ }
+ // If this had children, it was callback's job to free them already.
+ if (flags & DIRTREE_NOSAVE) {
+ free(new);
+ new = NULL;
+ }
+
+ return (flags & DIRTREE_ABORT)==DIRTREE_ABORT ? DIRTREE_ABORTVAL : new;
+}
+
+// Recursively read/process children of directory node (with dirfd in data),
+// filtering through callback().
- snprintf(path+len, sizeof(toybuf)-len, "/%s", entry->d_name);
- *ddt = this = dirtree_add_node(path);
- if (!this) continue;
- this->parent = parent;
- this->depth = parent ? parent->depth + 1 : 1;
- if (callback) norecurse = callback(path, this);
- if (!norecurse && S_ISDIR(this->st.st_mode))
- this->child = dirtree_read(path, this, callback);
- if (callback) free(this);
- else ddt = &(this->next);
- path[len]=0;
+void dirtree_recurse(struct dirtree *node,
+ int (*callback)(struct dirtree *node))
+{
+ struct dirtree *new, **ddt = &(node->child);
+ struct dirent *entry;
+ DIR *dir;
+ int dirfd;
+
+ if (!(dir = fdopendir(node->data))) {
+ char *path = dirtree_path(node, 0);
+ perror_msg("No %s", path);
+ free(path);
+ close(node->data);
+ }
+ // Dunno if I really need to do this, but the fdopendir man page insists
+ dirfd = xdup(node->data);
+
+ // The extra parentheses are to shut the stupid compiler up.
+ while ((entry = readdir(dir))) {
+ if (!(new = dirtree_add_node(dirfd, entry->d_name))) continue;
+ new->parent = node;
+ new = handle_callback(new, callback);
+ if (new == DIRTREE_ABORTVAL) break;
+ if (new) {
+ *ddt = new;
+ ddt = &((*ddt)->next);
+ }
}
- return dtroot;
+ closedir(dir);
+ close(dirfd);
}
+// Create dirtree from path, using callback to filter nodes.
+// If callback == NULL allocate a tree of struct dirtree nodes and return
+// pointer to root node.
+
+struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node))
+{
+ int fd = open(".", 0);
+ struct dirtree *root = dirtree_add_node(fd, path);
+ root->data = fd;
+ return handle_callback(root, callback);
+}
diff --git a/lib/lib.c b/lib/lib.c
index 4190b065..a7eb65a6 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -208,6 +208,13 @@ void xclose(int fd)
if (close(fd)) perror_exit("xclose");
}
+int xdup(int fd)
+{
+ fd = dup(fd);
+ if (fd == -1) perror_exit("xdup");
+ return fd;
+}
+
// Die unless we can open/create a file, returning FILE *.
FILE *xfopen(char *path, char *mode)
{
@@ -497,6 +504,16 @@ long atolx(char *numstr)
return val;
}
+int numlen(long l)
+{
+ int len = 0;
+ while (l) {
+ l /= 10;
+ len++;
+ }
+ return len;
+}
+
// Return how long the file at fd is, if there's any way to determine it.
off_t fdlength(int fd)
{
diff --git a/lib/lib.h b/lib/lib.h
index 2f33236a..812908af 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -17,6 +17,9 @@ ssize_t getline(char **lineptr, size_t *n, FILE *stream);
// llist.c
+// All these list types can be handled by the same code because first element
+// is always next pointer, so next = (mytype *)&struct.
+
struct string_list {
struct string_list *next;
char str[0];
@@ -28,8 +31,7 @@ struct arg_list {
};
struct double_list {
- struct double_list *next;
- struct double_list *prev;
+ struct double_list *next, *prev;
char *data;
};
@@ -42,16 +44,40 @@ struct double_list *dlist_add(struct double_list **list, char *data);
void get_optflags(void);
// dirtree.c
+
+// Values returnable from callback function (bitfield, or them together)
+// Default with no callback is 0
+
+// Do not add this node to the tree
+#define DIRTREE_NOSAVE 1
+// Do not recurse into children
+#define DIRTREE_NORECURSE 2
+// Call again after handling all children (Directories only. Sets linklen = -1)
+#define DIRTREE_COMEAGAIN 4
+// Follow symlinks to directories
+#define DIRTREE_SYMFOLLOW 8
+// Abort recursive dirtree. (Forces NOSAVE and NORECURSE on this entry.)
+#define DIRTREE_ABORT (256|DIRTREE_NOSAVE|DIRTREE_NORECURSE)
+
+#define DIRTREE_ABORTVAL ((struct dirtree *)1)
+
struct dirtree {
- struct dirtree *next, *child, *parent;
+ struct dirtree *next, *parent, *child;
+ long extra; // place for user to store their stuff (can be pointer)
+ long data; // dirfd for directory, linklen for symlink
struct stat st;
- int depth;
+ char *symlink;
char name[];
};
-struct dirtree *dirtree_add_node(char *path);
-struct dirtree *dirtree_read(char *path, struct dirtree *parent,
- int (*callback)(char *path, struct dirtree *node));
+struct dirtree *dirtree_add_node(int dirfd, char *name);
+char *dirtree_path(struct dirtree *node, int *plen);
+int dirtree_isdotdot(struct dirtree *catch);
+struct dirtree *handle_callback(struct dirtree *new,
+ int (*callback)(struct dirtree *node));
+void dirtree_recurse(struct dirtree *node,
+ int (*callback)(struct dirtree *node));
+struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node));
// lib.c
void xstrcpy(char *dest, char *src, size_t size);
@@ -76,6 +102,7 @@ void xunlink(char *path);
int xcreate(char *path, int flags, int mode);
int xopen(char *path, int flags);
void xclose(int fd);
+int xdup(int fd);
FILE *xfopen(char *path, char *mode);
ssize_t readall(int fd, void *buf, size_t len);
ssize_t writeall(int fd, void *buf, size_t len);
@@ -97,6 +124,7 @@ void itoa_to_buf(int n, char *buf, unsigned buflen);
char *utoa(unsigned n);
char *itoa(int n);
long atolx(char *c);
+int numlen(long l);
off_t fdlength(int fd);
char *xreadlink(char *name);
void loopfiles_rw(char **argv, int flags, int permissions, int failok,