diff options
-rw-r--r-- | lib/dirtree.c | 36 | ||||
-rw-r--r-- | toys/ls.c | 57 |
2 files changed, 60 insertions, 33 deletions
diff --git a/lib/dirtree.c b/lib/dirtree.c index fb74f8d8..0e2a385c 100644 --- a/lib/dirtree.c +++ b/lib/dirtree.c @@ -7,6 +7,8 @@ #include "toys.h" // Create a dirtree node from a path, with stat and symlink info. +// (This doesn't open directory filehandles yet so as not to exhaust the +// filehandle space on large trees. handle_callback() does that instead.) struct dirtree *dirtree_add_node(int dirfd, char *name) { @@ -42,20 +44,24 @@ error: return 0; } -// Return path to this node. +// Return path to this node, assembled recursively. char *dirtree_path(struct dirtree *node, int *plen) { char *path; int len; - if (!node || !node->name) return xmalloc(*plen); + if (!node || !node->name) { + path = xmalloc(*plen); + *plen = 0; + return path; + } - len = (plen ? *plen : 0) + strlen(node->name)+1; + 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 (len) path[len++]='/'; + len = (stpcpy(path+len, node->name) - path); + if (plen) *plen = len; return path; } @@ -90,7 +96,8 @@ struct dirtree *handle_callback(struct dirtree *new, flags = callback(new); if (S_ISDIR(new->st.st_mode)) { if (!(flags & DIRTREE_NORECURSE)) { - new->data = openat(new->data, new->name, 0); + new->data = openat (new->parent ? new->parent->data : AT_FDCWD, + new->name, 0); dirtree_recurse(new, callback); } new->data = -1; @@ -114,20 +121,22 @@ void dirtree_recurse(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); + + return; } - // Dunno if I really need to do this, but the fdopendir man page insists - dirfd = xdup(node->data); + + // according to the fddir() man page, the filehandle in the DIR * can still + // be externally used by things that don't lseek() it. // The extra parentheses are to shut the stupid compiler up. while ((entry = readdir(dir))) { - if (!(new = dirtree_add_node(dirfd, entry->d_name))) continue; + if (!(new = dirtree_add_node(node->data, entry->d_name))) continue; new->parent = node; new = handle_callback(new, callback); if (new == DIRTREE_ABORTVAL) break; @@ -138,7 +147,6 @@ void dirtree_recurse(struct dirtree *node, } closedir(dir); - close(dirfd); } // Create dirtree from path, using callback to filter nodes. @@ -147,9 +155,7 @@ void dirtree_recurse(struct dirtree *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; + struct dirtree *root = dirtree_add_node(AT_FDCWD, path); return handle_callback(root, callback); } @@ -130,17 +130,20 @@ static int compare(void *a, void *b) return strcmp(dta->name, dtb->name); } +// callback from dirtree_recurse() determining how to handle this entry. + static int filter(struct dirtree *new) { - int ret = DIRTREE_NORECURSE; + int flags = toys.optflags; -// TODO -1f should print here to handle enormous dirs without runing out of mem. + // TODO should -1f print here to handle enormous dirs without runing + // out of mem? - if (!(toys.optflags & (FLAG_a|FLAG_A)) && new->name[0]=='.') - ret |= DIRTREE_NOSAVE; - else if (!(toys.optflags & FLAG_a)) ret |= dirtree_isdotdot(new); + if (flags & FLAG_a) return DIRTREE_NORECURSE; + if (!(flags & FLAG_A) && new->name[0]=='.') + return DIRTREE_NOSAVE|DIRTREE_NORECURSE; - return ret; + return dirtree_isdotdot(new)|DIRTREE_NORECURSE; } // Display a list of dirtree entries, according to current format @@ -155,15 +158,14 @@ static void listfiles(struct dirtree *indir) // Figure out if we should show directories and current directory name if (indir == TT.files) showdirs = (flags & (FLAG_d|FLAG_R)); - else if (indir->parent == TT.files && toys.optc <= 1 && !(flags&FLAG_R)); - else { + if (indir != TT.files || (indir->parent && (flags & FLAG_R))) { char *path = dirtree_path(indir, 0); + if (TT.again++) xputc('\n'); xprintf("%s:\n", path); free(path); } - // Copy linked list to array and sort it. Directories go in array because // we visit them in sorted order. @@ -178,7 +180,8 @@ static void listfiles(struct dirtree *indir) continue; } - if (flags & FLAG_l) xprintf("total %lu\n", dtlen); + // This is wrong, should be blocks used not file count. + if (indir->parent && (flags & FLAG_l)) xprintf("total %lu\n", dtlen); if (!(flags & FLAG_f)) qsort(sort, dtlen, sizeof(void *), (void *)compare); @@ -187,7 +190,6 @@ static void listfiles(struct dirtree *indir) memset(totals, 0, 6*sizeof(unsigned)); if ((flags & (FLAG_1|FLAG_l)) != FLAG_1) { for (ul = 0; ul<dtlen; ul++) { - if (!showdirs && S_ISDIR(sort[ul]->st.st_mode)) continue; entrylen(sort[ul], len); if (flags & FLAG_l) { for (width=0; width<6; width++) @@ -271,20 +273,22 @@ static void listfiles(struct dirtree *indir) if (!S_ISDIR(sort[ul]->st.st_mode) || dirtree_isdotdot(sort[ul])) continue; if (indir == TT.files || (flags & FLAG_R)) { - sort[ul]->data = openat(indir->data, sort[ul]->name, 0); + int fd = openat(indir->data, sort[ul]->name, 0); + + sort[ul]->data = dup(fd); dirtree_recurse(sort[ul], filter); + sort[ul]->data = fd; listfiles(sort[ul]); } } free(sort); - close(indir->data); - - + if (indir->data != AT_FDCWD) close(indir->data); } void ls_main(void) { char **s, *noargs[] = {".", 0}; + struct dirtree *dt; // Do we have an implied -1 if (!isatty(1) || (toys.optflags&FLAG_l)) toys.optflags |= FLAG_1; @@ -296,9 +300,8 @@ void ls_main(void) // Iterate through command line arguments, collecting directories and files. // Non-absolute paths are relative to current directory. TT.files = dirtree_add_node(0, 0); - TT.files->data =open(".", 0); - for (s = toys.optargs ? toys.optargs : noargs; *s; s++) { - struct dirtree *dt = dirtree_add_node(TT.files->data, *s); + for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) { + dt = dirtree_add_node(AT_FDCWD, *s); if (!dt) { toys.exitval = 1; @@ -310,9 +313,27 @@ void ls_main(void) (struct double_list *)dt); } + if (!TT.files->child) return; + // Turn double_list into dirtree dlist_to_dirtree(TT.files); + // Special case a single directory argument: silently descend into it. + dt = TT.files->child; + + if (S_ISDIR(dt->st.st_mode) && !dt->next && !(toys.optflags&FLAG_d)) { + int fd = open(dt->name, 0); + TT.files = dt; + dt->data = dup(fd); + dirtree_recurse(dt, filter); + dt->data = fd; + } else TT.files->data = AT_FDCWD; + // Display the files we collected listfiles(TT.files); + + if (CFG_TOYBOX_FREE) { + free(TT.files->parent); + free(TT.files); + } } |