Mercurial > hg > toybox
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 } |