updates lurching towards functionality
[feisty_meow.git] / nucleus / library / filesystem / filename.cpp
index adca7e99dcee76d0d31b12f473c455897d594d95..d12d9e7e58825b735a2f93911591abc842ed5850 100644 (file)
 
 #include <basis/byte_array.h>
 #include <basis/functions.h>
+#include <configuration/application_configuration.h>
+/*
+ hmmm: note that we are relying on forward declared code here.
+ the canonical ordering for feisty's nucleus has the filesystem code come before
+ the configuration code, because the configuratin library uses filesystem features.
+ not sure i want to resolve this bizarritude at this time, but it points to the
+ need for a virtual interface at lower level than either filesystem or configuration
+ libraries, so we can emplace the need for the virtual unix root as a low-level
+ dependency to be implemented later.
+ */
+
 #include <textual/parser_bits.h>
 
 #include <stdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#ifdef __UNIX__
+#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
   #include <unistd.h>
-#endif
-#ifdef __WIN32__
+#else
   #include <io.h>
 #endif
 
@@ -43,13 +53,13 @@ class status_info : public stat
 
 namespace filesystem {
 
-#if defined(__WIN32__) || defined(__VMS__)
-  const char DEFAULT_SEPARATOR = '\\';
-#elif defined(__UNIX__)
+//#if defined(__WIN32__) || defined(__VMS__)
+//  const char DEFAULT_SEPARATOR = '\\';
+//#elif defined(__UNIX__)
   const char DEFAULT_SEPARATOR = '/';
-#else
-  #error "We have no idea what the default path separator is."
-#endif
+//#else
+//  #error "We have no idea what the default path separator is."
+//#endif
 
 const char *NO_PARENT_DEFAULT = ".";
   // used when no directory name can be popped off.
@@ -100,6 +110,12 @@ bool filename::good() const { return exists(); }
 
 bool filename::unlink() const { return ::unlink(observe()) == 0; }
 
+void filename::reset(const astring &name) {
+  *this = name;
+  _had_directory = true;  // until we know better.
+  canonicalize();
+}
+
 astring filename::null_device()
 {
 #ifdef __WIN32__
@@ -185,14 +201,24 @@ void filename::canonicalize()
   // on windows, we want to translate away from any cygwin or msys format into a more palatable
   // version that the rest of windows understands.
   // first, cygwin...
-  const astring CYGDRIVE_PATH = astring(astring(DEFAULT_SEPARATOR, 1) + "cygdrive"
-      + astring(DEFAULT_SEPARATOR, 1));
+//hmmm: make these into statics!
+  const astring CYGDRIVE_SENTINEL = "cygdrive";
+  const astring CYGDRIVE_PATH = astring(astring(DEFAULT_SEPARATOR, 1)
+      + CYGDRIVE_SENTINEL + astring(DEFAULT_SEPARATOR, 1));
+
   // must be at least as long as the string we're looking for, plus a drive letter afterwards.
-  if ( (length() > CYGDRIVE_PATH.length() + 1) && begins(CYGDRIVE_PATH) ) {
+  if ( (length() >= CYGDRIVE_PATH.length() + 1)
+      && separator(get(0))
+      && separator(get(CYGDRIVE_PATH.length() - 1))
+      && compare(CYGDRIVE_SENTINEL, 1, 
+          0, CYGDRIVE_SENTINEL.length(), true) ) {
     zap(0, CYGDRIVE_PATH.length() - 1);  // whack the cygdrive portion plus two slashes.
     insert(1, ":");  // add a colon after the imputed drive letter.
-LOG(astring("turned cygdrive string into: ") + *this);
+//LOG(astring("turned cygdrive path string into: ") + *this);
+  } else {
+//LOG(astring("path didn't match so left as: ") + *this);
   }
+
   // now we convert msys...
   if ( (length() >= 2) && (get(0) == DEFAULT_SEPARATOR)
         && textual::parser_bits::is_alpha(get(1)) ) {
@@ -210,9 +236,43 @@ LOG(astring("turned cygdrive string into: ") + *this);
       // then only on the near defunct windows platform.
       zap(0, 0);  // take off initial slash.
       insert(1, ":");  // add the obligatory colon.
-LOG(astring("turned msys string into: ") + *this);
+//LOG(astring("turned msys string into: ") + *this);
     }
   } 
+
+  // if we still have a unix style path here on windows, then there will be
+  // trouble when we pass that to the OS.  we are not using any cygwin or
+  // other virtualization libraries directly, so we can't rely on those to
+  // fix the path.  but if we built under something like cygwin, we should
+  // have stored the real dos-style location of the virtual unix root.  we
+  // will use that to replace the root '/' and this should fix most of that
+  // style of path.
+  bool inject_root = false;  // assume we don't need to do anything.
+
+LOG(astring("before root injection: ") + raw());
+
+  // condition here just checks if the path is only the root.
+  if ( (length() == 1) && separator(get(0)) ) { inject_root = true; }
+
+if (inject_root) LOG("decided to inject root since path is '/'.");
+
+  // condition is looking for first character being a slash, and second char as alphanumeric or dash or underscore.
+  // we will currently fail detecting freaky paths that don't start off with alphanumeric or one of that small set of special chars.
+  if ( (length() >= 2)
+      && separator(get(0)) 
+      && ( textual::parser_bits::is_alphanumeric(get(1)) || ('-' == get(1)) || ('_' == get(1)) ) ) { 
+    inject_root = true;
+if (inject_root) LOG(astring("decided to inject root since path is compatible: ") + *this);
+  }
+
+LOG(astring("after second phase root injection: ") + *this);
+
+  if (inject_root) {
+    // inject the actual path to the unix root in front, if we know it.
+    // if we don't know it, then a default path that's unlikely to work is idiotically plugged in.
+    insert(0, configuration::application_configuration::get_virtual_unix_root());
+LOG(astring("turned cygdrive path string into: ") + *this);
+  }
 #endif
 
   // we don't crop the last separator if the name's too small.  for msdos
@@ -309,10 +369,15 @@ bool filename::is_normal() const
   status_info fill;
   if (!get_info(&fill))
     return false;
+#if defined(__WIN32__) || defined(__VMS__)
+//hmmm: is there a corresponding set of functions for windows, where applicable?
+  bool weird = false;
+#else
   bool weird = S_ISCHR(fill.st_mode)
       || S_ISBLK(fill.st_mode)
       || S_ISFIFO(fill.st_mode)
       || S_ISSOCK(fill.st_mode);
+#endif
   return !weird;
 }
 
@@ -372,11 +437,10 @@ bool filename::exists() const
 {
   if (is_directory())
     return true;
+  // if the file name is empty, that cannot exist.
   if (!length())
     return false;
   return is_readable();
-///  byte_filer opened(observe(), "rb");
-///  return opened.good();
 }
 
 bool filename::legal_character(char to_check)
@@ -425,11 +489,12 @@ bool filename::unpack(byte_array &packed_form)
   return true;
 }
 
