nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@2: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@2: #include nuclear@1: #include "fs.h" nuclear@1: nuclear@2: static bool childcmpless(const FSNode *aptr, const FSNode *bptr); nuclear@1: static char *clean_path(char *path); nuclear@1: static char *filename(char *path); nuclear@2: static FSNode::Type st_mode_to_type(mode_t mode); nuclear@1: nuclear@1: FSNode::FSNode() nuclear@1: { nuclear@1: init(); nuclear@1: } nuclear@1: nuclear@1: FSNode::~FSNode() nuclear@1: { nuclear@1: destroy(); nuclear@1: } nuclear@1: nuclear@1: void FSNode::init() nuclear@1: { nuclear@1: path = name = 0; nuclear@1: parent = 0; nuclear@1: expanded = false; nuclear@2: sorted = true; nuclear@1: uid = gid = mode = 0; nuclear@2: type = UNKNOWN; nuclear@1: } nuclear@1: nuclear@1: void FSNode::destroy() nuclear@1: { nuclear@1: delete [] path; nuclear@1: children.clear(); nuclear@1: init(); nuclear@1: } nuclear@1: nuclear@1: void FSNode::destroy_tree() nuclear@1: { nuclear@1: for(size_t i=0; idestroy_tree(); nuclear@1: delete children[i]; nuclear@1: } nuclear@1: destroy(); nuclear@1: } nuclear@1: nuclear@2: void FSNode::set_type(Type type) nuclear@2: { nuclear@2: this->type = type; nuclear@2: } nuclear@2: nuclear@2: FSNode::Type FSNode::get_type() const nuclear@2: { nuclear@2: return type; nuclear@2: } nuclear@2: nuclear@2: bool FSNode::is_file() const nuclear@2: { nuclear@2: return type != DIRECTORY; nuclear@2: } nuclear@2: nuclear@2: bool FSNode::is_directory() const nuclear@2: { nuclear@2: return type == DIRECTORY; nuclear@2: } nuclear@2: nuclear@2: void FSNode::sort_children() nuclear@2: { nuclear@2: std::sort(children.begin(), children.end(), childcmpless); nuclear@2: sorted = true; nuclear@2: } nuclear@2: nuclear@1: void FSNode::set_path(const char *path) nuclear@1: { nuclear@1: delete [] this->path; nuclear@1: nuclear@1: char *buf = new char[strlen(path) + 1]; nuclear@1: strcpy(buf, path); nuclear@1: nuclear@1: char *tmp = clean_path(buf); nuclear@1: if(tmp == buf) { nuclear@1: this->path = tmp; nuclear@1: } else { nuclear@1: this->path = new char[strlen(tmp) + 1]; nuclear@1: strcpy(this->path, tmp); nuclear@1: delete [] buf; nuclear@1: } nuclear@1: nuclear@1: name = filename(this->path); nuclear@1: } nuclear@1: nuclear@1: void FSNode::set_name(const char *name) nuclear@1: { nuclear@1: delete [] path; nuclear@1: nuclear@1: if(parent) { nuclear@1: char *path = new char[strlen(name) + strlen(parent->path) + 2]; nuclear@1: sprintf(path, "%s/%s", parent->path, name); nuclear@1: set_path(path); nuclear@1: } else { nuclear@1: path = this->name = new char[strlen(name) + 1]; nuclear@1: strcpy(path, name); nuclear@1: } nuclear@1: } nuclear@1: nuclear@2: const char *FSNode::get_path() const nuclear@1: { nuclear@2: return path; nuclear@2: } nuclear@1: nuclear@2: const char *FSNode::get_name() const nuclear@2: { nuclear@2: return name; nuclear@2: } nuclear@2: nuclear@2: bool FSNode::add_child(FSNode *node) nuclear@2: { nuclear@2: if(node->parent == this) { nuclear@2: return true; nuclear@2: } nuclear@2: nuclear@2: if(node->parent) { nuclear@2: node->parent->remove_child(node); nuclear@2: } nuclear@2: node->parent = this; nuclear@2: nuclear@2: try { nuclear@2: children.push_back(node); nuclear@2: } nuclear@2: catch(...) { nuclear@2: return false; nuclear@2: } nuclear@2: nuclear@2: sorted = false; nuclear@2: return true; nuclear@2: } nuclear@2: nuclear@2: bool FSNode::remove_child(FSNode *node) nuclear@2: { nuclear@2: int cidx = find_child(node); nuclear@2: if(cidx == -1) { nuclear@2: return false; nuclear@2: } nuclear@2: children.erase(children.begin() + cidx); nuclear@2: nuclear@2: if(node->parent != this) { nuclear@2: fprintf(stderr, "FSNode::remove_child(): target node doesn't have this node as parent\n"); nuclear@2: // let's not touch it if this happens... nuclear@2: } else { nuclear@2: node->parent = 0; nuclear@2: } nuclear@2: return true; nuclear@2: } nuclear@2: nuclear@2: int FSNode::find_child(FSNode *node) const nuclear@2: { nuclear@2: for(size_t i=0; isort_children(); nuclear@2: } nuclear@2: nuclear@2: std::vector::const_iterator it; nuclear@2: it = std::lower_bound(children.begin(), children.end(), &key, childcmpless); nuclear@2: if(it == children.end() || strcmp((*it)->name, name) != 0) { nuclear@2: return -1; nuclear@2: } nuclear@2: return std::distance(children.begin(), it); nuclear@2: } nuclear@2: nuclear@2: FSNode *FSNode::get_parent() nuclear@2: { nuclear@2: return parent; nuclear@2: } nuclear@2: nuclear@2: const FSNode *FSNode::get_parent() const nuclear@2: { nuclear@2: return parent; nuclear@2: } nuclear@2: nuclear@2: int FSNode::get_child_count() const nuclear@2: { nuclear@2: return (int)children.size(); nuclear@2: } nuclear@2: nuclear@2: FSNode *FSNode::get_child(int n) nuclear@2: { nuclear@2: if(!sorted) { nuclear@2: sort_children(); nuclear@2: } nuclear@2: return children[n]; nuclear@2: } nuclear@2: nuclear@2: const FSNode *FSNode::get_child(int n) const nuclear@2: { nuclear@2: if(!sorted) { nuclear@2: ((FSNode*)this)->sort_children(); nuclear@2: } nuclear@2: return children[n]; nuclear@2: } nuclear@2: nuclear@2: bool FSNode::expand() nuclear@2: { nuclear@2: expanded = true; nuclear@2: return true; nuclear@2: } nuclear@2: nuclear@2: bool FSNode::is_expanded() const nuclear@2: { nuclear@2: return expanded; nuclear@2: } nuclear@2: nuclear@2: nuclear@2: // ---- FSDir ---- nuclear@2: FSDir::FSDir() nuclear@2: { nuclear@2: type = DIRECTORY; nuclear@2: } nuclear@2: nuclear@2: bool FSDir::expand() nuclear@2: { nuclear@2: if(expanded) return true; nuclear@1: nuclear@1: DIR *dir = opendir(path); nuclear@1: if(!dir) { nuclear@2: fprintf(stderr, "FSDir::expand() failed to open dir: %s: %s\n", path, strerror(errno)); nuclear@2: return false; nuclear@1: } nuclear@1: nuclear@2: char *pathbuf = (char*)alloca(strlen(path) + NAME_MAX + 2); nuclear@1: nuclear@1: struct dirent *ent; nuclear@1: while((ent = readdir(dir))) { nuclear@2: sprintf(pathbuf, "%s/%s", path, ent->d_name); nuclear@1: nuclear@1: struct stat st; nuclear@1: if(stat(pathbuf, &st) == -1) { nuclear@1: fprintf(stderr, "failed to stat: %s: %s\n", pathbuf, strerror(errno)); nuclear@1: continue; nuclear@1: } nuclear@1: nuclear@2: FSNode *child; nuclear@2: if(S_ISDIR(st.st_mode)) { nuclear@2: child = new FSDir; nuclear@1: } else { nuclear@2: FSFile *file = new FSFile; nuclear@2: file->set_size(st.st_size); nuclear@2: file->set_type(st_mode_to_type(st.st_mode)); nuclear@2: child = file; nuclear@1: } nuclear@2: add_child(child); nuclear@2: child->set_name(ent->d_name); nuclear@1: } nuclear@2: nuclear@2: closedir(dir); nuclear@2: nuclear@2: expanded = true; nuclear@2: return true; nuclear@1: } nuclear@2: nuclear@2: // ---- FSFile ---- nuclear@2: nuclear@2: FSFile::FSFile() nuclear@2: { nuclear@2: type = UNKNOWN; nuclear@2: size = 0; nuclear@2: } nuclear@2: nuclear@2: void FSFile::set_size(unsigned long s) nuclear@2: { nuclear@2: size = s; nuclear@2: } nuclear@2: nuclear@2: unsigned long FSFile::get_size() const nuclear@2: { nuclear@2: return size; nuclear@2: } nuclear@2: nuclear@2: // ---- static helpers ---- nuclear@2: nuclear@2: static bool childcmpless(const FSNode *aptr, const FSNode *bptr) nuclear@2: { nuclear@2: return strcmp(aptr->get_name(), bptr->get_name()) < 0; nuclear@2: } nuclear@1: nuclear@1: static char *clean_path(char *path) nuclear@1: { nuclear@1: while(*path && isspace(*path)) { nuclear@1: path++; nuclear@1: } nuclear@1: nuclear@1: char *end = path + strlen(path) - 1; nuclear@1: while(end >= path && isspace(*end)) { nuclear@1: *end-- = 0; nuclear@1: } nuclear@1: nuclear@1: char *ptr = path - 1; nuclear@1: while(*++ptr) { nuclear@1: if(*ptr == '\\') { nuclear@1: *ptr = '/'; nuclear@1: } nuclear@1: } nuclear@1: return path; nuclear@1: } nuclear@1: nuclear@1: static char *filename(char *path) nuclear@1: { nuclear@1: char *ptr = strrchr(path, '/'); nuclear@1: if(ptr) { nuclear@1: return ptr + 1; nuclear@1: } nuclear@1: return path; nuclear@1: } nuclear@2: nuclear@2: static FSNode::Type st_mode_to_type(mode_t mode) nuclear@2: { nuclear@2: switch(mode & S_IFMT) { nuclear@2: case S_IFDIR: nuclear@2: return FSNode::DIRECTORY; nuclear@2: case S_IFREG: nuclear@2: return FSNode::REGFILE; nuclear@2: case S_IFLNK: nuclear@2: return FSNode::LINK; nuclear@2: case S_IFBLK: nuclear@2: case S_IFCHR: nuclear@2: return FSNode::DEVICE; nuclear@2: case S_IFSOCK: nuclear@2: return FSNode::SOCKET; nuclear@2: case S_IFIFO: nuclear@2: return FSNode::FIFO; nuclear@2: default: nuclear@2: break; nuclear@2: } nuclear@2: return FSNode::UNKNOWN; nuclear@2: }