moving along
authorJohn Tsiombikas <nuclear@mutantstargoat.com>
Sun, 31 Jul 2016 20:13:36 +0000 (23:13 +0300)
committerJohn Tsiombikas <nuclear@mutantstargoat.com>
Sun, 31 Jul 2016 20:13:36 +0000 (23:13 +0300)
src/fs.cc
src/fs.h
src/fspath.cc [new file with mode: 0644]
src/fspath.h [new file with mode: 0644]
src/icon.cc

index c85040a..b65db45 100644 (file)
--- a/src/fs.cc
+++ b/src/fs.cc
@@ -1,7 +1,12 @@
 #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;
@@ -17,83 +26,158 @@ bool init_fs()
                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;
 }
index c6f95a7..6c7b406 100644 (file)
--- a/src/fs.h
+++ b/src/fs.h
@@ -1,23 +1,29 @@
 #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();
@@ -25,4 +31,7 @@ void cleanup_fs();
 
 void draw_fs();
 
+FSNode *get_fsnode(const char *path);
+FSNode *get_fsnode(const char *dir, const char *name);
+
 #endif // FS_H_
diff --git a/src/fspath.cc b/src/fspath.cc
new file mode 100644 (file)
index 0000000..e6677bd
--- /dev/null
@@ -0,0 +1,296 @@
+#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;
+}
diff --git a/src/fspath.h b/src/fspath.h
new file mode 100644 (file)
index 0000000..0bc0b70
--- /dev/null
@@ -0,0 +1,47 @@
+#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_
index 1e92995..ee7ccba 100644 (file)
@@ -52,7 +52,7 @@ bool ShapesIcons::init()
        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);
@@ -72,11 +72,11 @@ void ShapesIcons::shutdown()
 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;