changeset 569:2e0367cb9585

More work on ls. Now ls -lR sort of works-ish.
author Rob Landley <rob@landley.net>
date Sun, 22 Apr 2012 23:01:23 -0500
parents d5fb52b428ed
children 83702597fd31
files lib/dirtree.c toys/ls.c
diffstat 2 files changed, 60 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/lib/dirtree.c	Sat Apr 14 19:55:13 2012 +0200
+++ b/lib/dirtree.c	Sun Apr 22 23:01:23 2012 -0500
@@ -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 @@
 	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 @@
 	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 @@
 	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 @@
 	}
 
 	closedir(dir);
-	close(dirfd);
 }
 
 // Create dirtree from path, using callback to filter nodes.
@@ -147,9 +155,7 @@
 
 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);
 }
--- a/toys/ls.c	Sat Apr 14 19:55:13 2012 +0200
+++ b/toys/ls.c	Sun Apr 22 23:01:23 2012 -0500
@@ -130,17 +130,20 @@
     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 @@
 
     // 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 @@
         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 @@
     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 @@
         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 @@
     // 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 @@
                            (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);
+    }
 }