aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2012-04-14 22:30:41 -0500
committerRob Landley <rob@landley.net>2012-04-14 22:30:41 -0500
commiteb7ea22c7505f10928e104a9df39edc70a8f7036 (patch)
treebc928711030e2378298c8e5012c7c76b9b606661 /lib
parent43e9d331c8055dff7e243bd19d2d06df826d6f38 (diff)
downloadtoybox-eb7ea22c7505f10928e104a9df39edc70a8f7036.tar.gz
Rewrite dirtree so we don't need readdir, scandir, and fts.h. Rewrite ls (from scratch) to use new dirtree infrastructure. (This breaks everything else that currently uses dirtree.)
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,