moving along
[vrfileman] / src / fspath.cc
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;
+}