comparison lib/dirtree.c @ 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 44abf4d901f3
children 4058eacd4fbc
comparison
equal deleted inserted replaced
568:d5fb52b428ed 569:2e0367cb9585
5 */ 5 */
6 6
7 #include "toys.h" 7 #include "toys.h"
8 8
9 // Create a dirtree node from a path, with stat and symlink info. 9 // Create a dirtree node from a path, with stat and symlink info.
10 // (This doesn't open directory filehandles yet so as not to exhaust the
11 // filehandle space on large trees. handle_callback() does that instead.)
10 12
11 struct dirtree *dirtree_add_node(int dirfd, char *name) 13 struct dirtree *dirtree_add_node(int dirfd, char *name)
12 { 14 {
13 struct dirtree *dt = NULL; 15 struct dirtree *dt = NULL;
14 struct stat st; 16 struct stat st;
40 perror_msg("%s",name); 42 perror_msg("%s",name);
41 free(dt); 43 free(dt);
42 return 0; 44 return 0;
43 } 45 }
44 46
45 // Return path to this node. 47 // Return path to this node, assembled recursively.
46 48
47 char *dirtree_path(struct dirtree *node, int *plen) 49 char *dirtree_path(struct dirtree *node, int *plen)
48 { 50 {
49 char *path; 51 char *path;
50 int len; 52 int len;
51 53
52 if (!node || !node->name) return xmalloc(*plen); 54 if (!node || !node->name) {
55 path = xmalloc(*plen);
56 *plen = 0;
57 return path;
58 }
53 59
54 len = (plen ? *plen : 0) + strlen(node->name)+1; 60 len = (plen ? *plen : 0)+strlen(node->name)+1;
55 path = dirtree_path(node->parent, &len); 61 path = dirtree_path(node->parent, &len);
56 len = plen ? *plen : 0; 62 if (len) path[len++]='/';
57 if (len) path[len++]='/'; 63 len = (stpcpy(path+len, node->name) - path);
58 strcpy(path+len, node->name); 64 if (plen) *plen = len;
59 65
60 return path; 66 return path;
61 } 67 }
62 68
63 // Default callback, filters out "." and "..". 69 // Default callback, filters out "." and "..".
88 if (!callback) callback = dirtree_isdotdot; 94 if (!callback) callback = dirtree_isdotdot;
89 95
90 flags = callback(new); 96 flags = callback(new);
91 if (S_ISDIR(new->st.st_mode)) { 97 if (S_ISDIR(new->st.st_mode)) {
92 if (!(flags & DIRTREE_NORECURSE)) { 98 if (!(flags & DIRTREE_NORECURSE)) {
93 new->data = openat(new->data, new->name, 0); 99 new->data = openat (new->parent ? new->parent->data : AT_FDCWD,
100 new->name, 0);
94 dirtree_recurse(new, callback); 101 dirtree_recurse(new, callback);
95 } 102 }
96 new->data = -1; 103 new->data = -1;
97 if (flags & DIRTREE_COMEAGAIN) flags = callback(new); 104 if (flags & DIRTREE_COMEAGAIN) flags = callback(new);
98 } 105 }
112 int (*callback)(struct dirtree *node)) 119 int (*callback)(struct dirtree *node))
113 { 120 {
114 struct dirtree *new, **ddt = &(node->child); 121 struct dirtree *new, **ddt = &(node->child);
115 struct dirent *entry; 122 struct dirent *entry;
116 DIR *dir; 123 DIR *dir;
117 int dirfd;
118 124
119 if (!(dir = fdopendir(node->data))) { 125 if (!(dir = fdopendir(node->data))) {
120 char *path = dirtree_path(node, 0); 126 char *path = dirtree_path(node, 0);
121 perror_msg("No %s", path); 127 perror_msg("No %s", path);
122 free(path); 128 free(path);
123 close(node->data); 129 close(node->data);
130
131 return;
124 } 132 }
125 // Dunno if I really need to do this, but the fdopendir man page insists 133
126 dirfd = xdup(node->data); 134 // according to the fddir() man page, the filehandle in the DIR * can still
135 // be externally used by things that don't lseek() it.
127 136
128 // The extra parentheses are to shut the stupid compiler up. 137 // The extra parentheses are to shut the stupid compiler up.
129 while ((entry = readdir(dir))) { 138 while ((entry = readdir(dir))) {
130 if (!(new = dirtree_add_node(dirfd, entry->d_name))) continue; 139 if (!(new = dirtree_add_node(node->data, entry->d_name))) continue;
131 new->parent = node; 140 new->parent = node;
132 new = handle_callback(new, callback); 141 new = handle_callback(new, callback);
133 if (new == DIRTREE_ABORTVAL) break; 142 if (new == DIRTREE_ABORTVAL) break;
134 if (new) { 143 if (new) {
135 *ddt = new; 144 *ddt = new;
136 ddt = &((*ddt)->next); 145 ddt = &((*ddt)->next);
137 } 146 }
138 } 147 }
139 148
140 closedir(dir); 149 closedir(dir);
141 close(dirfd);
142 } 150 }
143 151
144 // Create dirtree from path, using callback to filter nodes. 152 // Create dirtree from path, using callback to filter nodes.
145 // If callback == NULL allocate a tree of struct dirtree nodes and return 153 // If callback == NULL allocate a tree of struct dirtree nodes and return
146 // pointer to root node. 154 // pointer to root node.
147 155
148 struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node)) 156 struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node))
149 { 157 {
150 int fd = open(".", 0); 158 struct dirtree *root = dirtree_add_node(AT_FDCWD, path);
151 struct dirtree *root = dirtree_add_node(fd, path);
152 root->data = fd;
153 159
154 return handle_callback(root, callback); 160 return handle_callback(root, callback);
155 } 161 }