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