rev |
line source |
nuclear@1
|
1 #include <stdio.h>
|
nuclear@1
|
2 #include <string.h>
|
nuclear@1
|
3 #include <ctype.h>
|
nuclear@1
|
4 #include <errno.h>
|
nuclear@2
|
5 #include <algorithm>
|
nuclear@1
|
6 #include <alloca.h>
|
nuclear@1
|
7 #include <unistd.h>
|
nuclear@1
|
8 #include <dirent.h>
|
nuclear@2
|
9 #include <sys/stat.h>
|
nuclear@1
|
10 #include "fs.h"
|
nuclear@1
|
11
|
nuclear@2
|
12 static bool childcmpless(const FSNode *aptr, const FSNode *bptr);
|
nuclear@1
|
13 static char *clean_path(char *path);
|
nuclear@1
|
14 static char *filename(char *path);
|
nuclear@2
|
15 static FSNode::Type st_mode_to_type(mode_t mode);
|
nuclear@1
|
16
|
nuclear@1
|
17 FSNode::FSNode()
|
nuclear@1
|
18 {
|
nuclear@1
|
19 init();
|
nuclear@1
|
20 }
|
nuclear@1
|
21
|
nuclear@1
|
22 FSNode::~FSNode()
|
nuclear@1
|
23 {
|
nuclear@1
|
24 destroy();
|
nuclear@1
|
25 }
|
nuclear@1
|
26
|
nuclear@1
|
27 void FSNode::init()
|
nuclear@1
|
28 {
|
nuclear@1
|
29 path = name = 0;
|
nuclear@1
|
30 parent = 0;
|
nuclear@1
|
31 expanded = false;
|
nuclear@2
|
32 sorted = true;
|
nuclear@1
|
33 uid = gid = mode = 0;
|
nuclear@2
|
34 type = UNKNOWN;
|
nuclear@1
|
35 }
|
nuclear@1
|
36
|
nuclear@1
|
37 void FSNode::destroy()
|
nuclear@1
|
38 {
|
nuclear@1
|
39 delete [] path;
|
nuclear@1
|
40 children.clear();
|
nuclear@1
|
41 init();
|
nuclear@1
|
42 }
|
nuclear@1
|
43
|
nuclear@1
|
44 void FSNode::destroy_tree()
|
nuclear@1
|
45 {
|
nuclear@1
|
46 for(size_t i=0; i<children.size(); i++) {
|
nuclear@1
|
47 children[i]->destroy_tree();
|
nuclear@1
|
48 delete children[i];
|
nuclear@1
|
49 }
|
nuclear@1
|
50 destroy();
|
nuclear@1
|
51 }
|
nuclear@1
|
52
|
nuclear@2
|
53 void FSNode::set_type(Type type)
|
nuclear@2
|
54 {
|
nuclear@2
|
55 this->type = type;
|
nuclear@2
|
56 }
|
nuclear@2
|
57
|
nuclear@2
|
58 FSNode::Type FSNode::get_type() const
|
nuclear@2
|
59 {
|
nuclear@2
|
60 return type;
|
nuclear@2
|
61 }
|
nuclear@2
|
62
|
nuclear@2
|
63 bool FSNode::is_file() const
|
nuclear@2
|
64 {
|
nuclear@2
|
65 return type != DIRECTORY;
|
nuclear@2
|
66 }
|
nuclear@2
|
67
|
nuclear@2
|
68 bool FSNode::is_directory() const
|
nuclear@2
|
69 {
|
nuclear@2
|
70 return type == DIRECTORY;
|
nuclear@2
|
71 }
|
nuclear@2
|
72
|
nuclear@2
|
73 void FSNode::sort_children()
|
nuclear@2
|
74 {
|
nuclear@2
|
75 std::sort(children.begin(), children.end(), childcmpless);
|
nuclear@2
|
76 sorted = true;
|
nuclear@2
|
77 }
|
nuclear@2
|
78
|
nuclear@1
|
79 void FSNode::set_path(const char *path)
|
nuclear@1
|
80 {
|
nuclear@1
|
81 delete [] this->path;
|
nuclear@1
|
82
|
nuclear@1
|
83 char *buf = new char[strlen(path) + 1];
|
nuclear@1
|
84 strcpy(buf, path);
|
nuclear@1
|
85
|
nuclear@1
|
86 char *tmp = clean_path(buf);
|
nuclear@1
|
87 if(tmp == buf) {
|
nuclear@1
|
88 this->path = tmp;
|
nuclear@1
|
89 } else {
|
nuclear@1
|
90 this->path = new char[strlen(tmp) + 1];
|
nuclear@1
|
91 strcpy(this->path, tmp);
|
nuclear@1
|
92 delete [] buf;
|
nuclear@1
|
93 }
|
nuclear@1
|
94
|
nuclear@1
|
95 name = filename(this->path);
|
nuclear@1
|
96 }
|
nuclear@1
|
97
|
nuclear@1
|
98 void FSNode::set_name(const char *name)
|
nuclear@1
|
99 {
|
nuclear@1
|
100 delete [] path;
|
nuclear@1
|
101
|
nuclear@1
|
102 if(parent) {
|
nuclear@1
|
103 char *path = new char[strlen(name) + strlen(parent->path) + 2];
|
nuclear@1
|
104 sprintf(path, "%s/%s", parent->path, name);
|
nuclear@1
|
105 set_path(path);
|
nuclear@1
|
106 } else {
|
nuclear@1
|
107 path = this->name = new char[strlen(name) + 1];
|
nuclear@1
|
108 strcpy(path, name);
|
nuclear@1
|
109 }
|
nuclear@1
|
110 }
|
nuclear@1
|
111
|
nuclear@2
|
112 const char *FSNode::get_path() const
|
nuclear@1
|
113 {
|
nuclear@2
|
114 return path;
|
nuclear@2
|
115 }
|
nuclear@1
|
116
|
nuclear@2
|
117 const char *FSNode::get_name() const
|
nuclear@2
|
118 {
|
nuclear@2
|
119 return name;
|
nuclear@2
|
120 }
|
nuclear@2
|
121
|
nuclear@2
|
122 bool FSNode::add_child(FSNode *node)
|
nuclear@2
|
123 {
|
nuclear@2
|
124 if(node->parent == this) {
|
nuclear@2
|
125 return true;
|
nuclear@2
|
126 }
|
nuclear@2
|
127
|
nuclear@2
|
128 if(node->parent) {
|
nuclear@2
|
129 node->parent->remove_child(node);
|
nuclear@2
|
130 }
|
nuclear@2
|
131 node->parent = this;
|
nuclear@2
|
132
|
nuclear@2
|
133 try {
|
nuclear@2
|
134 children.push_back(node);
|
nuclear@2
|
135 }
|
nuclear@2
|
136 catch(...) {
|
nuclear@2
|
137 return false;
|
nuclear@2
|
138 }
|
nuclear@2
|
139
|
nuclear@2
|
140 sorted = false;
|
nuclear@2
|
141 return true;
|
nuclear@2
|
142 }
|
nuclear@2
|
143
|
nuclear@2
|
144 bool FSNode::remove_child(FSNode *node)
|
nuclear@2
|
145 {
|
nuclear@2
|
146 int cidx = find_child(node);
|
nuclear@2
|
147 if(cidx == -1) {
|
nuclear@2
|
148 return false;
|
nuclear@2
|
149 }
|
nuclear@2
|
150 children.erase(children.begin() + cidx);
|
nuclear@2
|
151
|
nuclear@2
|
152 if(node->parent != this) {
|
nuclear@2
|
153 fprintf(stderr, "FSNode::remove_child(): target node doesn't have this node as parent\n");
|
nuclear@2
|
154 // let's not touch it if this happens...
|
nuclear@2
|
155 } else {
|
nuclear@2
|
156 node->parent = 0;
|
nuclear@2
|
157 }
|
nuclear@2
|
158 return true;
|
nuclear@2
|
159 }
|
nuclear@2
|
160
|
nuclear@2
|
161 int FSNode::find_child(FSNode *node) const
|
nuclear@2
|
162 {
|
nuclear@2
|
163 for(size_t i=0; i<children.size(); i++) {
|
nuclear@2
|
164 if(children[i] == node) {
|
nuclear@2
|
165 return i;
|
nuclear@2
|
166 }
|
nuclear@2
|
167 }
|
nuclear@2
|
168 return -1;
|
nuclear@2
|
169 }
|
nuclear@2
|
170
|
nuclear@2
|
171 int FSNode::find_child(const char *name) const
|
nuclear@2
|
172 {
|
nuclear@2
|
173 FSNode key;
|
nuclear@2
|
174 key.name = (char*)name;
|
nuclear@2
|
175
|
nuclear@2
|
176 if(!sorted) {
|
nuclear@2
|
177 ((FSNode*)this)->sort_children();
|
nuclear@2
|
178 }
|
nuclear@2
|
179
|
nuclear@2
|
180 std::vector<FSNode*>::const_iterator it;
|
nuclear@2
|
181 it = std::lower_bound(children.begin(), children.end(), &key, childcmpless);
|
nuclear@2
|
182 if(it == children.end() || strcmp((*it)->name, name) != 0) {
|
nuclear@2
|
183 return -1;
|
nuclear@2
|
184 }
|
nuclear@2
|
185 return std::distance(children.begin(), it);
|
nuclear@2
|
186 }
|
nuclear@2
|
187
|
nuclear@2
|
188 FSNode *FSNode::get_parent()
|
nuclear@2
|
189 {
|
nuclear@2
|
190 return parent;
|
nuclear@2
|
191 }
|
nuclear@2
|
192
|
nuclear@2
|
193 const FSNode *FSNode::get_parent() const
|
nuclear@2
|
194 {
|
nuclear@2
|
195 return parent;
|
nuclear@2
|
196 }
|
nuclear@2
|
197
|
nuclear@2
|
198 int FSNode::get_child_count() const
|
nuclear@2
|
199 {
|
nuclear@2
|
200 return (int)children.size();
|
nuclear@2
|
201 }
|
nuclear@2
|
202
|
nuclear@2
|
203 FSNode *FSNode::get_child(int n)
|
nuclear@2
|
204 {
|
nuclear@2
|
205 if(!sorted) {
|
nuclear@2
|
206 sort_children();
|
nuclear@2
|
207 }
|
nuclear@2
|
208 return children[n];
|
nuclear@2
|
209 }
|
nuclear@2
|
210
|
nuclear@2
|
211 const FSNode *FSNode::get_child(int n) const
|
nuclear@2
|
212 {
|
nuclear@2
|
213 if(!sorted) {
|
nuclear@2
|
214 ((FSNode*)this)->sort_children();
|
nuclear@2
|
215 }
|
nuclear@2
|
216 return children[n];
|
nuclear@2
|
217 }
|
nuclear@2
|
218
|
nuclear@2
|
219 bool FSNode::expand()
|
nuclear@2
|
220 {
|
nuclear@2
|
221 expanded = true;
|
nuclear@2
|
222 return true;
|
nuclear@2
|
223 }
|
nuclear@2
|
224
|
nuclear@2
|
225 bool FSNode::is_expanded() const
|
nuclear@2
|
226 {
|
nuclear@2
|
227 return expanded;
|
nuclear@2
|
228 }
|
nuclear@2
|
229
|
nuclear@2
|
230
|
nuclear@2
|
231 // ---- FSDir ----
|
nuclear@2
|
232 FSDir::FSDir()
|
nuclear@2
|
233 {
|
nuclear@2
|
234 type = DIRECTORY;
|
nuclear@2
|
235 }
|
nuclear@2
|
236
|
nuclear@2
|
237 bool FSDir::expand()
|
nuclear@2
|
238 {
|
nuclear@2
|
239 if(expanded) return true;
|
nuclear@1
|
240
|
nuclear@1
|
241 DIR *dir = opendir(path);
|
nuclear@1
|
242 if(!dir) {
|
nuclear@2
|
243 fprintf(stderr, "FSDir::expand() failed to open dir: %s: %s\n", path, strerror(errno));
|
nuclear@2
|
244 return false;
|
nuclear@1
|
245 }
|
nuclear@1
|
246
|
nuclear@2
|
247 char *pathbuf = (char*)alloca(strlen(path) + NAME_MAX + 2);
|
nuclear@1
|
248
|
nuclear@1
|
249 struct dirent *ent;
|
nuclear@1
|
250 while((ent = readdir(dir))) {
|
nuclear@2
|
251 sprintf(pathbuf, "%s/%s", path, ent->d_name);
|
nuclear@1
|
252
|
nuclear@1
|
253 struct stat st;
|
nuclear@1
|
254 if(stat(pathbuf, &st) == -1) {
|
nuclear@1
|
255 fprintf(stderr, "failed to stat: %s: %s\n", pathbuf, strerror(errno));
|
nuclear@1
|
256 continue;
|
nuclear@1
|
257 }
|
nuclear@1
|
258
|
nuclear@2
|
259 FSNode *child;
|
nuclear@2
|
260 if(S_ISDIR(st.st_mode)) {
|
nuclear@2
|
261 child = new FSDir;
|
nuclear@1
|
262 } else {
|
nuclear@2
|
263 FSFile *file = new FSFile;
|
nuclear@2
|
264 file->set_size(st.st_size);
|
nuclear@2
|
265 file->set_type(st_mode_to_type(st.st_mode));
|
nuclear@2
|
266 child = file;
|
nuclear@1
|
267 }
|
nuclear@2
|
268 add_child(child);
|
nuclear@2
|
269 child->set_name(ent->d_name);
|
nuclear@1
|
270 }
|
nuclear@2
|
271
|
nuclear@2
|
272 closedir(dir);
|
nuclear@2
|
273
|
nuclear@2
|
274 expanded = true;
|
nuclear@2
|
275 return true;
|
nuclear@1
|
276 }
|
nuclear@2
|
277
|
nuclear@2
|
278 // ---- FSFile ----
|
nuclear@2
|
279
|
nuclear@2
|
280 FSFile::FSFile()
|
nuclear@2
|
281 {
|
nuclear@2
|
282 type = UNKNOWN;
|
nuclear@2
|
283 size = 0;
|
nuclear@2
|
284 }
|
nuclear@2
|
285
|
nuclear@2
|
286 void FSFile::set_size(unsigned long s)
|
nuclear@2
|
287 {
|
nuclear@2
|
288 size = s;
|
nuclear@2
|
289 }
|
nuclear@2
|
290
|
nuclear@2
|
291 unsigned long FSFile::get_size() const
|
nuclear@2
|
292 {
|
nuclear@2
|
293 return size;
|
nuclear@2
|
294 }
|
nuclear@2
|
295
|
nuclear@2
|
296 // ---- static helpers ----
|
nuclear@2
|
297
|
nuclear@2
|
298 static bool childcmpless(const FSNode *aptr, const FSNode *bptr)
|
nuclear@2
|
299 {
|
nuclear@2
|
300 return strcmp(aptr->get_name(), bptr->get_name()) < 0;
|
nuclear@2
|
301 }
|
nuclear@1
|
302
|
nuclear@1
|
303 static char *clean_path(char *path)
|
nuclear@1
|
304 {
|
nuclear@1
|
305 while(*path && isspace(*path)) {
|
nuclear@1
|
306 path++;
|
nuclear@1
|
307 }
|
nuclear@1
|
308
|
nuclear@1
|
309 char *end = path + strlen(path) - 1;
|
nuclear@1
|
310 while(end >= path && isspace(*end)) {
|
nuclear@1
|
311 *end-- = 0;
|
nuclear@1
|
312 }
|
nuclear@1
|
313
|
nuclear@1
|
314 char *ptr = path - 1;
|
nuclear@1
|
315 while(*++ptr) {
|
nuclear@1
|
316 if(*ptr == '\\') {
|
nuclear@1
|
317 *ptr = '/';
|
nuclear@1
|
318 }
|
nuclear@1
|
319 }
|
nuclear@1
|
320 return path;
|
nuclear@1
|
321 }
|
nuclear@1
|
322
|
nuclear@1
|
323 static char *filename(char *path)
|
nuclear@1
|
324 {
|
nuclear@1
|
325 char *ptr = strrchr(path, '/');
|
nuclear@1
|
326 if(ptr) {
|
nuclear@1
|
327 return ptr + 1;
|
nuclear@1
|
328 }
|
nuclear@1
|
329 return path;
|
nuclear@1
|
330 }
|
nuclear@2
|
331
|
nuclear@2
|
332 static FSNode::Type st_mode_to_type(mode_t mode)
|
nuclear@2
|
333 {
|
nuclear@2
|
334 switch(mode & S_IFMT) {
|
nuclear@2
|
335 case S_IFDIR:
|
nuclear@2
|
336 return FSNode::DIRECTORY;
|
nuclear@2
|
337 case S_IFREG:
|
nuclear@2
|
338 return FSNode::REGFILE;
|
nuclear@2
|
339 case S_IFLNK:
|
nuclear@2
|
340 return FSNode::LINK;
|
nuclear@2
|
341 case S_IFBLK:
|
nuclear@2
|
342 case S_IFCHR:
|
nuclear@2
|
343 return FSNode::DEVICE;
|
nuclear@2
|
344 case S_IFSOCK:
|
nuclear@2
|
345 return FSNode::SOCKET;
|
nuclear@2
|
346 case S_IFIFO:
|
nuclear@2
|
347 return FSNode::FIFO;
|
nuclear@2
|
348 default:
|
nuclear@2
|
349 break;
|
nuclear@2
|
350 }
|
nuclear@2
|
351 return FSNode::UNKNOWN;
|
nuclear@2
|
352 }
|