]> Dogcows Code - chaz/yoink/blobdiff - src/stlplus/portability/file_system.cpp
import stlplus 3.7
[chaz/yoink] / src / stlplus / portability / file_system.cpp
index cfca256232a3e7e6cb4e14fd76a0ece2b71a403b..cac32927e30adf39e7513891bb022235e54ab879 100644 (file)
-////////////////////////////////////////////////////////////////////////////////
-
-//   Author:    Andy Rushton
-//   Copyright: (c) Southampton University 1999-2004
-//              (c) Andy Rushton           2004-2009
-//   License:   BSD License, see ../docs/license.html
-
-//   This is a portable interface to the file system.
-
-//   The idea is that you write all file system access code using these functions,
-//   which are ported to all platforms that we are interested in. Therefore your
-//   code is inherently portable.
-
-//   Native Windows version: switched on by macro _WIN32 which is defined by VC++/Borland/Mingw compilers
-//   Unix/Gnu version:   default variant, no compiler directives are required but _WIN32 must be absent
-//   Cygwin/Gnu version: as Unix version but with additional support for Windows drive letters
-
-////////////////////////////////////////////////////////////////////////////////
-#include "file_system.hpp"
-#include "wildcard.hpp"
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <algorithm>
-#include <ctype.h>
-
-#ifdef MSWINDOWS
-#include <windows.h>
-#include <dos.h>
-#include <direct.h>
-#include <fcntl.h>
-#include <io.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#else
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/param.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
-////////////////////////////////////////////////////////////////////////////////
-// definitions of separators
-
-#ifdef MSWINDOWS
-  static const char* separator_set = "\\/";
-  static const char preferred_separator = '\\';
-#else
-  static const char* separator_set = "/";
-  static const char preferred_separator = '/';
-#endif
-
-  static bool is_separator (char ch)
-  {
-    for (int i = 0; separator_set[i]; i++)
-    {
-      if (separator_set[i] == ch)
-        return true;
-    }
-    return false;
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-// implement string comparison of paths - Unix is case-sensitive, Windoze is case-insensitive
-
-#ifdef MSWINDOWS
-
-  static std::string lowercase(const std::string& val)
-  {
-    std::string text = val;
-    for (unsigned i = 0; i < text.size(); i++)
-      text[i] = tolower(text[i]);
-    return text;
-  }
-
-#endif
-
-  bool path_compare(const std::string& l, const std::string& r)
-  {
-#ifdef MSWINDOWS
-    return lowercase(l) == lowercase(r);
-#else
-    return l == r;
-#endif
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-// Internal data structure used to hold the different parts of a filespec
-
-  class file_specification
-  {
-  private:
-    bool m_relative;                 // true = relative, false = absolute
-    std::string m_drive;             // drive - drive letter (e.g. "c:") or the path for an UNC (e.g. "\\somewhere")
-                                     //         empty if not known or on Unix
-    std::vector<std::string> m_path; // the subdirectory path to follow from the drive
-    std::string m_filename;          // the filename
-  public:
-    file_specification(void) : m_relative(false) {}
-    ~file_specification(void) {}
-
-    bool initialise_folder(const std::string& spec);
-    bool initialise_file(const std::string& spec);
-    bool simplify(void);
-    bool make_absolute(const std::string& root = folder_current_full());
-    bool make_absolute(const file_specification& root);
-    bool make_relative(const std::string& root = folder_current_full());
-    bool make_relative(const file_specification& root);
-    bool relative(void) const {return m_relative;}
-    bool absolute(void) const {return !relative();}
-    void set_relative(void) {m_relative = true;}
-    void set_absolute(void) {m_relative = false;}
-
-    const std::string& drive(void) const {return m_drive;}
-    std::string& drive(void) {return m_drive;}
-    void set_drive(const std::string& drive) {m_drive = drive;}
-
-    const std::vector<std::string>& path(void) const {return m_path;}
-    std::vector<std::string>& path(void) {return m_path;}
-    void set_path(const std::vector<std::string>& path) {m_path = path;}
-
-    void add_subpath(const std::string& subpath) {m_path.push_back(subpath);}
-    unsigned subpath_size(void) const {return m_path.size();}
-    const std::string& subpath_element(unsigned i) const {return m_path[i];}
-    void subpath_erase(unsigned i) {m_path.erase(m_path.begin()+i);}
-
-    const std::string& file(void) const {return m_filename;}
-    std::string& file(void) {return m_filename;}
-    void set_file(const std::string& file) {m_filename = file;}
-
-    std::string image(void) const;
-  };
-
-  bool file_specification::initialise_folder(const std::string& folder_spec)
-  {
-    std::string spec = folder_spec;
-    m_relative = true;
-    m_drive.erase();
-    m_path.clear();
-    m_filename.erase();
-    unsigned i = 0;
-#ifdef MSWINDOWS
-    // first split off the drive letter or UNC prefix on Windows
-    if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')
-    {
-      // found a drive letter
-      i = 2;
-      m_drive = spec.substr(0, 2);
-      m_relative = false;
-      // if there is a drive but no path or a relative path, get the current
-      // path for this drive and prepend it to the path
-      if (i == spec.size() || !is_separator(spec[i]))
-      {
-        // getdcwd requires the drive number (1..26) not the letter (A..Z)
-        char path [MAX_PATH+1];
-        int drivenum = toupper(m_drive[0]) - 'A' + 1;
-        if (_getdcwd(drivenum, path, MAX_PATH+1))
-        {
-          // the path includes the drive so we have the drive info twice
-          // need to prepend this absolute path to the spec such that any remaining relative path is still retained
-          if (!is_separator(path[strlen(path)-1])) spec.insert(2, 1, preferred_separator);
-          spec.insert(2, path+2);
-        }
-        else
-        {
-          // non-existent drive - fill in just the root directory
-          spec.insert(2, 1, preferred_separator);
-        }
-      }
-    }
-    else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))
-    {
-      // found an UNC prefix
-      i = 2;
-      // find the end of the prefix by scanning for the next seperator or the end of the spec
-      while (i < spec.size() && !is_separator(spec[i])) i++;
-      m_drive = spec.substr(0, i);
-      m_relative = false;
-    }
-#endif
-#ifdef CYGWIN
-    // first split off the drive letter or UNC prefix on Windows - the Cygwin environment supports these too
-    if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')
-    {
-      // found a drive letter
-      i = 2;
-      m_drive = spec.substr(0, 2);
-      m_relative = false;
-      // if there is a drive but no path or a relative path, get the current
-      // path for this drive and prepend it to the path
-      if (i == spec.size() || !is_separator(spec[i]))
-      {
-        // non-existent drive - fill in just the root directory
-        spec.insert(2, 1, preferred_separator);
-      }
-    }
-    else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))
-    {
-      // found an UNC prefix
-      i = 2;
-      // find the end of the prefix by scanning for the next seperator or the end of the spec
-      while (i < spec.size() && !is_separator(spec[i])) i++;
-      m_drive = spec.substr(0, i);
-      m_relative = false;
-    }
-#endif
-    // check whether the path is absolute or relative and discard the leading / if absolute
-    if (i < spec.size() && is_separator(spec[i]))
-    {
-      m_relative = false;
-      i++;
-#ifdef MSWINDOWS
-      // if there's no drive, fill it in on Windows since absolute paths must have a drive
-      if (m_drive.empty())
-      {
-        m_drive += (char)(_getdrive() - 1 + 'A');
-        m_drive += ':';
-      }
-#endif
-    }
-    // now extract the path elements - note that a trailing / is not significant since /a/b/c/ === /a/b/c
-    // also note that the leading / has been discarded - all paths are relative
-    // if absolute() is set, then paths are relative to the drive, else they are relative to the current path
-    unsigned start = i;
-    while(i <= spec.size())
-    {
-      if (i == spec.size())
-      {
-        // path element terminated by the end of the string
-        // discard this element if it is zero length because that represents the trailing /
-        if (i != start)
-          m_path.push_back(spec.substr(start, i-start));
-      }
-      else if (is_separator(spec[i]))
-      {
-        // path element terminated by a separator
-        m_path.push_back(spec.substr(start, i-start));
-        start = i+1;
-      }
-      i++;
-    }
-    // TODO - some error handling?
-    return true;
-  }
-
-  bool file_specification::initialise_file(const std::string& spec)
-  {
-    m_filename.erase();
-    // remove last element as the file and then treat the rest as a folder
-    unsigned i = spec.size();
-    while (--i)
-    {
-      if (is_separator(spec[i]))
-        break;
-#ifdef MSWINDOWS
-      // on windoze you can say a:fred.txt so the colon separates the path from the filename
-      else if (i == 1 && spec[i] == ':')
-        break;
-#endif
-    }
-    bool result = initialise_folder(spec.substr(0,i+1));
-    m_filename = spec.substr(i+1,spec.size()-i-1);
-    // TODO - some error handling?
-    return result;
-  }
-
-  bool file_specification::simplify(void)
-  {
-    // simplify the path by removing unnecessary . and .. entries - Note that zero-length entries are treated like .
-    for (unsigned i = 0; i < m_path.size(); )
-    {
-      if (m_path[i].empty() || m_path[i].compare(".") == 0)
-      {
-        // found . or null
-        // these both mean do nothing - so simply delete this element
-        m_path.erase(m_path.begin()+i);
-      }
-      else if (m_path[i].compare("..") == 0)
-      {
-        // found ..
-        if (i == 0 && !m_relative)
-        {
-          // up from the root does nothing so can be deleted
-          m_path.erase(m_path.begin()+i);
-          i++;
-        }
-        else if (i == 0 || m_path[i-1].compare("..") == 0)
-        {
-          // the first element of a relative path or the previous element is .. then keep it
-          i++;
-        }
-        else
-        {
-          // otherwise delete this element and the previous one
-          m_path.erase(m_path.begin()+i);
-          m_path.erase(m_path.begin()+i-1);
-          i--;
-        }
-      }
-      // keep all other elements
-      else
-        i++;
-    }
-    // TODO - error checking?
-    return true;
-  }
-
-  bool file_specification::make_absolute(const std::string& root)
-  {
-    // test whether already an absolute path in which case there's nothing to do
-    if (absolute()) return true;
-    // now simply call the other version of make_absolute
-    file_specification rootspec;
-    rootspec.initialise_folder(root);
-    return make_absolute(rootspec);
-  }
-
-  bool file_specification::make_absolute(const file_specification& rootspec)
-  {
-    // test whether already an absolute path in which case there's nothing to do
-    if (absolute()) return true;
-    // initialise the result with the root and make the root absolute
-    file_specification result = rootspec;
-    result.make_absolute();
-    // now append this's relative path and filename to the root's absolute path
-    for (unsigned i = 0; i < subpath_size(); i++)
-      result.add_subpath(subpath_element(i));
-    result.set_file(file());
-    // now the result is the absolute path, so transfer it to this
-    *this = result;
-    // and simplify to get rid of any unwanted .. or . elements
-    simplify();
-    return true;
-  }
-
-  bool file_specification::make_relative(const std::string& root)
-  {
-    // test whether already an relative path in which case there's nothing to do
-    if (relative()) return true;
-    // now simply call the other version of make_relative
-    file_specification rootspec;
-    rootspec.initialise_folder(root);
-    return make_relative(rootspec);
-  }
-
-  bool file_specification::make_relative(const file_specification& rootspec)
-  {
-    // test whether already an relative path in which case there's nothing to do
-    if (relative()) return true;
-    // initialise the result with the root and make the root absolute
-    file_specification absolute_root = rootspec;
-    absolute_root.make_absolute();
-    // now compare elements of the absolute root with elements of this to find the common path
-    // if the drives are different, no conversion can take place and the result must be absolute, else clear the drive
-    if (!path_compare(drive(), absolute_root.drive())) return true;
-    set_drive("");
-    // first remove leading elements that are identical to the corresponding element in root
-    unsigned i = 0;
-    while(subpath_size() > 0 && 
-          i < absolute_root.subpath_size() && 
-          path_compare(subpath_element(0), absolute_root.subpath_element(i)))
-    {
-      subpath_erase(0);
-      i++;
-    }
-    // now add a .. prefix for every element in root that is different from this
-    while (i < absolute_root.subpath_size())
-    {
-      m_path.insert(m_path.begin(), "..");
-      i++;
-    }
-    set_relative();
-    return true;
-  }
-
-  std::string file_specification::image(void) const
-  {
-    std::string result = m_drive;
-    if (absolute())
-      result += preferred_separator;
-    if (!m_path.empty())
-    {
-      for (unsigned i = 0; i < m_path.size(); i++)
-      {
-        if (i != 0) result += std::string(1,preferred_separator);
-        result += m_path[i];
-      }
-    }
-    else if (relative())
-      result += '.';
-    // add a trailing / to the last directory element
-    if (result.empty() || !is_separator(result[result.size()-1]))
-      result += preferred_separator;
-    if (!m_filename.empty())
-      result += m_filename;
-    return result;
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-// classifying functions
-
-#ifdef MSWINDOWS
-// file type tests are not defined for some reason on Windows despite them providing the stat() function!
-#define R_OK 4
-#define W_OK 2
-#endif
-
-  bool is_present (const std::string& thing)
-  {
-    // strip off any trailing separator because that will cause the stat function to fail
-    std::string path = thing;
-    if (!path.empty() && is_separator(path[path.size()-1]))
-      path.erase(path.size()-1,1);
-    // now test if this thing exists using the built-in stat function
-    struct stat buf;
-    return stat(path.c_str(), &buf) == 0;
-  }
-
-  bool is_folder (const std::string& thing)
-  {
-    // strip off any trailing separator because that will cause the stat function to fail
-    std::string path = thing;
-    if (!path.empty() && is_separator(path[path.size()-1]))
-      path.erase(path.size()-1,1);
-    // now test if this thing exists using the built-in stat function and if so, is it a folder
-    struct stat buf;
-    if (!(stat(path.c_str(), &buf) == 0)) {return false;}
-    return (buf.st_mode & S_IFDIR) != 0;
-  }
-
-  bool is_file (const std::string& thing)
-  {
-    // strip off any trailing separator because that will cause the stat function to fail
-    std::string path = thing;
-    if (!path.empty() && is_separator(path[path.size()-1]))
-      path.erase(path.size()-1,1);
-    // now test if this thing exists using the built-in stat function and if so, is it a file
-    struct stat buf;
-    if (!(stat(path.c_str(), &buf) == 0)) {return false;}
-    return (buf.st_mode & S_IFREG) != 0;
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-// file functions
-
-  bool file_exists (const std::string& filespec)
-  {
-    return is_file(filespec);
-  }
-
-  bool file_readable (const std::string& filespec)
-  {
-    // a file is readable if it exists and can be read
-    if (!file_exists(filespec)) return false;
-    return access(filespec.c_str(),R_OK)==0;
-  }
-
-  bool file_writable (const std::string& filespec)
-  {
-    // a file is writable if it exists as a file and is writable or if it doesn't exist but could be created and would be writable
-    if (is_present(filespec))
-    {
-      if (!is_file(filespec)) return false;
-      return access(filespec.c_str(),W_OK)==0;
-    }
-    std::string dir = folder_part(filespec);
-    if (dir.empty()) dir = ".";
-    return folder_writable(dir);
-  }
-
-  size_t file_size (const std::string& filespec)
-  {
-    struct stat buf;
-    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
-    return buf.st_size;
-  }
-
-  bool file_delete (const std::string& filespec)
-  {
-    if (!is_file(filespec)) return false;
-    return remove(filespec.c_str())==0;
-  }
-
-  bool file_rename (const std::string& old_filespec, const std::string& new_filespec)
-  {
-    if (!is_file(old_filespec)) return false;
-    return rename(old_filespec.c_str(), new_filespec.c_str())==0;
-  }
-
-  bool file_copy (const std::string& old_filespec, const std::string& new_filespec)
-  {
-    if (!is_file(old_filespec)) return false;
-    // do an exact copy - to do this, use binary mode
-    bool result = true;
-    FILE* old_file = fopen(old_filespec.c_str(),"rb");
-    FILE* new_file = fopen(new_filespec.c_str(),"wb");
-    if (!old_file)
-      result = false;
-    else if (!new_file)
-      result = false;
-    else
-    {
-      for (int byte = getc(old_file); byte != EOF; byte = getc(old_file))
-        putc(byte,new_file);
-    }
-    if (old_file) fclose(old_file);
-    if (new_file) fclose(new_file);
-    return result;
-  }
-
-  bool file_move (const std::string& old_filespec, const std::string& new_filespec)
-  {
-    // try to move the file by renaming - if that fails then do a copy and delete the original
-    if (file_rename(old_filespec, new_filespec))
-      return true;
-    if (!file_copy(old_filespec, new_filespec))
-      return false;
-    // I'm not sure what to do if the delete fails - is that an error?
-    // I've made it an error and then delete the copy so that the original state is recovered
-    if (file_delete(old_filespec))
-      return true;
-    file_delete(new_filespec);
-    return false;
-  }
-
-  time_t file_created (const std::string& filespec)
-  {
-    struct stat buf;
-    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
-    return buf.st_ctime;
-  }
-
-  time_t file_modified (const std::string& filespec)
-  {
-    struct stat buf;
-    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
-    return buf.st_mtime;
-  }
-
-  time_t file_accessed (const std::string& filespec)
-  {
-    struct stat buf;
-    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
-    return buf.st_atime;
-  }
-
-  std::string create_filespec (const std::string& directory, const std::string& filename)
-  {
-    std::string result = directory;
-    // if directory is empty then no directory part will be added
-    // add trailing slash if the directory was specified and does not have a trailing slash
-    if (!result.empty() && !is_separator(result[result.size()-1]))
-      result += preferred_separator;
-    // if filename is null or empty, nothing will be added so the path is then a directory path
-    result += filename;
-    return result;
-  }
-
-  std::string create_filespec (const std::string& directory, const std::string& basename, const std::string& extension)
-  {
-    return create_filespec(directory, create_filename(basename, extension));
-  }
-
-  std::string create_filename(const std::string& basename, const std::string& extension)
-  {
-    std::string name = basename;
-    // extension is optional - so the dot is also optional
-    if (!extension.empty())
-    {
-      if (extension[0] != '.') name += '.';
-      name += extension;
-    }
-    return name;
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-// folder functions
-
-  bool folder_create (const std::string& directory)
-  {
-#ifdef MSWINDOWS
-    return mkdir(directory.c_str()) == 0;
-#else
-    return mkdir(directory.c_str(), 0777) == 0;
-#endif
-  }
-
-  bool folder_exists (const std::string& directory)
-  {
-    return is_folder(directory);
-  }
-
-  bool folder_readable (const std::string& directory)
-  {
-    // a folder is readable if it exists and has read access
-    std::string dir = directory;
-    if (dir.empty()) dir = ".";
-    if (!folder_exists(dir)) return false;
-    return access(dir.c_str(),R_OK)==0;
-  }
-
-  bool folder_writable (const std::string& directory)
-  {
-    // a folder is writable if it exists and has write access
-    std::string dir = directory;
-    if (dir.empty()) dir = ".";
-    if (!folder_exists(dir)) return false;
-    return access(dir.c_str(),W_OK)==0;
-  }
-
-  bool folder_delete (const std::string& directory, bool recurse)
-  {
-    std::string dir = directory;
-    if (dir.empty()) dir = ".";
-    if (!folder_exists(dir)) return false;
-    bool result = true;
-    // depth-first traversal ensures that directory contents are deleted before trying to delete the directory itself
-    if (recurse)
-    {
-      std::vector<std::string> subdirectories = folder_subdirectories(dir);
-      for (std::vector<std::string>::size_type d = 0; d < subdirectories.size(); ++d)
-        if (!folder_delete(folder_down(dir,subdirectories[d]),true)) 
-          result = false;
-      std::vector<std::string> files = folder_files(dir);
-      for (std::vector<std::string>::size_type f = 0; f < files.size(); ++f)
-        if (!file_delete(create_filespec(dir, files[f]))) 
-          result = false;
-    }
-    if (rmdir(dir.c_str())!=0) result = false;
-    return result;
-  }
-
-  bool folder_rename (const std::string& old_directory, const std::string& new_directory)
-  {
-    if (!folder_exists(old_directory)) return false;
-    return rename(old_directory.c_str(), new_directory.c_str())==0;
-  }
-
-  bool folder_empty(const std::string& directory)
-  {
-    std::string dir = directory.empty() ? std::string(".") : directory;
-    bool result = true;
-#ifdef MSWINDOWS
-    std::string wildcard = create_filespec(dir, "*.*");
-    long handle = -1;
-    _finddata_t fileinfo;
-    for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))
-    {
-      std::string strentry = fileinfo.name;
-      if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
-      {
-        result = false;
-        break;
-      }
-    }
-    _findclose(handle);
-#else
-    DIR* d = opendir(dir.c_str());
-    if (d)
-    {
-      for (dirent* entry = readdir(d); entry; entry = readdir(d))
-      {
-        std::string strentry = entry->d_name;
-        if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
-        {
-          result = false;
-          break;
-        }
-      }
-      closedir(d);
-    }
-#endif
-    return result;
-  }
-
-  bool folder_set_current(const std::string& folder)
-  {
-    if (!folder_exists(folder))
-      return false;
-#ifdef MSWINDOWS
-    // Windose implementation - this returns non-zero for success
-    return (SetCurrentDirectoryA(folder.c_str()) != 0);
-#else
-    // Unix implementation - this returns zero for success
-    return (chdir(folder.c_str()) == 0);
-#endif
-  }
-
-  std::string folder_current (void)
-  {
-    return ".";
-  }
-
-  std::string folder_current_full(void)
-  {
-    // It's not clear from the documentation whether the buffer for a path should be one byte longer
-    // than the maximum path length to allow for the null termination, so I have made it so anyway
-#ifdef MSWINDOWS
-    char abspath [MAX_PATH+1];
-    return std::string(_fullpath(abspath, ".", MAX_PATH+1));
-#else
-    char pathname [MAXPATHLEN+1];
-    getcwd(pathname,MAXPATHLEN+1);
-    return std::string(pathname);
-#endif
-  }
-
-  std::string folder_down (const std::string& directory, const std::string& subdirectory)
-  {
-    file_specification spec;
-    spec.initialise_folder(directory);
-    spec.add_subpath(subdirectory);
-    return spec.image();
-  }
-
-  std::string folder_up (const std::string& directory, unsigned levels)
-  {
-    file_specification spec;
-    spec.initialise_folder(directory);
-    for (unsigned i = 0; i < levels; i++)
-      spec.add_subpath("..");
-    spec.simplify();
-    return spec.image();
-  }
-
-  std::vector<std::string> folder_subdirectories (const std::string& directory)
-  {
-    return folder_wildcard(directory, "*", true, false);
-  }
-
-  std::vector<std::string> folder_files (const std::string& directory)
-  {
-    return folder_wildcard(directory, "*", false, true);
-  }
-
-  std::vector<std::string> folder_all(const std::string& directory)
-  {
-    return folder_wildcard(directory, "*", true, true);
-  }
-
-  std::vector<std::string> folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files)
-  {
-    std::string dir = directory.empty() ? std::string(".") : directory;
-    std::vector<std::string> results;
-#ifdef MSWINDOWS
-    std::string wildcard = create_filespec(dir, wild);
-    long handle = -1;
-    _finddata_t fileinfo;
-    for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))
-    {
-      std::string strentry = fileinfo.name;
-      if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
-        if ((subdirs && (fileinfo.attrib & _A_SUBDIR)) || (files && !(fileinfo.attrib & _A_SUBDIR)))
-          results.push_back(strentry);
-    }
-    _findclose(handle);
-#else
-    DIR* d = opendir(dir.c_str());
-    if (d)
-    {
-      for (dirent* entry = readdir(d); entry; entry = readdir(d))
-      {
-        std::string strentry = entry->d_name;
-        if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
-        {
-          std::string subpath = create_filespec(dir, strentry);
-          if (((subdirs && is_folder(subpath)) || (files && is_file(subpath))) && (wildcard(wild, strentry)))
-            results.push_back(strentry);
-        }
-      }
-      closedir(d);
-    }
-#endif
-    return results;
-  }
-
-  std::string folder_home (void)
-  {
-    if (getenv("HOME"))
-      return std::string(getenv("HOME"));
-#ifdef MSWINDOWS
-    if (getenv("HOMEDRIVE") || getenv("HOMEPATH"))
-      return std::string(getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH"));
-    return "C:\\";
-#else
-    if (getenv("USER"))
-      return folder_down("/home", std::string(getenv("USER")));
-    if (getenv("USERNAME"))
-      return folder_down("/home", std::string(getenv("USERNAME")));
-    return "";
-#endif
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-// path functions convert between full and relative paths
-
-  bool is_full_path(const std::string& path)
-  {
-    file_specification spec;
-    spec.initialise_folder(path.empty() ? std::string(".") : path);
-    return spec.absolute();
-  }
-
-  bool is_relative_path(const std::string& path)
-  {
-    file_specification spec;
-    spec.initialise_folder(path.empty() ? std::string(".") : path);
-    return spec.relative();
-  }
-
-  static std::string full_path(const std::string& root, const std::string& path)
-  {
-    // convert path to a full path using root as the start point for relative paths
-    // decompose the path and test whether it is already an absolute path, in which case just return it
-    file_specification spec;
-    spec.initialise_folder(path.empty() ? std::string(".") : path);
-    if (spec.absolute()) return spec.image();
-    // okay, so the path is relative after all, so we need to combine it with the root path
-    // decompose the root path and check whether it is relative
-    file_specification rootspec;
-    rootspec.initialise_folder(root.empty() ? std::string(".") : root);
-    if (rootspec.relative())
-      rootspec.make_absolute();
-    // Now do the conversion of the path relative to the root
-    spec.make_absolute(rootspec);
-    return spec.image();
-  }
-
-  static std::string relative_path(const std::string& root, const std::string& path)
-  {
-    // convert path to a relative path, using the root path as its starting point
-    // first convert both paths to full paths relative to CWD
-    file_specification rootspec;
-    rootspec.initialise_folder(root.empty() ? std::string(".") : root);
-    if (rootspec.relative())
-      rootspec.make_absolute();
-    file_specification spec;
-    spec.initialise_folder(path.empty() ? std::string(".") : path);
-    if (spec.relative())
-      spec.make_absolute();
-    // now make path spec relative to the root spec
-    spec.make_relative(rootspec);
-    return spec.image();
-  }
-
-  std::string folder_to_path (const std::string& path, const std::string& directory)
-  {
-    return full_path(path, directory);
-  }
-
-  std::string filespec_to_path (const std::string& path, const std::string& spec)
-  {
-    return create_filespec(folder_to_path(path, folder_part(spec)),filename_part(spec));
-  }
-
-  std::string folder_to_path(const std::string& folder)
-  {
-    return folder_to_path(folder_current(), folder);
-  }
-
-  std::string filespec_to_path(const std::string& filespec)
-  {
-    return filespec_to_path(folder_current(), filespec);
-  }
-
-  std::string folder_to_relative_path(const std::string& root, const std::string& folder)
-  {
-    return relative_path(root, folder);
-  }
-
-  std::string filespec_to_relative_path(const std::string& root, const std::string& spec)
-  {
-    return create_filespec(folder_to_relative_path(root, folder_part(spec)),filename_part(spec));
-  }
-
-  std::string folder_to_relative_path(const std::string& folder)
-  {
-    return folder_to_relative_path(folder_current(), folder);
-  }
-
-  std::string filespec_to_relative_path(const std::string& filespec)
-  {
-    return filespec_to_relative_path(folder_current(), filespec);
-  }
-
-  std::string folder_append_separator(const std::string& folder)
-  {
-    std::string result = folder;
-    if (result.empty() || !is_separator(result[result.size()-1]))
-      result += preferred_separator;
-    return result;
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-
-  std::string basename_part (const std::string& spec)
-  {
-    std::string fname = filename_part(spec);
-    // scan back through filename until a '.' is found and remove suffix
-    // the whole filename is the basename if there is no '.'
-    std::string::size_type i = fname.find_last_of('.');
-    // observe Unix convention that a dot at the start of a filename is part of the basename, not the extension
-    if (i != 0 && i != std::string::npos)
-      fname.erase(i, fname.size()-i);
-    return fname;
-  }
-
-  std::string filename_part (const std::string& spec)
-  {
-    // scan back through filename until a preferred_separator is found and remove prefix;
-    // if there is no preferred_separator then remove nothing, i.e. the whole filespec is filename
-    unsigned i = spec.size();
-    while (i--)
-    {
-      if (is_separator(spec[i]))
-        return spec.substr(i+1,spec.size()-i-1);
-    }
-    return spec;
-  }
-
-  std::string extension_part (const std::string& spec)
-  {
-    std::string fname = filename_part(spec);
-    // scan back through filename until a '.' is found and remove prefix;
-    std::string::size_type i = fname.find_last_of('.');
-    // observe Unix convention that a dot at the start of a filename is part of the name, not the extension;
-    if (i != 0 && i != std::string::npos)
-      fname.erase(0, i+1);
-    else
-      fname.erase();
-    return fname;
-  }
-
-  std::string folder_part (const std::string& spec)
-  {
-    // scan back through filename until a separator is found and remove prefix
-    // if there is no separator, remove the whole
-    unsigned i = spec.size();
-    while (i--)
-    {
-      if (is_separator(spec[i]))
-        return spec.substr(0,i);
-    }
-    return std::string();
-  }
-
-  std::vector<std::string> filespec_elements(const std::string& filespec)
-  {
-    file_specification spec;
-    spec.initialise_file(filespec);
-    std::vector<std::string> result = spec.path();
-    if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());
-    if (!spec.file().empty()) result.push_back(spec.file());
-    return result;
-  }
-
-  std::vector<std::string> folder_elements(const std::string& folder)
-  {
-    file_specification spec;
-    spec.initialise_folder(folder);
-    std::vector<std::string> result = spec.path();
-    if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());
-    return result;
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-// mimic the command lookup used by the shell
-
-// Windows looks at the following locations:
-// 1) application root
-// 2) current directory
-// 3) 32-bit system directory
-// 4) 16-bit system directory
-// 5) windows system directory
-// 6) %path%
-// currently only (2) and (6) has been implemented although many system folders are on the path anyway
-// also implement the implied .exe extension on commands with no path (see CreateProcess documentation)
-// TODO - PATHEXT handling to find non-exe executables
-
-  std::string path_lookup (const std::string& command)
-  {
-    std::string path = std::string(".") + PATH_SPLITTER + getenv("PATH");
-    return lookup(command, path);
-  }
-
-  std::string lookup (const std::string& command, const std::string& path, const std::string& splitter)
-  {
-    // first check whether the command is already a path and check whether it exists
-    if (!folder_part(command).empty())
-    {
-      if (file_exists(command))
-        return command;
-    }
-    else
-    {
-      // command is just a name - so do path lookup
-      // split the path into its elements
-      std::vector<std::string> paths;
-      if (!path.empty())
-      {
-        for(std::string::size_type offset = 0;;)
-        {
-          std::string::size_type found = path.find(splitter, offset);
-          if (found != std::string::npos)
-          {
-            paths.push_back(path.substr(offset, found-offset));
-            offset = found + splitter.size();
-          }
-          else
-          {
-            paths.push_back(path.substr(offset, path.size()-offset));
-            break;
-          }
-        }
-      }
-      // now lookup each path to see if it its the matching one
-      for (unsigned i = 0; i < paths.size(); i++)
-      {
-        std::string spec = create_filespec(paths[i], command);
-        if (file_exists(spec))
-        {
-          return spec;
-        }
-      }
-    }
-#ifdef MSWINDOWS
-    // if there is no extension, try recursing on each possible extension
-    // TODO iterate through PATHEXT
-    if (extension_part(command).empty())
-      return lookup(create_filespec(folder_part(command), basename_part(command), "exe"), path, splitter);
-#endif
-    // if path lookup failed, return empty string to indicate error
-    return std::string();
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-
-  std::string install_path(const std::string& argv0)
-  {
-    std::string bin_directory = folder_part(argv0);
-    if (bin_directory.empty())
-    {
-      // do path lookup to find the executable path
-      bin_directory = folder_part(path_lookup(argv0));
-    }
-    return bin_directory;
-  }
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004 onwards\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   This is a portable interface to the file system.\r
+\r
+//   The idea is that you write all file system access code using these functions,\r
+//   which are ported to all platforms that we are interested in. Therefore your\r
+//   code is inherently portable.\r
+\r
+//   Native Windows version: switched on by macro _WIN32 which is defined by VC++/Borland/Mingw compilers\r
+//   Unix/Gnu version:   default variant, no compiler directives are required but _WIN32 must be absent\r
+//   Cygwin/Gnu version: as Unix version but with additional support for Windows drive letters\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "file_system.hpp"\r
+#include "wildcard.hpp"\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <time.h>\r
+#include <algorithm>\r
+#include <ctype.h>\r
+\r
+#ifdef MSWINDOWS\r
+#include <windows.h>\r
+#include <dos.h>\r
+#include <direct.h>\r
+#include <fcntl.h>\r
+#include <io.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#else\r
+#include <dirent.h>\r
+#include <fcntl.h>\r
+#include <sys/param.h>\r
+#include <unistd.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// definitions of separators\r
+\r
+#ifdef MSWINDOWS\r
+  static const char* separator_set = "\\/";\r
+  static const char preferred_separator = '\\';\r
+#else\r
+  static const char* separator_set = "/";\r
+  static const char preferred_separator = '/';\r
+#endif\r
+\r
+  static bool is_separator (char ch)\r
+  {\r
+    for (int i = 0; separator_set[i]; i++)\r
+    {\r
+      if (separator_set[i] == ch)\r
+        return true;\r
+    }\r
+    return false;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// implement string comparison of paths - Unix is case-sensitive, Windoze is case-insensitive\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  static std::string lowercase(const std::string& val)\r
+  {\r
+    std::string text = val;\r
+    for (unsigned i = 0; i < text.size(); i++)\r
+      text[i] = tolower(text[i]);\r
+    return text;\r
+  }\r
+\r
+#endif\r
+\r
+  bool path_compare(const std::string& l, const std::string& r)\r
+  {\r
+#ifdef MSWINDOWS\r
+    return lowercase(l) == lowercase(r);\r
+#else\r
+    return l == r;\r
+#endif\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Internal data structure used to hold the different parts of a filespec\r
+\r
+  class file_specification\r
+  {\r
+  private:\r
+    bool m_relative;                 // true = relative, false = absolute\r
+    std::string m_drive;             // drive - drive letter (e.g. "c:") or the path for an UNC (e.g. "\\somewhere")\r
+                                     //         empty if not known or on Unix\r
+    std::vector<std::string> m_path; // the subdirectory path to follow from the drive\r
+    std::string m_filename;          // the filename\r
+  public:\r
+    file_specification(void) : m_relative(false) {}\r
+    ~file_specification(void) {}\r
+\r
+    bool initialise_folder(const std::string& spec);\r
+    bool initialise_file(const std::string& spec);\r
+    bool simplify(void);\r
+    bool make_absolute(const std::string& root = folder_current_full());\r
+    bool make_absolute(const file_specification& root);\r
+    bool make_relative(const std::string& root = folder_current_full());\r
+    bool make_relative(const file_specification& root);\r
+    bool relative(void) const {return m_relative;}\r
+    bool absolute(void) const {return !relative();}\r
+    void set_relative(void) {m_relative = true;}\r
+    void set_absolute(void) {m_relative = false;}\r
+\r
+    const std::string& drive(void) const {return m_drive;}\r
+    std::string& drive(void) {return m_drive;}\r
+    void set_drive(const std::string& drive) {m_drive = drive;}\r
+\r
+    const std::vector<std::string>& path(void) const {return m_path;}\r
+    std::vector<std::string>& path(void) {return m_path;}\r
+    void set_path(const std::vector<std::string>& path) {m_path = path;}\r
+\r
+    void add_subpath(const std::string& subpath) {m_path.push_back(subpath);}\r
+    unsigned subpath_size(void) const {return m_path.size();}\r
+    const std::string& subpath_element(unsigned i) const {return m_path[i];}\r
+    void subpath_erase(unsigned i) {m_path.erase(m_path.begin()+i);}\r
+\r
+    const std::string& file(void) const {return m_filename;}\r
+    std::string& file(void) {return m_filename;}\r
+    void set_file(const std::string& file) {m_filename = file;}\r
+\r
+    std::string image(void) const;\r
+  };\r
+\r
+  bool file_specification::initialise_folder(const std::string& folder_spec)\r
+  {\r
+    std::string spec = folder_spec;\r
+    m_relative = true;\r
+    m_drive.erase();\r
+    m_path.clear();\r
+    m_filename.erase();\r
+    unsigned i = 0;\r
+#ifdef MSWINDOWS\r
+    // first split off the drive letter or UNC prefix on Windows\r
+    if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')\r
+    {\r
+      // found a drive letter\r
+      i = 2;\r
+      m_drive = spec.substr(0, 2);\r
+      m_relative = false;\r
+      // if there is a drive but no path or a relative path, get the current\r
+      // path for this drive and prepend it to the path\r
+      if (i == spec.size() || !is_separator(spec[i]))\r
+      {\r
+        // getdcwd requires the drive number (1..26) not the letter (A..Z)\r
+        char path [MAX_PATH+1];\r
+        int drivenum = toupper(m_drive[0]) - 'A' + 1;\r
+        if (_getdcwd(drivenum, path, MAX_PATH+1))\r
+        {\r
+          // the path includes the drive so we have the drive info twice\r
+          // need to prepend this absolute path to the spec such that any remaining relative path is still retained\r
+          if (!is_separator(path[strlen(path)-1])) spec.insert(2, 1, preferred_separator);\r
+          spec.insert(2, path+2);\r
+        }\r
+        else\r
+        {\r
+          // non-existent drive - fill in just the root directory\r
+          spec.insert(2, 1, preferred_separator);\r
+        }\r
+      }\r
+    }\r
+    else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))\r
+    {\r
+      // found an UNC prefix\r
+      i = 2;\r
+      // find the end of the prefix by scanning for the next seperator or the end of the spec\r
+      while (i < spec.size() && !is_separator(spec[i])) i++;\r
+      m_drive = spec.substr(0, i);\r
+      m_relative = false;\r
+    }\r
+#endif\r
+#ifdef CYGWIN\r
+    // first split off the drive letter or UNC prefix on Windows - the Cygwin environment supports these too\r
+    if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')\r
+    {\r
+      // found a drive letter\r
+      i = 2;\r
+      m_drive = spec.substr(0, 2);\r
+      m_relative = false;\r
+      // if there is a drive but no path or a relative path, get the current\r
+      // path for this drive and prepend it to the path\r
+      if (i == spec.size() || !is_separator(spec[i]))\r
+      {\r
+        // non-existent drive - fill in just the root directory\r
+        spec.insert(2, 1, preferred_separator);\r
+      }\r
+    }\r
+    else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))\r
+    {\r
+      // found an UNC prefix\r
+      i = 2;\r
+      // find the end of the prefix by scanning for the next seperator or the end of the spec\r
+      while (i < spec.size() && !is_separator(spec[i])) i++;\r
+      m_drive = spec.substr(0, i);\r
+      m_relative = false;\r
+    }\r
+#endif\r
+    // check whether the path is absolute or relative and discard the leading / if absolute\r
+    if (i < spec.size() && is_separator(spec[i]))\r
+    {\r
+      m_relative = false;\r
+      i++;\r
+#ifdef MSWINDOWS\r
+      // if there's no drive, fill it in on Windows since absolute paths must have a drive\r
+      if (m_drive.empty())\r
+      {\r
+        m_drive += (char)(_getdrive() - 1 + 'A');\r
+        m_drive += ':';\r
+      }\r
+#endif\r
+    }\r
+    // now extract the path elements - note that a trailing / is not significant since /a/b/c/ === /a/b/c\r
+    // also note that the leading / has been discarded - all paths are relative\r
+    // if absolute() is set, then paths are relative to the drive, else they are relative to the current path\r
+    unsigned start = i;\r
+    while(i <= spec.size())\r
+    {\r
+      if (i == spec.size())\r
+      {\r
+        // path element terminated by the end of the string\r
+        // discard this element if it is zero length because that represents the trailing /\r
+        if (i != start)\r
+          m_path.push_back(spec.substr(start, i-start));\r
+      }\r
+      else if (is_separator(spec[i]))\r
+      {\r
+        // path element terminated by a separator\r
+        m_path.push_back(spec.substr(start, i-start));\r
+        start = i+1;\r
+      }\r
+      i++;\r
+    }\r
+    // TODO - some error handling?\r
+    return true;\r
+  }\r
+\r
+  bool file_specification::initialise_file(const std::string& spec)\r
+  {\r
+    m_filename.erase();\r
+    // remove last element as the file and then treat the rest as a folder\r
+    unsigned i = spec.size();\r
+    while (--i)\r
+    {\r
+      if (is_separator(spec[i]))\r
+        break;\r
+#ifdef MSWINDOWS\r
+      // on windoze you can say a:fred.txt so the colon separates the path from the filename\r
+      else if (i == 1 && spec[i] == ':')\r
+        break;\r
+#endif\r
+    }\r
+    bool result = initialise_folder(spec.substr(0,i+1));\r
+    m_filename = spec.substr(i+1,spec.size()-i-1);\r
+    // TODO - some error handling?\r
+    return result;\r
+  }\r
+\r
+  bool file_specification::simplify(void)\r
+  {\r
+    // simplify the path by removing unnecessary . and .. entries - Note that zero-length entries are treated like .\r
+    for (unsigned i = 0; i < m_path.size(); )\r
+    {\r
+      if (m_path[i].empty() || m_path[i].compare(".") == 0)\r
+      {\r
+        // found . or null\r
+        // these both mean do nothing - so simply delete this element\r
+        m_path.erase(m_path.begin()+i);\r
+      }\r
+      else if (m_path[i].compare("..") == 0)\r
+      {\r
+        // found ..\r
+        if (i == 0 && !m_relative)\r
+        {\r
+          // up from the root does nothing so can be deleted\r
+          m_path.erase(m_path.begin()+i);\r
+          i++;\r
+        }\r
+        else if (i == 0 || m_path[i-1].compare("..") == 0)\r
+        {\r
+          // the first element of a relative path or the previous element is .. then keep it\r
+          i++;\r
+        }\r
+        else\r
+        {\r
+          // otherwise delete this element and the previous one\r
+          m_path.erase(m_path.begin()+i);\r
+          m_path.erase(m_path.begin()+i-1);\r
+          i--;\r
+        }\r
+      }\r
+      // keep all other elements\r
+      else\r
+        i++;\r
+    }\r
+    // TODO - error checking?\r
+    return true;\r
+  }\r
+\r
+  bool file_specification::make_absolute(const std::string& root)\r
+  {\r
+    // test whether already an absolute path in which case there's nothing to do\r
+    if (absolute()) return true;\r
+    // now simply call the other version of make_absolute\r
+    file_specification rootspec;\r
+    rootspec.initialise_folder(root);\r
+    return make_absolute(rootspec);\r
+  }\r
+\r
+  bool file_specification::make_absolute(const file_specification& rootspec)\r
+  {\r
+    // test whether already an absolute path in which case there's nothing to do\r
+    if (absolute()) return true;\r
+    // initialise the result with the root and make the root absolute\r
+    file_specification result = rootspec;\r
+    result.make_absolute();\r
+    // now append this's relative path and filename to the root's absolute path\r
+    for (unsigned i = 0; i < subpath_size(); i++)\r
+      result.add_subpath(subpath_element(i));\r
+    result.set_file(file());\r
+    // now the result is the absolute path, so transfer it to this\r
+    *this = result;\r
+    // and simplify to get rid of any unwanted .. or . elements\r
+    simplify();\r
+    return true;\r
+  }\r
+\r
+  bool file_specification::make_relative(const std::string& root)\r
+  {\r
+    // test whether already an relative path in which case there's nothing to do\r
+    if (relative()) return true;\r
+    // now simply call the other version of make_relative\r
+    file_specification rootspec;\r
+    rootspec.initialise_folder(root);\r
+    return make_relative(rootspec);\r
+  }\r
+\r
+  bool file_specification::make_relative(const file_specification& rootspec)\r
+  {\r
+    // test whether already an relative path in which case there's nothing to do\r
+    if (relative()) return true;\r
+    // initialise the result with the root and make the root absolute\r
+    file_specification absolute_root = rootspec;\r
+    absolute_root.make_absolute();\r
+    // now compare elements of the absolute root with elements of this to find the common path\r
+    // if the drives are different, no conversion can take place and the result must be absolute, else clear the drive\r
+    if (!path_compare(drive(), absolute_root.drive())) return true;\r
+    set_drive("");\r
+    // first remove leading elements that are identical to the corresponding element in root\r
+    unsigned i = 0;\r
+    while(subpath_size() > 0 && \r
+          i < absolute_root.subpath_size() && \r
+          path_compare(subpath_element(0), absolute_root.subpath_element(i)))\r
+    {\r
+      subpath_erase(0);\r
+      i++;\r
+    }\r
+    // now add a .. prefix for every element in root that is different from this\r
+    while (i < absolute_root.subpath_size())\r
+    {\r
+      m_path.insert(m_path.begin(), "..");\r
+      i++;\r
+    }\r
+    set_relative();\r
+    return true;\r
+  }\r
+\r
+  std::string file_specification::image(void) const\r
+  {\r
+    std::string result = m_drive;\r
+    if (absolute())\r
+      result += preferred_separator;\r
+    if (!m_path.empty())\r
+    {\r
+      for (unsigned i = 0; i < m_path.size(); i++)\r
+      {\r
+        if (i != 0) result += std::string(1,preferred_separator);\r
+        result += m_path[i];\r
+      }\r
+    }\r
+    else if (relative())\r
+      result += '.';\r
+    // add a trailing / to the last directory element\r
+    if (result.empty() || !is_separator(result[result.size()-1]))\r
+      result += preferred_separator;\r
+    if (!m_filename.empty())\r
+      result += m_filename;\r
+    return result;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// classifying functions\r
+\r
+// Under both Windows and Unix, the stat function is used for classification\r
+\r
+// Under Linux, the following classifications are defined\r
+// source: Linux man page for stat(2) http://linux.die.net/man/2/stat\r
+//   S_IFMT    0170000 bitmask for the file type bitfields\r
+//   S_IFSOCK  0140000 socket (Note this overlaps with S_IFDIR)\r
+//   S_IFLNK   0120000 symbolic link\r
+//   S_IFREG   0100000 regular file\r
+//   S_IFBLK   0060000 block device\r
+//   S_IFDIR   0040000 directory\r
+//   S_IFCHR   0020000 character device\r
+//   S_IFIFO   0010000 FIFO\r
+// There are also some Posix-standard macros:\r
+//   S_ISREG(m)        is it a regular file? \r
+//   S_ISDIR(m)        directory? \r
+//   S_ISCHR(m)        character device? \r
+//   S_ISBLK(m)        block device? \r
+//   S_ISFIFO(m)       FIFO (named pipe)? \r
+//   S_ISLNK(m)        symbolic link? (Not in POSIX.1-1996.) \r
+//   S_ISSOCK(m)       socket? (Not in POSIX.1-1996.)\r
+// Under Windows, the following are defined:\r
+// source: Header file sys/stat.h distributed with Visual Studio 10\r
+//   _S_IFMT  (S_IFMT)   0xF000 file type mask\r
+//   _S_IFREG (S_IFREG)  0x8000 regular\r
+//   _S_IFDIR (S_IFDIR)  0x4000 directory\r
+//   _S_IFCHR (S_IFCHR)  0x2000 character special\r
+//   _S_IFIFO            0x1000 pipe\r
+\r
+#ifdef MSWINDOWS\r
+// file type tests are not defined for some reason on Windows despite them providing the stat() function!\r
+#define R_OK 4\r
+#define W_OK 2\r
+// Posix-style macros for Windows\r
+#ifndef S_ISREG\r
+#define S_ISREG(mode)  ((mode & _S_IFMT) == _S_IFREG)\r
+#endif\r
+#ifndef S_ISDIR\r
+#define S_ISDIR(mode)  ((mode & _S_IFMT) == _S_IFDIR)\r
+#endif\r
+#ifndef S_ISCHR\r
+#define S_ISCHR(mode)  ((mode & _S_IFMT) == _S_IFCHR)\r
+#endif\r
+#ifndef S_ISBLK\r
+#define S_ISBLK(mode)  (false)\r
+#endif\r
+#ifndef S_ISFIFO\r
+#define S_ISFIFO(mode) ((mode & _S_IFMT) == _S_IFIFO)\r
+#endif\r
+#ifndef S_ISLNK\r
+#define S_ISLNK(mode)  (false)\r
+#endif\r
+#ifndef S_ISSOCK\r
+#define S_ISSOCK(mode) (false)\r
+#endif\r
+#endif\r
+\r
+  bool is_present (const std::string& thing)\r
+  {\r
+    // strip off any trailing separator because that will cause the stat function to fail\r
+    std::string path = thing;\r
+    if (!path.empty() && is_separator(path[path.size()-1]))\r
+      path.erase(path.size()-1,1);\r
+    // now test if this thing exists using the built-in stat function\r
+    struct stat buf;\r
+    return stat(path.c_str(), &buf) == 0;\r
+  }\r
+\r
+  bool is_folder (const std::string& thing)\r
+  {\r
+    // strip off any trailing separator because that will cause the stat function to fail\r
+    std::string path = thing;\r
+    if (!path.empty() && is_separator(path[path.size()-1]))\r
+      path.erase(path.size()-1,1);\r
+    // now test if this thing exists using the built-in stat function and if so, is it a folder\r
+    struct stat buf;\r
+    if (!(stat(path.c_str(), &buf) == 0))\r
+      return false;\r
+    // If the object is present, see if it is a directory\r
+    // this is the Posix-approved way of testing\r
+    return S_ISDIR(buf.st_mode);\r
+  }\r
+\r
+  bool is_file (const std::string& thing)\r
+  {\r
+    // strip off any trailing separator because that will cause the stat function to fail\r
+    std::string path = thing;\r
+    if (!path.empty() && is_separator(path[path.size()-1]))\r
+      path.erase(path.size()-1,1);\r
+    // now test if this thing exists using the built-in stat function and if so, is it a file\r
+    struct stat buf;\r
+    if (!(stat(path.c_str(), &buf) == 0))\r
+      return false;\r
+    // If the object is present, see if it is a file or file-like object\r
+    // Note that devices are neither folders nor files\r
+    // this is the Posix-approved way of testing\r
+    return S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode) || S_ISSOCK(buf.st_mode) || S_ISFIFO(buf.st_mode);\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// file functions\r
+\r
+  bool file_exists (const std::string& filespec)\r
+  {\r
+    return is_file(filespec);\r
+  }\r
+\r
+  bool file_readable (const std::string& filespec)\r
+  {\r
+    // a file is readable if it exists and can be read\r
+    if (!file_exists(filespec)) return false;\r
+    return access(filespec.c_str(),R_OK)==0;\r
+  }\r
+\r
+  bool file_writable (const std::string& filespec)\r
+  {\r
+    // a file is writable if it exists as a file and is writable or if\r
+    // it doesn't exist but could be created and would be writable\r
+    if (is_present(filespec))\r
+    {\r
+      if (!is_file(filespec)) return false;\r
+      return access(filespec.c_str(),W_OK)==0;\r
+    }\r
+    std::string dir = folder_part(filespec);\r
+    if (dir.empty()) dir = ".";\r
+    return folder_writable(dir);\r
+  }\r
+\r
+  size_t file_size (const std::string& filespec)\r
+  {\r
+    struct stat buf;\r
+    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+    return buf.st_size;\r
+  }\r
+\r
+  bool file_delete (const std::string& filespec)\r
+  {\r
+    if (!is_file(filespec)) return false;\r
+    return remove(filespec.c_str())==0;\r
+  }\r
+\r
+  bool file_rename (const std::string& old_filespec, const std::string& new_filespec)\r
+  {\r
+    if (!is_file(old_filespec)) return false;\r
+    return rename(old_filespec.c_str(), new_filespec.c_str())==0;\r
+  }\r
+\r
+  bool file_copy (const std::string& old_filespec, const std::string& new_filespec)\r
+  {\r
+    if (!is_file(old_filespec)) return false;\r
+    // do an exact copy - to do this, use binary mode\r
+    bool result = true;\r
+    FILE* old_file = fopen(old_filespec.c_str(),"rb");\r
+    FILE* new_file = fopen(new_filespec.c_str(),"wb");\r
+    if (!old_file)\r
+      result = false;\r
+    else if (!new_file)\r
+      result = false;\r
+    else\r
+    {\r
+      for (int byte = getc(old_file); byte != EOF; byte = getc(old_file))\r
+        putc(byte,new_file);\r
+    }\r
+    if (old_file) fclose(old_file);\r
+    if (new_file) fclose(new_file);\r
+    return result;\r
+  }\r
+\r
+  bool file_move (const std::string& old_filespec, const std::string& new_filespec)\r
+  {\r
+    // try to move the file by renaming - if that fails then do a copy and delete the original\r
+    if (file_rename(old_filespec, new_filespec))\r
+      return true;\r
+    if (!file_copy(old_filespec, new_filespec))\r
+      return false;\r
+    // I'm not sure what to do if the delete fails - is that an error?\r
+    // I've made it an error and then delete the copy so that the original state is recovered\r
+    if (file_delete(old_filespec))\r
+      return true;\r
+    file_delete(new_filespec);\r
+    return false;\r
+  }\r
+\r
+  time_t file_created (const std::string& filespec)\r
+  {\r
+    struct stat buf;\r
+    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+    return buf.st_ctime;\r
+  }\r
+\r
+  time_t file_modified (const std::string& filespec)\r
+  {\r
+    struct stat buf;\r
+    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+    return buf.st_mtime;\r
+  }\r
+\r
+  time_t file_accessed (const std::string& filespec)\r
+  {\r
+    struct stat buf;\r
+    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+    return buf.st_atime;\r
+  }\r
+\r
+  std::string create_filespec (const std::string& directory, const std::string& filename)\r
+  {\r
+    std::string result = directory;\r
+    // if directory is empty then no directory part will be added\r
+    // add trailing slash if the directory was specified and does not have a trailing slash\r
+    if (!result.empty() && !is_separator(result[result.size()-1]))\r
+      result += preferred_separator;\r
+    // if filename is null or empty, nothing will be added so the path is then a directory path\r
+    result += filename;\r
+    return result;\r
+  }\r
+\r
+  std::string create_filespec (const std::string& directory, const std::string& basename, const std::string& extension)\r
+  {\r
+    return create_filespec(directory, create_filename(basename, extension));\r
+  }\r
+\r
+  std::string create_filename(const std::string& basename, const std::string& extension)\r
+  {\r
+    std::string name = basename;\r
+    // extension is optional - so the dot is also optional\r
+    if (!extension.empty())\r
+    {\r
+      if (extension[0] != '.') name += '.';\r
+      name += extension;\r
+    }\r
+    return name;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// folder functions\r
+\r
+  bool folder_create (const std::string& directory)\r
+  {\r
+#ifdef MSWINDOWS\r
+    return mkdir(directory.c_str()) == 0;\r
+#else\r
+    return mkdir(directory.c_str(), 0777) == 0;\r
+#endif\r
+  }\r
+\r
+  bool folder_exists (const std::string& directory)\r
+  {\r
+    return is_folder(directory);\r
+  }\r
+\r
+  bool folder_readable (const std::string& directory)\r
+  {\r
+    // a folder is readable if it exists and has read access\r
+    std::string dir = directory;\r
+    if (dir.empty()) dir = ".";\r
+    if (!folder_exists(dir)) return false;\r
+    return access(dir.c_str(),R_OK)==0;\r
+  }\r
+\r
+  bool folder_writable (const std::string& directory)\r
+  {\r
+    // a folder is writable if it exists and has write access\r
+    std::string dir = directory;\r
+    if (dir.empty()) dir = ".";\r
+    if (!folder_exists(dir)) return false;\r
+    return access(dir.c_str(),W_OK)==0;\r
+  }\r
+\r
+  bool folder_delete (const std::string& directory, bool recurse)\r
+  {\r
+    std::string dir = directory;\r
+    if (dir.empty()) dir = ".";\r
+    if (!folder_exists(dir)) return false;\r
+    bool result = true;\r
+    // depth-first traversal ensures that directory contents are deleted before trying to delete the directory itself\r
+    if (recurse)\r
+    {\r
+      std::vector<std::string> subdirectories = folder_subdirectories(dir);\r
+      for (std::vector<std::string>::size_type d = 0; d < subdirectories.size(); ++d)\r
+        if (!folder_delete(folder_down(dir,subdirectories[d]),true)) \r
+          result = false;\r
+      std::vector<std::string> files = folder_files(dir);\r
+      for (std::vector<std::string>::size_type f = 0; f < files.size(); ++f)\r
+        if (!file_delete(create_filespec(dir, files[f]))) \r
+          result = false;\r
+    }\r
+    if (rmdir(dir.c_str())!=0) result = false;\r
+    return result;\r
+  }\r
+\r
+  bool folder_rename (const std::string& old_directory, const std::string& new_directory)\r
+  {\r
+    if (!folder_exists(old_directory)) return false;\r
+    return rename(old_directory.c_str(), new_directory.c_str())==0;\r
+  }\r
+\r
+  bool folder_empty(const std::string& directory)\r
+  {\r
+    std::string dir = directory.empty() ? std::string(".") : directory;\r
+    bool result = true;\r
+#ifdef MSWINDOWS\r
+    std::string wildcard = create_filespec(dir, "*.*");\r
+    long handle = -1;\r
+    _finddata_t fileinfo;\r
+    for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))\r
+    {\r
+      std::string strentry = fileinfo.name;\r
+      if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+      {\r
+        result = false;\r
+        break;\r
+      }\r
+    }\r
+    _findclose(handle);\r
+#else\r
+    DIR* d = opendir(dir.c_str());\r
+    if (d)\r
+    {\r
+      for (dirent* entry = readdir(d); entry; entry = readdir(d))\r
+      {\r
+        std::string strentry = entry->d_name;\r
+        if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+        {\r
+          result = false;\r
+          break;\r
+        }\r
+      }\r
+      closedir(d);\r
+    }\r
+#endif\r
+    return result;\r
+  }\r
+\r
+  bool folder_set_current(const std::string& folder)\r
+  {\r
+    if (!folder_exists(folder))\r
+      return false;\r
+#ifdef MSWINDOWS\r
+    // Windose implementation - this returns non-zero for success\r
+    return (SetCurrentDirectoryA(folder.c_str()) != 0);\r
+#else\r
+    // Unix implementation - this returns zero for success\r
+    return (chdir(folder.c_str()) == 0);\r
+#endif\r
+  }\r
+\r
+  std::string folder_current (void)\r
+  {\r
+    return ".";\r
+  }\r
+\r
+  std::string folder_current_full(void)\r
+  {\r
+    // It's not clear from the documentation whether the buffer for a path should be one byte longer\r
+    // than the maximum path length to allow for the null termination, so I have made it so anyway\r
+#ifdef MSWINDOWS\r
+    char abspath [MAX_PATH+1];\r
+    return std::string(_fullpath(abspath, ".", MAX_PATH+1));\r
+#else\r
+    char pathname [MAXPATHLEN+1];\r
+    char* result = getcwd(pathname,MAXPATHLEN+1);\r
+    if (!result)\r
+    {\r
+      // should really report the error from errno\r
+      return std::string();\r
+    }\r
+    return std::string(result);\r
+#endif\r
+  }\r
+\r
+  std::string folder_down (const std::string& directory, const std::string& subdirectory)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(directory);\r
+    spec.add_subpath(subdirectory);\r
+    return spec.image();\r
+  }\r
+\r
+  std::string folder_up (const std::string& directory, unsigned levels)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(directory);\r
+    for (unsigned i = 0; i < levels; i++)\r
+      spec.add_subpath("..");\r
+    spec.simplify();\r
+    return spec.image();\r
+  }\r
+\r
+  std::vector<std::string> folder_subdirectories (const std::string& directory)\r
+  {\r
+    return folder_wildcard(directory, "*", true, false);\r
+  }\r
+\r
+  std::vector<std::string> folder_files (const std::string& directory)\r
+  {\r
+    return folder_wildcard(directory, "*", false, true);\r
+  }\r
+\r
+  std::vector<std::string> folder_all(const std::string& directory)\r
+  {\r
+    return folder_wildcard(directory, "*", true, true);\r
+  }\r
+\r
+  std::vector<std::string> folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files)\r
+  {\r
+    std::string dir = directory.empty() ? std::string(".") : directory;\r
+    std::vector<std::string> results;\r
+#ifdef MSWINDOWS\r
+    std::string wildcard = create_filespec(dir, wild);\r
+    long handle = -1;\r
+    _finddata_t fileinfo;\r
+    for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))\r
+    {\r
+      std::string strentry = fileinfo.name;\r
+      if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+        if ((subdirs && (fileinfo.attrib & _A_SUBDIR)) || (files && !(fileinfo.attrib & _A_SUBDIR)))\r
+          results.push_back(strentry);\r
+    }\r
+    _findclose(handle);\r
+#else\r
+    DIR* d = opendir(dir.c_str());\r
+    if (d)\r
+    {\r
+      for (dirent* entry = readdir(d); entry; entry = readdir(d))\r
+      {\r
+        std::string strentry = entry->d_name;\r
+        if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+        {\r
+          std::string subpath = create_filespec(dir, strentry);\r
+          if (((subdirs && is_folder(subpath)) || (files && is_file(subpath))) && (wildcard(wild, strentry)))\r
+            results.push_back(strentry);\r
+        }\r
+      }\r
+      closedir(d);\r
+    }\r
+#endif\r
+    return results;\r
+  }\r
+\r
+  std::string folder_home (void)\r
+  {\r
+    if (getenv("HOME"))\r
+      return std::string(getenv("HOME"));\r
+#ifdef MSWINDOWS\r
+    if (getenv("HOMEDRIVE") || getenv("HOMEPATH"))\r
+      return std::string(getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH"));\r
+    return "C:\\";\r
+#else\r
+    if (getenv("USER"))\r
+      return folder_down("/home", std::string(getenv("USER")));\r
+    if (getenv("USERNAME"))\r
+      return folder_down("/home", std::string(getenv("USERNAME")));\r
+    return "";\r
+#endif\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// path functions convert between full and relative paths\r
+\r
+  bool is_full_path(const std::string& path)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+    return spec.absolute();\r
+  }\r
+\r
+  bool is_relative_path(const std::string& path)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+    return spec.relative();\r
+  }\r
+\r
+  static std::string full_path(const std::string& root, const std::string& path)\r
+  {\r
+    // convert path to a full path using root as the start point for relative paths\r
+    // decompose the path and test whether it is already an absolute path, in which case just return it\r
+    file_specification spec;\r
+    spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+    if (spec.absolute()) return spec.image();\r
+    // okay, so the path is relative after all, so we need to combine it with the root path\r
+    // decompose the root path and check whether it is relative\r
+    file_specification rootspec;\r
+    rootspec.initialise_folder(root.empty() ? std::string(".") : root);\r
+    if (rootspec.relative())\r
+      rootspec.make_absolute();\r
+    // Now do the conversion of the path relative to the root\r
+    spec.make_absolute(rootspec);\r
+    return spec.image();\r
+  }\r
+\r
+  static std::string relative_path(const std::string& root, const std::string& path)\r
+  {\r
+    // convert path to a relative path, using the root path as its starting point\r
+    // first convert both paths to full paths relative to CWD\r
+    file_specification rootspec;\r
+    rootspec.initialise_folder(root.empty() ? std::string(".") : root);\r
+    if (rootspec.relative())\r
+      rootspec.make_absolute();\r
+    file_specification spec;\r
+    spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+    if (spec.relative())\r
+      spec.make_absolute();\r
+    // now make path spec relative to the root spec\r
+    spec.make_relative(rootspec);\r
+    return spec.image();\r
+  }\r
+\r
+  std::string folder_to_path (const std::string& path, const std::string& directory)\r
+  {\r
+    return full_path(path, directory);\r
+  }\r
+\r
+  std::string filespec_to_path (const std::string& path, const std::string& spec)\r
+  {\r
+    return create_filespec(folder_to_path(path, folder_part(spec)),filename_part(spec));\r
+  }\r
+\r
+  std::string folder_to_path(const std::string& folder)\r
+  {\r
+    return folder_to_path(folder_current(), folder);\r
+  }\r
+\r
+  std::string filespec_to_path(const std::string& filespec)\r
+  {\r
+    return filespec_to_path(folder_current(), filespec);\r
+  }\r
+\r
+  std::string folder_to_relative_path(const std::string& root, const std::string& folder)\r
+  {\r
+    return relative_path(root, folder);\r
+  }\r
+\r
+  std::string filespec_to_relative_path(const std::string& root, const std::string& spec)\r
+  {\r
+    return create_filespec(folder_to_relative_path(root, folder_part(spec)),filename_part(spec));\r
+  }\r
+\r
+  std::string folder_to_relative_path(const std::string& folder)\r
+  {\r
+    return folder_to_relative_path(folder_current(), folder);\r
+  }\r
+\r
+  std::string filespec_to_relative_path(const std::string& filespec)\r
+  {\r
+    return filespec_to_relative_path(folder_current(), filespec);\r
+  }\r
+\r
+  std::string folder_append_separator(const std::string& folder)\r
+  {\r
+    std::string result = folder;\r
+    if (result.empty() || !is_separator(result[result.size()-1]))\r
+      result += preferred_separator;\r
+    return result;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string basename_part (const std::string& spec)\r
+  {\r
+    std::string fname = filename_part(spec);\r
+    // scan back through filename until a '.' is found and remove suffix\r
+    // the whole filename is the basename if there is no '.'\r
+    std::string::size_type i = fname.find_last_of('.');\r
+    // observe Unix convention that a dot at the start of a filename is part of the basename, not the extension\r
+    if (i != 0 && i != std::string::npos)\r
+      fname.erase(i, fname.size()-i);\r
+    return fname;\r
+  }\r
+\r
+  std::string filename_part (const std::string& spec)\r
+  {\r
+    // scan back through filename until a preferred_separator is found and remove prefix;\r
+    // if there is no preferred_separator then remove nothing, i.e. the whole filespec is filename\r
+    unsigned i = spec.size();\r
+    while (i--)\r
+    {\r
+      if (is_separator(spec[i]))\r
+        return spec.substr(i+1,spec.size()-i-1);\r
+    }\r
+    return spec;\r
+  }\r
+\r
+  std::string extension_part (const std::string& spec)\r
+  {\r
+    std::string fname = filename_part(spec);\r
+    // scan back through filename until a '.' is found and remove prefix;\r
+    std::string::size_type i = fname.find_last_of('.');\r
+    // observe Unix convention that a dot at the start of a filename is part of the name, not the extension;\r
+    if (i != 0 && i != std::string::npos)\r
+      fname.erase(0, i+1);\r
+    else\r
+      fname.erase();\r
+    return fname;\r
+  }\r
+\r
+  std::string folder_part (const std::string& spec)\r
+  {\r
+    // scan back through filename until a separator is found and remove prefix\r
+    // if there is no separator, remove the whole\r
+    unsigned i = spec.size();\r
+    while (i--)\r
+    {\r
+      if (is_separator(spec[i]))\r
+        return spec.substr(0,i);\r
+    }\r
+    return std::string();\r
+  }\r
+\r
+  std::vector<std::string> filespec_elements(const std::string& filespec)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_file(filespec);\r
+    std::vector<std::string> result = spec.path();\r
+    if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());\r
+    if (!spec.file().empty()) result.push_back(spec.file());\r
+    return result;\r
+  }\r
+\r
+  std::vector<std::string> folder_elements(const std::string& folder)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(folder);\r
+    std::vector<std::string> result = spec.path();\r
+    if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());\r
+    return result;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// mimic the command lookup used by the shell\r
+\r
+// Windows looks at the following locations:\r
+// 1) application root\r
+// 2) current directory\r
+// 3) 32-bit system directory\r
+// 4) 16-bit system directory\r
+// 5) windows system directory\r
+// 6) %path%\r
+// currently only (2) and (6) has been implemented although many system folders are on the path anyway\r
+// also implement the implied .exe extension on commands with no path (see CreateProcess documentation)\r
+// TODO - PATHEXT handling to find non-exe executables\r
+\r
+  std::string path_lookup (const std::string& command)\r
+  {\r
+    std::string path = std::string(".") + PATH_SPLITTER + getenv("PATH");\r
+    return lookup(command, path);\r
+  }\r
+\r
+  std::string lookup (const std::string& command, const std::string& path, const std::string& splitter)\r
+  {\r
+    // first check whether the command is already a path and check whether it exists\r
+    if (!folder_part(command).empty())\r
+    {\r
+      if (file_exists(command))\r
+        return command;\r
+    }\r
+    else\r
+    {\r
+      // command is just a name - so do path lookup\r
+      // split the path into its elements\r
+      std::vector<std::string> paths;\r
+      if (!path.empty())\r
+      {\r
+        for(std::string::size_type offset = 0;;)\r
+        {\r
+          std::string::size_type found = path.find(splitter, offset);\r
+          if (found != std::string::npos)\r
+          {\r
+            paths.push_back(path.substr(offset, found-offset));\r
+            offset = found + splitter.size();\r
+          }\r
+          else\r
+          {\r
+            paths.push_back(path.substr(offset, path.size()-offset));\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      // now lookup each path to see if it its the matching one\r
+      for (unsigned i = 0; i < paths.size(); i++)\r
+      {\r
+        std::string spec = create_filespec(paths[i], command);\r
+        if (file_exists(spec))\r
+        {\r
+          return spec;\r
+        }\r
+      }\r
+    }\r
+#ifdef MSWINDOWS\r
+    // if there is no extension, try recursing on each possible extension\r
+    // TODO iterate through PATHEXT\r
+    if (extension_part(command).empty())\r
+      return lookup(create_filespec(folder_part(command), basename_part(command), "exe"), path, splitter);\r
+#endif\r
+    // if path lookup failed, return empty string to indicate error\r
+    return std::string();\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string install_path(const std::string& argv0)\r
+  {\r
+    std::string bin_directory = folder_part(argv0);\r
+    if (bin_directory.empty())\r
+    {\r
+      // do path lookup to find the executable path\r
+      bin_directory = folder_part(path_lookup(argv0));\r
+    }\r
+    return bin_directory;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
This page took 0.062865 seconds and 4 git commands to generate.