welding in a virtual unix root for windoze
[feisty_meow.git] / nucleus / library / filesystem / filename.cpp
index ef148ea8152d72d85004b02435b19487ca1d9e26..ba72084e997a929723c4ba7c9fa6358bd83da41d 100644 (file)
 
 #include <basis/byte_array.h>
 #include <basis/functions.h>
+#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
 
+#undef LOG
+#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s())
+
 using namespace basis;
 using namespace structures;
 
@@ -96,6 +99,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__
@@ -146,6 +155,7 @@ void filename::push(const astring &to_push)
 
 void filename::canonicalize()
 {
+  FUNCDEF("canonicalize");
   // turn all the non-default separators into the default.
   bool found_sep = false;
   for (int j = 0; j < length(); j++) {
@@ -176,6 +186,63 @@ void filename::canonicalize()
     } else saw_sep = false;
   }
 
+#ifdef __WIN32__
+  // 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...
+//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)
+      && 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 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)) ) {
+    // we seem reasonably sure now that this is a windows path hiding in msys format, but
+    // the next character needs to be a slash (if there is a next character) for it to be
+    // the windows drive form.  otherwise it could be /tmp, which would obviously not be
+    // intended as a windows path.
+    if ( (length() == 2) || (get(2) == DEFAULT_SEPARATOR) ) {
+      // cool, this should be interpretable as an msys path, except for those wacky types
+      // of folks that might use a top-level single character directory name.  we cannot
+      // help them, because we have made a design decision to support msys-style paths.
+      // note that this would only affect someone if they were referring to their directory on
+      // the current windows partition (c:, d:, etc.) without providing the drive letter,
+      // if they had that single character directory name (e.g., c:\x, d:\q, etc.) and even
+      // 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);
+    }
+  } 
+
+  // if no specialized path specifications were seen, and we have a unix style path
+  // here, then there will be trouble when we pass that to windows.
+//if first character is a slash, and second char is alphanumeric, then we check...
+//can we find a cygwin root dir stored in our config stuff?
+//  maybe in the build version file?  ugh, yuck.
+//  what about in generated files, created at build time?  --> yes, nice option.
+//
+//hmmm: we need the capability to re-create the config file that tells us
+// where cyg root is, but how can we, aside from guessing at where to find
+// cygwin (c:/cygwin c:/cygwin64 etc).
+//
+//hmmm: 
+
+#endif
+
   // we don't crop the last separator if the name's too small.  for msdos
   // names, that would be chopping a slash off the c:\ style name.
   if (length() > 3) {
@@ -265,6 +332,23 @@ bool filename::is_executable() const
   return !!(fill.st_mode & S_IEXEC);
 }
 
+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;
+}
+
 int filename::find_last_separator(const astring &look_at) const
 {
   int last_sep = -1;
@@ -321,11 +405,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)
@@ -374,16 +457,17 @@ 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
       // string to the list.
-      if (!i || accumulator.length()) pieces += accumulator;
+      if (i && accumulator.length()) pieces += accumulator;
       // now reset our accumulated text.
       accumulator = astring::empty_string();
     } else {
@@ -394,9 +478,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))
@@ -408,8 +493,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())
@@ -458,8 +548,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())