-void filename::separate(string_array &pieces) const
+void filename::separate(bool &rooted, string_array &pieces) const
 {
   pieces.reset();
   const astring &raw_form = raw();
   astring accumulator;  // holds the names we find.
+  rooted = raw_form.length() && separator(raw_form[0]);
   for (int i = 0; i < raw_form.length(); i++) {
     if (separator(raw_form[i])) {
       // this is a separator character, so eat it and add the accumulated
@@ -445,9 +510,10 @@ void filename::separate(string_array &pieces) const
   if (accumulator.length()) pieces += accumulator;
 }
 
-void filename::join(const string_array &pieces)
+void filename::join(bool rooted, const string_array &pieces)
 {
   astring constructed_name;  // we'll make a filename here.
+  if (rooted) constructed_name += DEFAULT_SEPARATOR;
   for (int i = 0; i < pieces.length(); i++) {
     constructed_name += pieces[i];
     if (!i || (i != pieces.length() - 1))
@@ -459,8 +525,13 @@ void filename::join(const string_array &pieces)
 bool filename::base_compare_prefix(const filename &to_compare,
     string_array &first, string_array &second)
 {
-  separate(first);
-  to_compare.separate(second);
+  bool first_rooted;
+  separate(first_rooted, first);
+  bool second_rooted;
+  to_compare.separate(second_rooted, second);
+  if (first_rooted != second_rooted) {
+    return false;
+  }
   // that case should never be allowed, since there are some bits missing
   // in the name to be compared.
   if (first.length() > second.length())
@@ -509,8 +580,10 @@ bool filename::compare_prefix(const filename &to_compare)
 bool filename::base_compare_suffix(const filename &to_compare,
     string_array &first, string_array &second)
 {
-  separate(first);
-  to_compare.separate(second);
+  bool first_rooted;
+  separate(first_rooted, first);
+  bool second_rooted;
+  to_compare.separate(second_rooted, second);
   // that case should never be allowed, since there are some bits missing
   // in the name to be compared.
   if (first.length() > second.length())