vrfileman

view src/fs.cc @ 2:282da6123fd4

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