diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dirtree.c | 185 | ||||
-rw-r--r-- | lib/lib.c | 17 | ||||
-rw-r--r-- | lib/lib.h | 42 |
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); +} @@ -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) { @@ -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, |