#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+#include <string>
+#include <map>
#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
#include "fs.h"
#include "icon.h"
#include "gmath/gmath.h"
static IconRenderer *iconrend;
+static std::map<std::string, FSNode*> node_cache;
+static FSNode *cur_node;
+static int start_child;
+
bool init_fs()
{
iconrend = new ShapesIcons;
return false;
}
+ cur_node = get_fsnode(0);
+ cur_node->expand();
return true;
}
void cleanup_fs()
{
+ std::map<std::string, FSNode*>::iterator it = node_cache.begin();
+ while(it != node_cache.end()) {
+ FSNode *node = it++->second;
+ delete node;
+ }
+ node_cache.clear();
delete iconrend;
}
-void draw_fs()
+static Vec3 icon_pos(int row, int col, int ncols, float row_spacing, float radius)
{
- FSNode test;
- test.type = FSNODE_DIR;
+ float angle = 2.0 * M_PI * (float)col / (float)ncols;
+ float x = sin(angle) * radius;
+ float z = -cos(angle) * radius;
+ float y = (float)row * row_spacing;
+ return Vec3(x, y, z);
+}
- Mat4 xform;
- xform.rotate(time_sec, 0, 0);
- xform.rotate(0, 0, time_sec * 0.5);
- xform.translate(0, 2, -5);
+void draw_fs()
+{
+ static const int ncols = 8;
+ static const float row_spacing = 2.0;
+ static const float radius = 5;
- glPushMatrix();
- glMultMatrixf(xform[0]);
+ Mat4 base_xform;
+ base_xform.rotate(time_sec, 0, 0);
+ base_xform.rotate(0, 0, time_sec * 0.5);
+ base_xform.translate(0, 2, 0);
glUseProgram(0);
glDisable(GL_TEXTURE_2D);
- iconrend->draw(&test);
- glPopMatrix();
+ int first = start_child % ncols;
+ int nchildren = (int)cur_node->children.size();
+ int col = 0, row = 0;
+
+ for(int i=0; i<nchildren; i++) {
+ int idx = (i + first) % nchildren;
+ Vec3 pos = icon_pos(row, col, ncols, row_spacing, radius);
+
+ Mat4 xform = base_xform;
+ xform.translate(pos);
+
+ glPushMatrix();
+ glMultMatrixf(xform[0]);
+ iconrend->draw(cur_node->children[idx]);
+ glPopMatrix();
+
+ if(++col >= ncols) {
+ col = 0;
+ ++row;
+ }
+ }
}
-// ---- FSNode implementation ----
-FSNode::FSNode()
+FSNode *get_fsnode(const char *path)
{
- type = FSNODE_UNKNOWN;
- abs_path = name = suffix = 0;
+ char *abspath = make_abs_path(path);
+
+ FSNode *node = node_cache[abspath];
+ if(!node) {
+ node = new FSNode;
+ node->path = path;
+
+ struct stat st;
+ if(stat(node->path, &st) == -1) {
+ fprintf(stderr, "failed to stat: %s\n", node->path.get_path());
+ delete node;
+ return 0;
+ }
+ node->size = st.st_size;
+
+ switch(st.st_mode & S_IFMT) {
+ case S_IFREG:
+ node->type = FSTYPE_FILE;
+ break;
+
+ case S_IFDIR:
+ node->type = FSTYPE_DIR;
+ break;
+
+ case S_IFBLK:
+ case S_IFCHR:
+ node->type = FSTYPE_DEV;
+ break;
+
+ default:
+ node->type = FSTYPE_UNKNOWN;
+ }
+ node_cache[abspath] = node;
+ }
+
+ return node;
}
-FSNode::~FSNode()
+FSNode *get_fsnode(const char *dir, const char *name)
{
- if(abs_path) {
- delete [] abs_path;
+ if(!dir) {
+ return get_fsnode(name);
+ }
+ if(!name || *name == '/') {
+ return 0;
}
+
+ int len = strlen(dir) + 1 + strlen(name);
+ char *buf = new char[len + 1];
+ sprintf(buf, "%s/%s", dir, name);
+ FSNode *res = get_fsnode(buf);
+ delete [] buf;
+ return res;
}
-void FSNode::set_path(const char *s)
+// ---- FSNode implementation ----
+FSNode::FSNode()
{
- int len = strlen(s);
- if(!len) return;
+ type = FSTYPE_UNKNOWN;
+ size = 0;
+ parent = 0;
+}
- delete [] abs_path;
+bool FSNode::expand()
+{
+ if(type != FSTYPE_DIR) {
+ return false;
+ }
- const char *slash = s + len - 1;
- while(slash > s && *slash != '/') {
- --slash;
+ DIR *dir = opendir(path);
+ if(!dir) {
+ fprintf(stderr, "failed to open dir: %s: %s\n", path.get_path(), strerror(errno));
+ return false;
}
- if(name == s) { // no slashes found
- char buf[1024];
- if(!getcwd(buf, sizeof buf)) {
- abort();
- }
- int dirlen = strlen(buf);
- abs_path = new char[len + dirlen + 2];
- sprintf(abs_path, "%s/%s", buf, s);
+ struct dirent *dent;
+ while((dent = readdir(dir))) {
+ if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
+ continue;
+ }
- name = abs_path + dirlen + 1;
- suffix = abs_path + len + dirlen - 1;
- } else {
- abs_path = new char[len + 1];
- memcpy(abs_path, s, len + 1);
+ FSNode *node = get_fsnode(path, dent->d_name);
+ if(!node) continue;
- name = abs_path + (slash - s);
- suffix = abs_path + len - 1;
+ children.push_back(node);
}
+ printf("expanded %d children\n", (int)children.size());
- while(suffix > name && *suffix != '.') {
- --suffix;
- }
- if(suffix == name) {
- suffix = 0;
- }
+ parent = get_fsnode(path.get_parent());
+ return true;
}
#ifndef FS_H_
#define FS_H_
+#include <vector>
+#include <stdlib.h>
+#include "fspath.h"
+
enum FSNodeType {
- FSNODE_UNKNOWN,
- FSNODE_FILE,
- FSNODE_DIR,
- FSNODE_DEV
+ FSTYPE_UNKNOWN,
+ FSTYPE_FILE,
+ FSTYPE_DIR,
+ FSTYPE_DEV
};
class FSNode {
public:
FSNodeType type;
- char *abs_path;
- char *name, *suffix;
+ FSPath path;
+ size_t size;
+
+ FSNode *parent;
+ std::vector<FSNode*> children;
FSNode();
- ~FSNode();
- void set_path(const char *s);
+ bool expand();
};
bool init_fs();
void draw_fs();
+FSNode *get_fsnode(const char *path);
+FSNode *get_fsnode(const char *dir, const char *name);
+
#endif // FS_H_
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "fspath.h"
+
+
+char *make_abs_path(const char *s)
+{
+ char wd[1024];
+ int slen = s ? strlen(s) : 0;
+ int len = slen;
+ char *res;
+
+ if(!s || *s != '/') {
+ getcwd(wd, sizeof wd);
+ int wdlen = strlen(wd);
+ len = wdlen + 1 + slen;
+
+ res = new char[len + 1];
+ if(s) {
+ sprintf(res, "%s/%s", wd, s);
+ } else {
+ memcpy(res, wd, len + 1);
+ }
+ } else {
+ res = new char[len + 1];
+ memcpy(res, s, len + 1);
+ }
+
+ return res;
+}
+
+FSPath::FSPath()
+{
+ abs_path = name = suffix = parent = 0;
+}
+
+FSPath::FSPath(const char *s)
+{
+ abs_path = name = suffix = parent = 0;
+ set_path(s);
+}
+
+FSPath::~FSPath()
+{
+ delete [] abs_path;
+ delete [] parent;
+}
+
+FSPath::FSPath(const FSPath &p)
+{
+ abs_path = name = suffix = parent = 0;
+
+ if(abs_path) {
+ int len = strlen(p.abs_path);
+ abs_path = new char[len + 1];
+ memcpy(abs_path, p.abs_path, len + 1);
+
+ if(p.name) {
+ name = abs_path + (p.name - p.abs_path);
+ }
+ if(p.suffix) {
+ suffix = abs_path + (p.suffix - p.abs_path);
+ }
+ }
+ if(p.parent) {
+ int len = strlen(p.parent);
+ parent = new char[len + 1];
+ memcpy(parent, p.parent, len + 1);
+ }
+}
+
+FSPath &FSPath::operator =(const FSPath &p)
+{
+ if(&p != this) {
+ delete [] abs_path;
+ delete [] parent;
+ abs_path = name = suffix = parent = 0;
+
+ if(p.abs_path) {
+ int len = strlen(p.abs_path);
+ abs_path = new char[len + 1];
+ memcpy(abs_path, p.abs_path, len + 1);
+
+ if(p.name) {
+ name = abs_path + (p.name - p.abs_path);
+ }
+ if(p.suffix) {
+ suffix = abs_path + (p.suffix - p.abs_path);
+ }
+ }
+ if(p.parent) {
+ int len = strlen(p.parent);
+ parent = new char[len + 1];
+ memcpy(parent, p.parent, len + 1);
+ }
+ }
+ return *this;
+}
+
+FSPath::FSPath(FSPath &&p)
+{
+ abs_path = p.abs_path;
+ name = p.name;
+ suffix = p.suffix;
+ parent = p.parent;
+
+ p.abs_path = p.name = p.suffix = p.parent = 0;
+}
+
+FSPath &FSPath::operator =(FSPath &&p)
+{
+ if(&p != this) {
+ delete [] abs_path;
+ delete [] parent;
+
+ abs_path = p.abs_path;
+ name = p.name;
+ suffix = p.suffix;
+ parent = p.parent;
+
+ p.abs_path = p.name = p.suffix = p.parent = 0;
+ }
+ return *this;
+}
+
+void FSPath::sanitize()
+{
+ // first pass, convert and backslashes to slashes
+ char *s = abs_path;
+ while(*s) {
+ if(*s == '\\') {
+ *s = '/';
+ }
+ ++s;
+ }
+
+ // second pass, remove any "./" and "foo/.." parts
+ s = abs_path;
+ int len = strlen(s);
+ while((s = strstr(s, "./"))) {
+ len -= 2;
+ memmove(s, s + 2, len + 1);
+ }
+ assert(len == (int)strlen(abs_path));
+
+ s = abs_path;
+ while((s = strstr(s, "/.."))) {
+ char *start = s;
+ while(start > abs_path && *--start != '/');
+
+ memmove(start, s + 3, len - 2);
+ len -= (s + 3) - start;
+ s = start;
+ }
+ assert(len == (int)strlen(abs_path));
+
+ // remove double slashes
+ while((s = strstr(abs_path, "//"))) {
+ memmove(s, s + 1, len-- - (s - abs_path));
+ }
+ assert(len == (int)strlen(abs_path));
+
+ // remove trailing slash
+ if(abs_path[len - 1] == '/') {
+ abs_path[--len] = 0;
+ }
+ assert(len == (int)strlen(abs_path));
+
+ // setup name and prefix pointers as necessary
+ name = suffix = 0;
+ s = abs_path;
+ while(*s) {
+ if(*s == '/') name = s + 1;
+ if(*s == '.') suffix = s;
+ ++s;
+ }
+
+ if(!name) {
+ name = abs_path; // shouldn't happen, all paths start with a slash
+ fprintf(stderr, "FSPath::sanitize: absolute path \"%s\" isn't absolute!\n", abs_path);
+ }
+
+ if(suffix == abs_path || suffix <= name) {
+ suffix = 0;
+ }
+
+ // create parent name string
+ if(abs_path[0] == '/' && abs_path[1] == 0) {
+ parent = 0; // root dir has no parent
+ } else {
+ int plen = name - 1 - abs_path;
+ assert(plen > 0);
+
+ parent = new char[plen + 1];
+ memcpy(parent, abs_path, plen);
+ parent[plen] = 0;
+ }
+}
+
+bool FSPath::set_path(const char *s)
+{
+ char *tmp = make_abs_path(s);
+ if(!tmp) {
+ return false;
+ }
+
+ delete [] abs_path;
+ delete [] parent;
+ abs_path = tmp;
+ parent = 0;
+
+ sanitize();
+ return true;
+}
+
+const char *FSPath::get_path() const
+{
+ return abs_path;
+}
+
+const char *FSPath::get_name() const
+{
+ return name;
+}
+
+const char *FSPath::get_suffix() const
+{
+ return suffix;
+}
+
+const char *FSPath::get_parent() const
+{
+ return parent;
+}
+
+bool FSPath::append_path(const char *s)
+{
+ if(!s || !*s) return false;
+
+ if(!abs_path) {
+ return set_path(s);
+ }
+
+ if(*s == '/') {
+ return false; // can't append an absolute path
+ }
+
+ int len = strlen(abs_path) + 1 + strlen(s);
+ try {
+ char *tmp = new char[len + 1];
+
+ delete [] abs_path;
+ abs_path = tmp;
+ delete [] parent;
+ parent = 0;
+ }
+ catch(...) {
+ return false;
+ }
+
+ sanitize();
+ return true;
+}
+
+bool FSPath::exists() const
+{
+ struct stat st;
+ return abs_path && stat(abs_path, &st) != -1;
+}
+
+bool FSPath::is_file() const
+{
+ struct stat st;
+ return abs_path && stat(abs_path, &st) != -1 && S_ISREG(st.st_mode);
+}
+
+bool FSPath::is_dir() const
+{
+ struct stat st;
+ return abs_path && stat(abs_path, &st) != -1 && S_ISDIR(st.st_mode);
+}
+
+bool FSPath::is_dev() const
+{
+ struct stat st;
+ unsigned int devmask = S_IFBLK | S_IFCHR;
+ return abs_path && stat(abs_path, &st) != -1 && (st.st_mode & devmask);
+}
+
+FSPath::operator const char* () const
+{
+ return abs_path;
+}
--- /dev/null
+#ifndef FSPATH_H_
+#define FSPATH_H_
+
+char *make_abs_path(const char *s);
+
+class FSPath {
+private:
+ char *abs_path;
+ char *name, *suffix;
+ char *parent;
+
+ void sanitize();
+
+public:
+ FSPath();
+ FSPath(const char *s);
+ ~FSPath();
+
+ FSPath(const FSPath &p);
+ FSPath &operator =(const FSPath &p);
+
+ FSPath(FSPath &&p);
+ FSPath &operator =(FSPath &&p);
+
+ bool set_path(const char *s);
+ const char *get_path() const;
+
+ const char *get_name() const;
+ const char *get_suffix() const;
+ const char *get_parent() const;
+
+ bool append_path(const char *s);
+
+ // does this path actually exist in the filesystem?
+ bool exists() const;
+ // true if this path exists and is a regular file
+ bool is_file() const;
+ // true if this path exists and is a directory
+ bool is_dir() const;
+ // true of this path exists and is a device file
+ bool is_dev() const;
+
+ operator const char* () const;
+};
+
+
+#endif // FSPATH_H_
Mat4 xform;
gen_geosphere(priv->shape[SHAPE_SPHERE], 0.5, 0);
- gen_box(priv->shape[SHAPE_BOX], 1, 1, 1);
+ gen_box(priv->shape[SHAPE_BOX], 0.7, 0.7, 0.7);
gen_torus(priv->shape[SHAPE_TORUS], 0.4, 0.1, 12, 6);
gen_cone(priv->shape[SHAPE_CONE], 0.5, 1.0, 8, 2, 1);
static int fstype_shape(FSNodeType type)
{
switch(type) {
- case FSNODE_FILE:
+ case FSTYPE_FILE:
return SHAPE_SPHERE;
- case FSNODE_DIR:
+ case FSTYPE_DIR:
return SHAPE_BOX;
- case FSNODE_DEV:
+ case FSTYPE_DEV:
return SHAPE_TORUS;
default:
break;