aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/dirtree.c36
-rw-r--r--toys/ls.c57
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);
}
diff --git a/toys/ls.c b/toys/ls.c
index fd316b64..65cad79e 100644
--- a/toys/ls.c
+++ b/toys/ls.c
@@ -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);
+ }
}