GIT repositories

Index page of all the GIT repositories that are clonable form this server via HTTPS. Übersichtsseite aller GIT-Repositories, die von diesem Server aus über git clone (HTTPS) erreichbar sind.

Services

A bunch of service scripts to convert, analyse and generate data. Ein paar Services zum Konvertieren, Analysieren und Generieren von Daten.

GNU octave web interface

A web interface for GNU Octave, which allows to run scientific calculations from netbooks, tables or smartphones. The interface provides a web form generator for Octave script parameters with pre-validation, automatic script list generation, as well presenting of output text, figures and files in a output HTML page. Ein Webinterface für GNU-Octave, mit dem wissenschaftliche Berechnungen von Netbooks, Tablets oder Smartphones aus durchgeführt werden können. Die Schnittstelle beinhaltet einen Formulargenerator für Octave-Scriptparameter, mit Einheiten und Einfabevalidierung. Textausgabe, Abbildungen und generierte Dateien werden abgefangen und in einer HTML-Seite dem Nutzer als Ergebnis zur Verfügung gestellt.

C++ Dateisysem Klassen-Template

C++ File system class template

Eine einfacher "Wrpaper-Template-Klasse" für's Dateisystem mit statischen Funktionen, von cwd() über chmod() zu is_dir(). Die meisten sind klein genug um vom Compiler "inlined" zu werden, d.h. es ist einfach eine "Bequemlichkeits-Klasse", die ich ("vor langer Zeit mal") implementiert habe und seitdem warte.

Features

  • Funkionen mit Textargumenten arbeiten mit std::string (oder abgeleitete davon).

  • Numerische Datentypen (wie pid_t, gid_t werden nach long konvertiert).

  • Die Defaultspezialisierung der Klasse ::sw::fs verwendet std::string.

  • Alle Datei-/Verzeichnisfunktionen folgen Symlinks, außer is_link(), readlink() (welche mit den Links selbst arbeiten), sowie realpath(), die alle Symlinks auflöst.

  • Die Klasse arbeitet mit Bool'schen Rückgabewerten statt mit Exceptions, damit kann sie auch mit -fno-exceptions kompiliert werden.

  • Siehe auch: boost filesystem. Unterschiede zu boost: Sehr klein im Vergleich, nur eine Headerdatei, keine Implementierungsdatei - aber auch weniger Umfang und Plattformsupport. Für die Standardfälle Linux, Win, Mac/BSD bevorzuge ich oftmals immer noch diese kleine Klasse.

It's a simple template based filesystem wrapper class. Static functions provide common file system operations, as well as functions like getting GID, UID, PID, etc. Most functions are small enough to be inlined and will be "transparent", so this is basically a convenience header I once implemented and maintained since:

Features

  • Functions with text input are wrapped to use std::string (or derived).

  • Numeric value types (such as pid_t, gid_t ...) are converted to long.

  • A default template specialisation typenamed (class) ::sw::fs uses std::string.

  • All file/directory functions follow symbolic links, except is_link(), readlink(), which refer to the targeted links, and realpath(), which resolves all symlinks.

  • The class uses boolean returns rather than exceptions and can be compiled with -fno-exceptions.

  • See also: boost filesystem. Differences to boost: Very lightweight, single header file, no implementation file, but less functionality, less platforms supported. For the standard cases Linux, Win, Mac/BSD and small checks to do I still prefer this little class.

Dateien

Files

filesystem.hh

Beispiele (microtest)

Examples (microtest)

#include <filesystem.hh>
#include "test.hh"
 
using namespace sw;
using namespace std;
using namespace sw::utest;
 
template <typename T>
std::string vect2csv(std::vector<T> v)
{
  if(v.empty()) return "";
  std::sort(v.begin(), v.end());
  std::stringstream ss;
  ss << v[0];
  for(typename std::vector<T>::const_iterator it=v.begin()+1; it != v.end(); ++it) {
    ss << "," << *it;
  }
  return ss.str();
}
 
void test()
{
  std::string tmp_dir = test_tmpdir();
  test_expect( fs::is_dir(tmp_dir) );
 
  std::string filebase = "test.txt";
  std::string file = tmp_dir + DIRECTORY_SEPARATOR + filebase;
  std::string text = "HALLO HIER";
  std::string lnkbase = filebase + ".lnk";
  std::string lnk = file + ".lnk";
    std::string file_cp = file+".copy.txt";
  std::string file_mv =  file+".move.txt";
 
  test_comment("vars.filebase = " << filebase );
  test_comment("vars.file     = " << file );
  test_comment("vars.text     = " << text );
  test_comment("vars.lnkbase  = " << lnkbase );
  test_comment("vars.lnk      = " << lnk );
  test_comment("vars.file_cp  = " << file_cp );
  test_comment("vars.file_mv  = " << file_mv );
 
  test_comment("fs::appname() = " << fs::appname() );
  test_expect( fs::basename(fs::appname()).find("filesystem") == 0 );
  test_comment("fs::cwd() = " << fs::cwd() );
  test_expect( !fs::cwd().empty() );
  test_comment("home  =" << fs::home());
  test_expect( !fs::home().empty() );
  test_expect( !fs::tmpdir().empty() );
 
  // user, group, or what's applicable  ....
  test_comment( "uid   =" << fs::uid() );
  test_comment( "gid   =" << fs::gid() );
  test_comment( "user  =" << fs::user() );
  test_comment( "group =" << fs::group() );
  if(buildinfo::is_windows()) {
    test_expect( fs::uid() == 0);
    test_expect( fs::gid() == 0);
    test_expect( !fs::user().empty() );
    test_expect( fs::group().empty() );
  } else {
    test_expect( fs::uid() != 0 || fs::user() == "root" );
    test_expect( fs::gid() != 0 || fs::user() == "root" );
    test_expect (!fs::group().empty());
    test_expect( !fs::user().empty() );
  }
  {
    test_comment("Writing text file " << file);
    if(test_expect_cond( fs::file_put_contents(file, text) )) {
      test_expect( fs::file_exists(file) );
      if(!test_expect_cond(fs::filesize(file) == (int)text.length())) {
        test_comment("fs::filesize(file) == " << fs::filesize(file));
        test_comment("text.length()      == " << (int)text.length());
      }
      test_expect( fs::is_file(file) );
      test_expect( !fs::is_dir(file) );
 
      test_expect( fs::is_accessable(file) );
      test_expect( fs::is_readable(file) );
      test_expect( fs::is_writable(file) );
      test_expect( !fs::is_executable(file) );
 
      test_note  ( "fs::basename('" << file << "') = '" << fs::basename(file) << "'" );
      test_expect( fs::basename(file) == filebase );
      test_note  ( "fs::dirname('" << file << "') = '" << fs::dirname(file) << "'" );
      test_expect( fs::dirname(file) == tmp_dir );
      test_note  ( "fs::realpath('" << file << "') = '" << fs::realpath(file) << "'" );
      test_expect( !fs::realpath(file).empty() );
      test_note  ( "fs::basename(fs::realpath('" << file << "')) = '" << fs::basename(fs::realpath(file)) << "'" );
      test_expect( fs::basename(fs::realpath(file)) == filebase );
 
      if(!buildinfo::is_windows()) {
        test_expect( fs::uid(file) == fs::uid() );
        test_expect( fs::gid(file) == fs::gid() );
        test_expect( fs::user(file) == fs::user() );
        test_expect( fs::group(file) == fs::group() );
      } else {
        test_expect( fs::uid(file) == 0 );
        test_expect( fs::gid(file) == 0 );
        test_expect( fs::user(file) == "" );
        test_expect( fs::group(file) == "" );
      }
      test_expect( fs::filemtime(file) > 0 );
      test_comment("fs::filemtime(file)" << fs::filemtime(file));
      test_expect( fs::filectime(file) <= fs::filemtime(file) );
      test_comment("fs::filectime(file)" << fs::filectime(file));
      test_expect( fs::fileatime(file) > 0 );
      test_comment("fs::fileatime(file)" << fs::fileatime(file));
    }
  }
  {
    std::string dir = tmp_dir + "dir";
    test_expect_noexcept( fs::rmdir(dir) );
    test_comment("Creating directory '" << dir << "'");
    test_expect( fs::mkdir(dir) );
    test_expect( fs::is_dir(dir) );
    test_expect( fs::mkdir(dir) );
    test_expect( fs::is_dir(dir) );
    test_expect( fs::rmdir(dir) );
    test_expect( !fs::is_dir(dir) );
    test_expect( fs::mkdir(dir, "-w-------") );
    test_expect( fs::getmod(dir) == 0200 || utest::buildinfo::is_windows() );
    test_expect( fs::rmdir(dir) );
    test_expect( fs::mkdir(dir, "rwx------") );
    test_expect( fs::getmod(dir) == 0700 || utest::buildinfo::is_windows());
    test_expect( fs::rmdir(dir) );
    test_expect( fs::mkdir(dir, "rwxrwx---") );
    test_expect( fs::getmod(dir) == 0770 || utest::buildinfo::is_windows());
    test_expect( fs::rmdir(dir) );
    test_expect( fs::mkdir(dir, "rwxrwxrwx") );
    test_expect( fs::getmod(dir) == 0777 || utest::buildinfo::is_windows());
    test_expect( fs::rmdir(dir) );
    test_expect( fs::mkdir(dir, "rwx-wx-wx") );
    test_expect( fs::getmod(dir) == 0733 || utest::buildinfo::is_windows());
    test_expect( fs::rmdir(dir) );
    test_expect( fs::mkdir(dir, "rwx--x--x") );
    test_expect( fs::getmod(dir) == 0711 || utest::buildinfo::is_windows());
    test_expect( fs::rmdir(dir) );
  }
  if(!utest::buildinfo::is_windows()) {
    test_comment("Creating link " << lnk << " for " << file);
    test_expect( fs::symlink(file, lnk) );
    if( test_expect_cond(fs::is_file(lnk)) ) { // either target file or lnk file, no matter
      test_expect ( !fs::readlink(lnk).empty() );
      test_expect ( fs::realpath(fs::readlink(lnk)) == fs::realpath(file) );
      test_expect ( fs::file_exists(lnk) );
      test_expect ( fs::filesize(lnk) == (int)text.length() ); // must apply to the linked file
      test_expect( fs::is_file(lnk) );
      test_expect( !fs::is_dir(lnk) );
      test_expect( fs::is_link(lnk) );
      test_expect( fs::is_readable(lnk) );
      test_expect( fs::is_writable(lnk) );
      test_expect( !fs::is_executable(lnk) );
      test_expect( fs::basename(lnk) == lnkbase );
      test_expect( fs::dirname(lnk) == tmp_dir);
      test_expect( fs::realpath(lnk) == fs::realpath(file) );
      test_expect( fs::uid(lnk) == fs::uid() );
      test_expect( fs::gid(lnk) == fs::gid() );
      test_expect( fs::user(lnk) == fs::user() );
      test_expect( fs::group(lnk) == fs::group() );
      test_expect( fs::filemtime(lnk) == fs::filemtime(file) );
      test_expect( fs::filectime(lnk) == fs::filectime(file) );
      test_expect( fs::filectime(lnk) == fs::filectime(file) );
      test_expect( fs::unlink(lnk) );
      test_expect( !fs::is_link(lnk) );
    }
  }
  {
    test_expect( fs::copy(file, file_cp) );
    test_expect( fs::filesize(file) == fs::filesize(file_cp) );
    test_expect( fs::move(file_cp, file_mv) );
    test_expect( fs::filesize(file_mv) == fs::filesize(file) );
    test_expect( fs::unlink(file_mv) );
    test_expect( fs::unlink(file) );
  }
  {
    test_expect ( fs::setenv("SWL_TEST", "TEST TEST TEST") );
    test_comment ( "fs::getenv('SWL_TEST') = " << fs::getenv("SWL_TEST") );
    test_expect ( fs::getenv("SWL_TEST") == "TEST TEST TEST" );
  }
  {
    if(!buildinfo::is_windows()) {
      test_comment( "fs::realpath(.) = " << fs::realpath(".") );
      test_comment( "fs::realpath(~) = " << fs::realpath("~") );
      test_expect( fs::realpath("") == "" );
      test_expect( fs::realpath("/") == "/" );
      test_expect( fs::realpath(".") == fs::cwd() );
      test_expect( fs::realpath("~") == fs::home() );
      test_expect( fs::realpath("~/.profile") == fs::home() + "/.profile" );
      test_expect( fs::realpath("~/not_existing_file") == fs::home() + "/not_existing_file" );
      test_expect( fs::realpath("./not_existing_file") == fs::cwd() + "/not_existing_file" );
      test_expect( fs::realpath("not_existing_file") == fs::cwd() + "/not_existing_file" );
      test_expect( fs::realpath("/tmp/") == "/tmp" );
      test_expect( fs::realpath("~").find("/") == 0 );
      test_expect( fs::realpath("~").find("~") == string::npos );
      test_expect( fs::realpath("~/.profile").find("~") == string::npos );
      test_expect( fs::basename(fs::realpath("~/.profile")) == ".profile" );
    } else {
      test_comment( "fs::realpath(.) = " << fs::realpath(".") );
      test_comment( "fs::realpath(~) = " << fs::realpath("~") );
      test_expect( fs::realpath("") == "" );
      test_expect( fs::realpath(".") == fs::cwd() );
      test_expect( fs::realpath("~") == fs::home() );
    }
  }
  {
    if(!buildinfo::is_windows()) {
      test_expect( fs::basename("") == "" );
      test_expect( fs::basename("/") == "/" );
      test_expect( fs::basename("/tmp") == "tmp" );
      test_expect( fs::basename("/tmp/") == "tmp" );
      test_expect( fs::basename("/tmp//") == "tmp" );
      test_expect( fs::basename("//tmp//") == "tmp" );
      test_expect( fs::basename("/tmp/a.txt") == "a.txt" );
      test_expect( fs::basename("/tmp/a.txt/") == "a.txt" );
      test_expect( fs::basename("//tmp//a.txt/") == "a.txt" );
      test_expect( fs::basename("a.txt") == "a.txt" );
      test_expect( fs::dirname("") == "." );
      test_expect( fs::dirname(".") == "." );
      test_expect( fs::dirname("a.txt") == "." );
      test_expect( fs::dirname("/") == "/" );
      test_expect( fs::dirname("//") == "//" );
      test_expect( fs::dirname("///") == "/" );
      test_expect( fs::dirname("/aaa/a.txt") == "/aaa" );
    } else {
      test_expect( fs::basename("") == "" );
      test_expect( fs::basename("c:\\") == "c:" );
      test_comment( "fs::basename(c:\\) = " << fs::basename("c:\\") );
      test_expect( fs::basename("c:\\Program Files") == "Program Files" );
      test_expect( fs::basename("c:\\Program Files\\") == "Program Files" );
      test_expect( fs::basename("c:\\Program Files\\a.txt") == "a.txt" );
      test_expect( fs::basename("c:\\Not a folder\\a.txt") == "a.txt" );
      test_expect( fs::basename("a.txt") == "a.txt" );
      test_expect( fs::dirname("a.txt") == "." );
      test_expect( fs::dirname("c:\\Program Files") == "c:" );
      test_expect( fs::dirname("c:\\Program Files\\a.txt") == "c:\\Program Files" );
      test_expect( fs::dirname("c:\\Not a folder\\a.txt") == "c:\\Not a folder" );
    }
  }
  {
    string dbase, d1, d2, d3, f1, f2, f3, f4, f5, sep;
    if(!buildinfo::is_windows()) {
      sep = "/";
      dbase = tmp_dir + "/fstest";
      d1 = dbase + "/dir1";
      d2 = dbase + "/dir1/dir2";
      d3 = dbase + "/dir3";
      f1 = dbase + "/file1.txt";
      f2 = dbase + "/dir1/file2.txt";
      f3 = dbase + "/dir1/dir2/file3.txt";
      f4 = dbase + "/file4.txt";
      f5 = dbase + "/file5.txt";
    } else {
      sep = "\\";
      dbase = tmp_dir + "\\fstest";
      d1 = dbase + "\\dir1";
      d2 = dbase + "\\dir1\\dir2";
      d3 = dbase + "\\dir3";
      f1 = dbase + "\\file1.txt";
      f2 = dbase + "\\dir1\\file2.txt";
      f3 = dbase + "\\dir1\\dir2\\file3.txt";
      f4 = dbase + "\\file4.txt";
      f5 = dbase + "\\file5.txt";
    }
    test_expect( fs::mkdir(dbase) && fs::is_dir(dbase) );
    test_expect( fs::mkdir(d1) && fs::is_dir(d1) );
    test_expect( fs::mkdir(d2) && fs::is_dir(d2) );
    test_expect( fs::mkdir(d3) && fs::is_dir(d3) );
    test_expect( fs::touch(f1) && fs::is_file(f1) );
    test_expect( fs::touch(f2) && fs::is_file(f2) );
    test_expect( fs::touch(f3) && fs::is_file(f3) );
    test_expect( fs::touch(f4) && fs::is_file(f4) );
    test_expect( fs::move(f1, f5) && fs::is_file(f5) && !fs::is_file(f1) );
    test_expect( fs::chdir(dbase) && fs::cwd() == fs::realpath(dbase));
    test_comment("fs::cwd() = " << fs::cwd() << " ?== " << dbase);
    test_expect( fs::move(f5, f1) && fs::is_file(f1) && !fs::is_file(f5) );
    test_expect( fs::move(f1, d1) );
    test_expect( !fs::is_file(f1) );
    test_expect( fs::is_file(d1 + sep + "file1.txt") );
    test_expect( fs::move(d1 + sep + "file1.txt", f1) );
    test_expect( !fs::move(f2, f1) ); // existing file, no overwrite
    test_expect( fs::is_file(f2) );
    test_expect( fs::is_file(f1) );
    test_expect( fs::move(f2, f1, true) ); // existing file, overwrite
    test_expect( !fs::is_file(f2) );
    test_expect( fs::is_file(f1) );
    test_expect( fs::move(d3, d2) ); // move directory
    test_expect( fs::is_dir(d2) );
    test_expect( fs::is_dir(d2 + sep + "dir3") );
    test_expect( fs::move(d2 + sep + "dir3", d3) ); // move to basedir with destination basename spec
    test_expect( !fs::move(dbase, d1) ); // move parent to child
    test_expect( fs::move("file4.txt", "file5.txt") ); // cwd based operations
    test_expect( !fs::is_file(f4) );
    test_expect( fs::is_file(f5) );
    test_expect( fs::move("file5.txt", "file1.txt", true) ); // cwd based operations
    test_expect( !fs::is_file(f5) );
    test_expect( fs::is_file(f1) );
    test_expect( fs::move("file1.txt", "dir3") );
    test_expect( !fs::is_file(f1) );
    test_expect( fs::is_file(d3 + sep + "file1.txt") );
    test_expect( fs::move("dir1"+sep+"dir2"+sep+"file3.txt", "dir3", true) );
    test_expect( fs::is_file(d3+sep+"file3.txt") );
    test_expect( fs::is_file(d3+sep+"file1.txt") );
    test_expect( !fs::is_file(d2+sep+"file3.txt") );
    test_expect( fs::move("dir3"+sep+"file3.txt", "dir3"+sep+"file1.txt", true) );
    test_expect( fs::is_file(d3+sep+"file1.txt") );
    test_expect( !fs::is_file(d3+sep+"file3.txt") );
    test_expect( fs::move("dir3"+sep+"file1.txt", ".") );
    test_expect( fs::is_file(dbase+sep+"file1.txt") );
    test_expect( !fs::is_file(d3+sep+"file1.txt") );
    test_expect( fs::move("dir1"+sep+"dir2", ".") );
    test_expect( fs::is_dir(dbase+sep+"dir1") );
    test_expect( fs::is_dir(dbase+sep+"dir2") );
    test_expect( fs::is_dir(dbase+sep+"dir3") );
    test_expect( fs::is_file(dbase+sep+"file1.txt") );
    test_expect( fs::unlink(dbase+sep+"file1.txt") );
    test_expect( fs::chdir(tmp_dir) );
    test_expect( fs::rmdir(dbase+sep+"dir1") );
    test_expect( fs::rmdir(dbase+sep+"dir2") );
    test_expect( fs::rmdir(dbase+sep+"dir3") );
    test_expect( fs::rmdir(dbase) );
  }
  {
    string sep = buildinfo::is_windows() ? "\\" : "/";
    string dbase = tmp_dir + sep + "fstest2";
    string d1 = dbase + sep + "dir1";
    string f1 = dbase + sep + "file1.txt";
    string f2 = dbase + sep + "file2.txt";
    string f3 = d1 + sep + "file3.txt";
    string f4 = dbase + sep + "file4.txt";
    test_expect( fs::unlink(f4) && !fs::is_file(f4) );
    test_expect( fs::unlink(f4) && !fs::is_file(f4) );
    test_expect( fs::mkdir(dbase) && fs::is_dir(dbase) );
    test_expect( fs::mkdir(d1) && fs::is_dir(d1) );
    test_expect( fs::file_put_contents(f1, "TESTTESTTEST") && fs::is_file(f1) );
    test_comment( "f1=" << fs::cat(f1) );
    test_expect( fs::touch(f2) && fs::is_file(f2) );
    test_expect( fs::touch(f3) && fs::is_file(f3) );
    test_expect( fs::copy(f1, f4) && fs::is_file(f4) );
    test_expect( fs::getmod(f1) == fs::getmod(f4) );
    test_expect( fs::cat(f1) == fs::cat(f4) );
    test_expect( fs::touch(f1, 0)  );
    test_expect( fs::filemtime(f1) == 0 );
    test_expect( fs::fileatime(f1) == 0 );
    test_expect( fs::filectime(f1) != 0 );
    test_expect( fs::touch(f1, 3600 * 24) );
    test_expect( fs::filemtime(f1) == 3600 * 24 );
    test_expect( fs::fileatime(f1) == 3600 * 24 );
    test_expect( fs::filectime(f1) != 3600 * 24 );
  }
  {
    string sep = buildinfo::is_windows() ? "\\" : "/";
    string dbase = tmp_dir + sep + "fstest3";
    string d1 = dbase + sep + "dir1";
    string f1 = dbase + sep + "file1.txt";
    string f2 = dbase + sep + "file2.txt";
    string f3 = d1 + sep + "file3.txt";
    string f4 = dbase + sep + "file4.txt";
    test_expect( fs::unlink(f4) && !fs::is_file(f4) );
    test_expect( fs::mkdir(dbase) && fs::is_dir(dbase) );
    test_expect( fs::mkdir(d1) && fs::is_dir(d1) );
    test_expect( fs::file_put_contents(f1, "TESTTESTTEST") && fs::is_file(f1) );
    test_expect( fs::touch(f2) && fs::is_file(f2) );
    test_expect( fs::touch(f3) && fs::is_file(f3) );
    test_expect( fs::touch(f4) && fs::is_file(f4) );
    {
      std::vector<string> v;
      test_expect( fs::readdir(dbase, v) );
      test_expect( vect2csv(v) == "dir1,file1.txt,file2.txt,file4.txt" );
    }
    {
      std::vector<string> v;
      test_expect( fs::readdir(dbase, v, true) );
      test_expect( vect2csv(v) == "file1.txt,file2.txt,file4.txt" );
    }
    {
      std::vector<string> v;
      test_expect( fs::readdir(dbase, v, false, true) );
      test_expect( vect2csv(v) == "dir1" );
    }
  }
}

Quelltext

Source code

/**
 * @package de.atwillys.cc.swl
 * @license BSD (simplified)
 * @author Stefan Wilhelm (stfwi)
 *
 * @file filesystem.hh
 * @ccflags
 * @ldflags
 * @platform linux, bsd, windows
 * @standard >= c++98
 *
 * -----------------------------------------------------------------------------
 *
 * IMPORTANT NOTICE: Functionality for Windows systems is not completely
 *                   implemented!
 *
 * -----------------------------------------------------------------------------
 *
 * Template based filesystem wrapper class. Static functions provide common
 * file system operations, as well as functions like getting GID, UID, PID,
 * etc. Most functions are small enough to be inlined and will be "transparent",
 * so this is basically a convenience header --
 *
 * - Functions with text input are wrapped to use std::string (or derived).
 *
 * - Numeric value types (such as pid_t, gid_t ...) are converted to long.
 *
 * - A default template specialisation typenamed (class) `::sw::fs` uses
 *   std::string.
 *
 * - All file/directory functions follow symbolic links, except `is_link()`,
 *   `readlink()`, which refer to the targeted links, and `realpath()`, which
 *   resolves all symlinks.
 *
 * - Usage examples:
 *
 *  - string s = fs::cwd();             // Current working directory
 *  - string s = fs::home();            // Actual user home directory
 *  - string s = fs::appname();         // Name of this application
 *  - string s = fs::user();            // Name of the actual user
 *  - string s = fs::group();           // Name of the actual user's group
 *
 *  - string s = fs::user(0);           // User name by UID (here root)
 *  - string s = fs::group(0);          // Group name by GID (here root)
 *  - string s = fs::user("/");         // User name of the file owner
 *  - string s = fs::group("/");        // Group name of the file group
 *
 *  - string s = fs::realpath("~/");    // Real path (home shortcut is resolved)
 *  - string s = fs::dirname(file);     // Parent directory of a file
 *  - string s = fs::basename(file);    // File name without parent directory
 *
 *  - bool b   = fs::file_exists(path); // Path (file/dir) exists ?
 *  - bool b   = fs::is_dir(path);      // Path is a directory ?
 *  - bool b   = fs::is_file(path);     // Path is a file ?
 *  - bool b   = fs::is_link(path);     // Path is a link ?
 *  - bool b   = fs::is_pipe(path);     // Path is a fifo/pipe ?
 *  - bool b   = fs::is_readable(path); // Path is a readable ?
 *  - bool b   = fs::is_writable(path); // Path is a writable ?
 *  - bool b   = fs::is_executable(path);//Path is a executable ?
 *
 *  - long l   = fs::filesize(path);    // Size of a file in bytes
 *  - long   l = fs::fileatime(path);   // Access timestamp, second resolution
 *  - double d = fs::fileatime(path);   // Access timestamp, sub-second resolution
 *  - double d = fs::filectime(path);   // Creation timestamp, sub-second resolution
 *  - double d = fs::filemtime(path);   // Modified timestamp, sub-second resolution
 *
 *  - bool b   = fs::chdir(path);       // Change working directory
 *  - bool b   = fs::mkdir(path);       // Create directory
 *  - bool b   = fs::rmdir(path);       // Remove empty directory
 *  - bool b   = fs::copy(path);        // Copy file
 *  - bool b   = fs::rename(path);      // Move file
 *  - bool b   = fs::link(path);        // Create symbolic link
 *  - bool b   = fs::unlink(path);      // Delete file or link
 *
 *  - string s = fs::tempfile();        // Returns the path to a temporary file.
 *  - string s = fs::readlink(path);    // Returns target of a symbolic link.
 *
 *  - string s = fs::getenv(key);       // Get an environment variable
 *  - if(!fs::setenv(key, value)) {};   // Set an environment variable
 *
 * -----------------------------------------------------------------------------
 * +++ BSD license header (You know that ...) +++
 * Copyright (c) 2006-2014, Stefan Wilhelm (stfwi, <cerbero s@atwilly s.de>)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met: (1) Redistributions
 * of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer. (2) Redistributions in binary form must reproduce
 * the above copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the distribution.
 * (3) Neither the name of atwillys.de nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
 * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * -----------------------------------------------------------------------------
 */
#ifndef SW__FILESYSTEM
#define SW__FILESYSTEM
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <streambuf>
#include <iterator>
 
#if defined(__MSC_VER)
  #ifndef OS_WIN
  #define OS_WIN
  #endif
  #include <windows.h>
  #include <process.h>
  #include <sys\types.h>
  #include <sys\stat.h>
  #include <io.h>
  #include <direct.h>
  #include <Lmcons.h>
  #include <Shlobj.h>
  #define time_t signed long long
  #define MAX_PATH_SIZE _MAX_PATH
  typedef unsigned short mode_t;
#else
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <limits.h>
  #include <unistd.h>
  #include <libgen.h>
  #include <dirent.h>
  #define MAX_PATH_SIZE (PATH_MAX)
  #if defined(__MINGW32__) || defined(__MINGW64__)
    #ifndef OS_WIN_MINGW
      #define OS_WIN_MINGW
    #endif
    #include <windows.h>
    #include <process.h>
    #include <io.h>
    #include <direct.h>
    #include <Lmcons.h>
    #include <Shlobj.h>
    #include <sys/utime.h>
  #else
    #include <pwd.h>
    #include <grp.h>
    #include <utime.h>
    #include <sys/times.h>
    #if defined(__linux) || defined(__linux__)
    #include <sys/sendfile.h>
    #include <fcntl.h>
    #endif
  #endif
#endif
#ifndef S_IRUSR
  #define S_IRUSR (_S_IREAD)
  #define S_IWUSR (_S_IWRITE)
  #define S_IXUSR (0)
#endif
#ifndef S_IRGRP
  #define S_ISUID (0)
  #define S_ISGID (0)
  #define S_ISVTX (0)
  #define S_IRGRP (_S_IREAD)
  #define S_IWGRP (_S_IWRITE)
  #define S_IXGRP (0)
  #define S_IROTH (_S_IREAD)
  #define S_IWOTH (_S_IWRITE)
  #define S_IXOTH (0)
  #define S_IRWXG (S_IRGRP|S_IWGRP|S_IXGRP)
  #define S_IRWXO (S_IROTH|S_IWOTH|S_IXOTH)
#endif
#ifndef S_IRWXU
#define S_IRWXU (S_IRUSR|S_IWUSR|S_IXUSR)
#endif
#ifndef S_ISLNK
#define S_ISLNK(X) (0)
#endif
#ifndef assert
#define assert(X) (!!(X))
#endif
 
namespace sw { namespace detail {
 
/**
 * Simple file system wrapper class.
 *
 * @template
 * @class basic_fs
 */
template <typename str_type>
class basic_fs
{
public:
 
  /**
   * Returns the process id of this process, -1 on error.
   * @return long
   */
  inline static long pid()
  {
    #ifdef OS_WIN
    return (unsigned long) _getpid();
    #else
    return (unsigned long) getpid();
    #endif
  }
 
  /**
   * Returns the user id of this process, -1 on error.
   * Notice: Not applicable for Windows, returns 0.
   * @return long
   */
  inline static long uid()
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return 0;
    #else
    return (long) getuid();
    #endif
  }
 
  /**
   * Returns the user id a file, -1 on error
   * Notice: Not applicable for Windows, returns 0.
   * @return long
   */
  inline static long uid(const str_type & path)
  {
    #ifdef OS_WIN
    return 0;
    #else
    struct stat st;
    return (::stat(path.c_str(), &st) != 0) ? -1 : (long) st.st_uid;
    #endif
  }
 
  /**
   * Returns the group id of this process, -1 on error
   * Notice: Not applicable for Windows, returns 0.
   * @return long
   */
  inline static long gid()
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return 0;
    #else
    return (long) getgid();
    #endif
  }
 
  /**
   * Returns the group id a file, -1 on error.
   * Notice: Not applicable for Windows, returns 0.
   * @return long
   */
  inline static long gid(const str_type & path)
  {
    #ifdef OS_WIN
    return 0;
    #else
    struct stat st;
    return (::stat(path.c_str(), &st) != 0) ? -1 : (long) st.st_gid;
    #endif
  }
 
  /**
   * Returns the actual user name or "" on error.
   * @return str_type
   */
  inline static str_type user()
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    char nm[UNLEN+1];
    DWORD l = UNLEN;
    nm[UNLEN] = '\0';
    ::GetUserNameA(nm, &l);
    nm[l] = '\0';
    return str_type(nm);
    #else
    return user(::geteuid());
    #endif
  }
 
  /**
   * Returns the user name by UID or "" on error.
   * Notice: Not applicable for Windows, return is "".
   * @param long uid
   * @return str_type
   */
  inline static str_type user(long uid)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    (void) uid;
    return "";
    #else
    struct passwd *pw = ::getpwuid((uid_t)uid);
    return pw ? str_type(pw->pw_name) : "";
    #endif
  }
 
  /**
   * Returns the user name by file or "" on error.
   * @param const str_type & path
   * @return str_type
   */
  inline static str_type user(const str_type & path)
  {
    return user(uid(path));
  }
 
  /**
   * Returns the actual group name or "" on error.
   * Notice: Not applicable for Windows, returns "".
   * @return str_type
   */
  inline static str_type group()
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return "";
    #else
    return group(::getgid());
    #endif
  }
 
  /**
   * Returns the group name by GID or "" on error.
   * Notice: Not applicable for Windows, returns "".
   * @param long gid
   * @return str_type
   */
  inline static str_type group(long gid)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return "";
    #else
    struct group *gr = ::getgrgid((gid_t)gid);
    return gr ? str_type(gr->gr_name) : "";
    #endif
  }
 
  /**
   * Returns the group name by file or "" on error.
   * Notice: Not applicable for Windows, returns "".
   * @param const str_type & path
   * @return str_type
   */
  inline static str_type group(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    (void) path;
    return "";
    #else
    return group(gid(path));
    #endif
  }
 
  /**
   * Get current working directory
   * @return str_type
   */
  inline static str_type cwd()
  {
    char apath[MAX_PATH_SIZE];
    apath[MAX_PATH_SIZE - 1] = '\0';
    #ifdef OS_WIN
    return _getcwd(apath, MAX_PATH_SIZE - 1) == 0 ? "" : apath;
    #else
    return ::getcwd(apath, MAX_PATH_SIZE - 1) == 0 ? "" : apath;
    #endif
  }
 
  /**
   * Return home directory of process user or empty string on error.
   * @return str_type
   */
  inline static str_type home()
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    char p[MAX_PATH];
    return (SUCCEEDED(::SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0, p))) ? p : "";
    #else
    struct passwd *pw = ::getpwuid(::getuid());
    return (pw->pw_dir) ? pw->pw_dir : "";
    #endif
  }
 
  /**
   * Returns a temporary directory (either the global or the users)
   * @return str_type
   */
  inline static str_type tmpdir()
  {
#if defined OS_WIN || defined OS_WIN_MINGW
    char bf[MAX_PATH+1];
    DWORD r = ::GetTempPathA(sizeof(bf), bf);
    if(!r || r > sizeof(bf)) return "";
    bf[r] = bf[sizeof(bf)-1] = '\0';
    while(--r > 0 && bf[r] == '\\') bf[r] = '\0';
    return bf;
    #else
    return "/tmp";
    #endif
  }
 
  /**
   * Get application name
   * @return str_type
   */
  inline static str_type appname()
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    char p[MAX_PATH];
    return (::GetModuleFileNameA( NULL, p, MAX_PATH ) > 0) ? p : "";
    #elif defined __APPLE__ & __MACH__
    const char* p = ::getprogname();
    return p ? p : "";
    #else
    char path[MAX_PATH_SIZE];
    return (::readlink("/proc/self/exe", path, MAX_PATH_SIZE) < 0) ? "" : basename(path);
    #endif
  }
 
  /**
   * Returns the basename of the path (name without parent directory)
   * @return str_type
   */
  inline static str_type basename(const str_type & path)
  {
    if(path.empty()) return path;
    #if defined OS_WIN || defined OS_WIN_MINGW
    typename str_type::size_type p = path.find_last_of('\\');
    if(p == str_type::npos) return path;
    if(p == path.length()-1) return path.length() > 1 ? basename(path.substr(0, p)) : str_type("");
    return path.substr(p+1);
    #else
    // std98: const_cast on path.c_str() risky.
    std::vector<char> v(path.length()+1);
    std::copy(path.begin(), path.end(), v.begin());
    v.back() = 0;
    return ::basename(v.data());
    #endif
  }
 
  /**
   * Returns the parent directory of the path
   * @return str_type
   */
  static inline str_type dirname(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    typename str_type::size_type p = path.find_last_of('\\');
    if(p == str_type::npos) return ".";
    if(p <= 0) return "";
    return path.substr(0, p);
    #else
    // std98: const_cast on path.c_str() risky.
    std::vector<char> v(path.length()+1);
    std::copy(path.begin(), path.end(), v.begin());
    v.back() = 0;
    return ::dirname(v.data());
    #endif
  }
 
  /**
   * Get full path of file / dir
   * @param const str_type & path
   * @return str_type
   */
  static str_type realpath(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    if(path.empty()) return "";
    if(path == "~") return home();
    char apath[MAX_PATH_SIZE];
    str_type s = path;
    while(!s.empty() && (s[s.length()-1] == '\\')) s.resize(s.length()-1);
    if(path.length() > 2 && path[0] == '~' && (path[1] == '\\' || path[1] == '/')) {
      s = home() + s.substr(1);
    }
    return (::_fullpath(apath, s.c_str(), MAX_PATH_SIZE) != NULL) ? apath : "";
    #else
    str_type file = path;
    if(path.empty()) return "";
    if(path == "~") return home();
    if(path == ".") return cwd();
    if(path.length()>1 && path[0]=='~' && path[1] == '/') {
      str_type h = home();
      if(!h.empty()) file = h + file.substr(1);
    } else if(path.length()>1 && path[0]=='.' && path[1] == '/') {
      file = cwd() + file.substr(1);
    }
    char pth[MAX_PATH_SIZE+1], pwd[MAX_PATH_SIZE+1], *p;
    pth[MAX_PATH_SIZE] = pwd[MAX_PATH_SIZE] = '\0';
    strncpy(pth, file.c_str(), MAX_PATH_SIZE);
    strncpy(pwd, cwd().c_str(), MAX_PATH_SIZE);
    if(!(p = ::realpath(pth, pwd))) { // extension: parent dir must exist
      str_type dir = dirname(file);
      file = basename(file);
      strncpy(pth, dir.c_str(), MAX_PATH_SIZE);
      strncpy(pwd, cwd().c_str(), MAX_PATH_SIZE);
      if((p = ::realpath(pth, pwd)) != NULL) {
        dir = p;
        return dir + "/" + file;
      } else {
        return "";
      }
    } else {
      return p ? p : "";
    }
    #endif
  }
 
  /**
   * Returns true if file or directory is accessable (only existance)
   * @param const str_type & path
   * @return bool
   */
  static inline bool is_accessable(const str_type & path)
  {
    #ifndef OS_WIN
    return ::access(path.c_str(), 0) == 0;
    #else
    return _access(path.c_str(), 0) == 0;
    #endif
  }
 
  /**
   * Returns true if file or directory or fifo exists
   * @param const str_type & path
   * @return bool
   */
  static inline bool exists(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return (::GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES);
    #else
    struct stat st;
    return (::stat(path.c_str(), &st) == 0) && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)
            || S_ISFIFO(st.st_mode) || S_ISLNK(st.st_mode)
    );
    #endif
  }
 
  /**
   * Returns true if file or directory or fifo exists
   * @param const str_type & path
   * @return bool
   */
  static inline bool file_exists(const str_type & path)
  { return exists(path); }
 
  /**
   * Returns true if path is a file
   * @param const str_type & path
   * @return bool
   */
  static inline bool is_file(str_type path)
  {
    #if defined(OS_WIN) || defined (OS_WIN_MINGW)
    DWORD fa = ::GetFileAttributesA(path.c_str());
    return (fa != INVALID_FILE_ATTRIBUTES) && ((fa & (FILE_ATTRIBUTE_DEVICE|FILE_ATTRIBUTE_DIRECTORY)) == 0);
    #else
    struct stat st;
    return (::stat(path.c_str(), &st) == 0) && S_ISREG(st.st_mode);
    #endif
  }
 
  /**
   * Returns true if path is a directory
   * @param const str_type & path
   * @return bool
   */
  static inline bool is_dir(const str_type & path)
  {
    #ifdef OS_WIN
    DWORD fa = ::GetFileAttributesA(path.c_str());
    return (fa != INVALID_FILE_ATTRIBUTES) && ((fa & FILE_ATTRIBUTE_DIRECTORY) != 0);
    #else
    struct stat st;
    return (::stat(path.c_str(), &st) == 0) && S_ISDIR(st.st_mode);
    #endif
  }
 
  /**
   * Returns true if path is a link
   * @param const str_type & path
   * @return bool
   */
  static inline bool is_link(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    if(path.length() < 5) return false;
    str_type s = path.substr(path.length()-4);
    std::transform(s.begin(), s.end(), s.begin(), ::tolower);
    return s == ".lnk";
    #else
    struct stat st;
    return (::lstat(path.c_str(), &st) == 0) && S_ISLNK(st.st_mode);
    #endif
  }
 
  /**
   * Returns true if path is a fifo
   * @param const str_type & path
   * @return bool
   */
  static inline bool is_fifo(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return false;
    #else
    struct stat st;
    return (::lstat(path.c_str(), &st) == 0) && S_ISFIFO(st.st_mode);
    #endif
  }
 
  /**
   * Returns true if path is a executable file.
   * In linux, returns false for directorys,
   * returns true is one of group, owner or all
   * exec mode flags is set. Means it needn't be
   * executable for the current user!
   * @param const str_type & path
   * @return bool
   */
  static inline bool is_executable(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    DWORD dw;
    return ::GetBinaryTypeA(path.c_str(), &dw) != 0;
    #else
    struct stat st;
    if(::stat(path.c_str(), &st) != 0) return false;
    if(st.st_uid == (uid_t) uid()) return (st.st_mode & S_IXUSR) != 0;
    if(st.st_gid == (gid_t) gid()) return (st.st_mode & S_IXGRP) != 0;
    return (st.st_mode & S_IXOTH) != 0;
    #endif
  }
 
  /**
   * Returns if the a file/dir is readable
   * @param const str_type & path
   * @return bool
   */
  static inline bool is_readable(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return _access(path.c_str(), 4) == 0;
    #else
    struct stat st;
    if(::stat(path.c_str(), &st) != 0) return false;
    if(st.st_uid == (uid_t) uid()) return (st.st_mode & S_IRUSR) != 0;
    if(st.st_gid == (gid_t) gid()) return (st.st_mode & S_IRGRP) != 0;
    return (st.st_mode & S_IROTH) != 0;
    #endif
  }
 
  /**
   * Returns if the a file/dir is writable
   * @param const str_type & path
   * @return bool
   */
  static inline bool is_writable(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return _access(path.c_str(), 2) == 0;
    #else
    struct stat st;
    if(::stat(path.c_str(), &st) != 0) return false;
    if(st.st_uid == (uid_t) uid()) return (st.st_mode & S_IWUSR) != 0;
    if(st.st_gid == (gid_t) gid()) return (st.st_mode & S_IWGRP) != 0;
    return (st.st_mode & S_IWOTH) != 0;
    #endif
    return false;
  }
 
  /**
   * Returns the file size of a file, -1 if not a file
   * @param const str_type & path
   * @return long
   */
  static inline long filesize(const str_type & path)
  {
    struct stat st;
    return (::stat(path.c_str(), &st) != 0 || ((st.st_mode & S_IFDIR) != 0)) ? -1 : (st.st_size);
  }
 
  /**
   * Returns the file modification timestamp of a file, -1 on error.
   * Timestamp is in seconds since 1970-01-01T00:00:00, resolution nanoseconds.
   * @param const str_type & path
   * @return double
   */
  static inline time_t filemtime(const str_type & path)
  {
    struct stat st; return ::stat(path.c_str(), &st) != 0 ? 0 :
    #if defined __APPLE__ & __MACH__
    ((time_t) st.st_mtimespec.tv_sec);
    #else
    (time_t) st.st_mtime;
    #endif
  }
 
  /**
   * Returns the file last-access timestamp of a file, -1 on error
   * Timestamp is in seconds since 1970-01-01T00:00:00, resolution nanoseconds.
   * @param const str_type & path
   * @return double
   */
  static inline time_t fileatime(const str_type & path)
  {
    struct stat st; return ::stat(path.c_str(), &st) != 0 ? 0 :
    #if defined __APPLE__ & __MACH__
    ((time_t) st.st_atimespec.tv_sec);
    #else
    (time_t) st.st_atime;
    #endif
  }
 
  /**
   * Returns the file creation timestamp of a file, -1 on error
   * Timestamp is in seconds since 1970-01-01T00:00:00, resolution nanoseconds.
   * @param const str_type & path
   * @return double
   */
  static inline time_t filectime(const str_type & path)
  {
    struct stat st; return ::stat(path.c_str(), &st) != 0 ? 0 :
    #if defined __APPLE__ & __MACH__
    (time_t) st.st_ctimespec.tv_sec;
    #else
    (time_t) st.st_ctime;
    #endif
  }
 
  /**
   * Returns path to a unique temp file for a specified proposal.
   * The proposal is modified with numbers. The psoposal must
   * consist of word chars (A..Z, a..z, 0..9, '_).
   * @param const str_type & path
   * @return str_type
   */
  static inline str_type tempfile(const str_type & proposal)
  {
    str_type path = ((!proposal.empty()) ? proposal : "tmpf" ) + "_XXXXXX";
    const char* r = ::mktemp(path.c_str());
    return (!r) ? "" : r;
  }
 
  /**
   * Deletes a file or directory permanently
   * @param const str_type & path
   * @return bool
   */
  static inline bool unlink(const str_type & path)
  {
    if(path.empty()) return false;
    #ifdef OS_WIN
    return _unlink(path.c_str()) >= 0 || !exists(path);
    #else
    return (::unlink(path.c_str()) >= 0) || !exists(path);
    #endif
  }
 
  /**
   * Renames a file or directory without realpath check.
   * @param const str_type & path
   * @param const str_type & newname
   * @return bool
   */
  static inline bool rename(const str_type & path, const str_type & newname)
  {
    if(path.empty() || newname.empty()) return false;
    #ifdef OS_WIN
    return (path == newname) || (_rename(path.c_str(), newname.c_str()) >= 0);
    #else
    return (path == newname) || (::rename(path.c_str(), newname.c_str()) == 0);
    #endif
  }
 
  /**
   * Changes the current directory
   * @param const str_type & path
   * @return bool
   */
  static inline bool chdir(const str_type & dir)
  {
    if(dir.empty() || dir == ".") return true;
    #if defined OS_WIN || defined OS_WIN_MINGW
    str_type s = realpath(dir);
    return SetCurrentDirectoryA(s.c_str()) != 0;
    #else
    return ::chdir(dir.c_str()) == 0;
    #endif
  }
 
  /**
   * Creates a new directory (mode 0755, if OS applicable)
   * @param const str_type & path
   * @return bool
   */
  static inline bool mkdir(const str_type & path)
  {
    #if defined OS_WIN || defined __MINGW32__ || defined __MINGW64__
    return (_mkdir(path.c_str()) == 0) || (is_dir(path));
    #else
    return (::mkdir(path.c_str(), (S_IRWXU|S_IRWXG|S_IRWXO)&(~(S_IWGRP|S_IWOTH))) == 0)
            || (is_dir(path));
    #endif
  }
 
  /**
   * Creates a new directory with OCTAL mode specification (only effect if OS applicable), e.g.
   * 0775, 0700 etc.
   * @param const str_type & path
   * @param int mode_octal = 0755
   * @return bool
   */
  static inline bool mkdir(const str_type & path, int mode_octal)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return is_dir(path) || _mkdir(path.c_str()) == 0;
    #else
    return is_dir(path) || (mkdir(path) && chmod(path, mode_octal));
    #endif
  }
 
  /**
   * Creates a new directory with string specified mode (only effect if OS supports it, e.g.
   * `mkdir("/tmp/dir", "rwxr-xr-x")` ).
   * @param const str_type & path
   * @param int mode_octal = 0755
   * @return bool
   */
  static inline bool mkdir(const str_type & path, std::string mode)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    (void) mode;
    return is_dir(path) || _mkdir(path.c_str()) == 0;
    #else
    return is_dir(path) || (mkdir(path) && chmod(path, mode));
    #endif
  }
 
  /**
   * Deletes a directory
   * @param const str_type & path
   * @return bool
   */
  static inline bool rmdir(str_type path)
  {
    if((path=realpath(path)).empty() || path == "/" || !is_dir(path)) return false;
    #if defined OS_WIN || defined OS_WIN_MINGW
    return _rmdir(path.c_str()) == 0;
    #else
    return ::rmdir(path.c_str()) == 0;
    #endif
  }
 
  /**
   * Creates a hard link of a file.
   * Note: Not applicable for Windows. Function returns always false.
   * @param const str_type & src
   * @param const str_type & dst
   * @return bool
   */
  static inline bool link(const str_type & src, const str_type & dst)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    return ::CreateHardLinkA(src.c_str(), dst.c_str(), NULL) != 0;
    #else
    return ::link(src.c_str(), dst.c_str()) == 0;
    #endif
  }
 
  /**
   * Creates a symbolic link of a file/directory/node
   * @param const str_type & src
   * @param const str_type & dst
   * @return bool
   */
  static bool inline symlink(const str_type & src, const str_type & dst)
  {
    if(src.empty() || dst.empty()) return false;
    #if defined OS_WIN && !defined OS_WIN_MINGW
    return ::CreateSymbolicLinkA(src.c_str(), dst.c_str(), is_dir(src) ? 0x1 : 0x0) != 0;
    #elif defined OS_WIN_MINGW
    return false;
    #else
    return ::symlink(src.c_str(), dst.c_str()) == 0;
    #endif
  }
 
  /**
   * Returns the destination location of a symbolic link.
   * @param const str_type & path
   * @return str_type
   */
  static inline str_type readlink(const str_type & path)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    (void) path;
    return ""; // ----------------------------------------------------------IMPLEMENT
    #else
    char p[MAX_PATH_SIZE+1];
    long l = ::readlink(path.c_str(), p, MAX_PATH_SIZE);
    if(l > 0) { p[l] = '\0'; return p; } else { return ""; }
    #endif
  }
 
  /**
   * Copy a file
   * @param str_type path
   * @param str_type dest
   * @param bool overwrite=false
   * @return bool
   */
  static bool copy(str_type path, str_type dest, bool overwrite=false)
  {
    if((path = realpath(path)).empty() || (dest = realpath(dest)).empty() ||
       (!overwrite && exists(dest))) return false;
    #if defined OS_WIN || defined OS_WIN_MINGW
    return CopyFileA(path.c_str(), dest.c_str(), overwrite ? FALSE : TRUE) == TRUE; // overwrite --> FailIfExist
    #else
    if(is_file(dest) && !unlink(dest)) { // rm file, so that creation time is now
      return false;
    }
    if(is_file(path)) {
      #if ( defined(__linux) || defined(__linux__) )
      int src=-1, dst=-1;
      bool ok;
      try {
        struct ::stat st;
        ok = (true
          && ((src = ::open(path.c_str(), O_RDONLY, 0)) >= 0)
          && (::fstat(src, &st) == 0)
          && ((dst = ::open(dest.c_str(), O_WRONLY|O_CREAT|O_TRUNC, st.st_mode)) >= 0)
          && (::sendfile(dst, src, 0, st.st_size) >= 0)
        );
      } catch(...) {
        ok = false;
      }
      if(src >= 0) ::close(src);
      if(dst >= 0) ::close(dst);
      if(!ok) ::unlink(dest.c_str()); // failed
      return ok;
      #else
      std::ifstream src(path.c_str(), std::ios::binary);
      std::ofstream dst(dest.c_str(), std::ios::binary);
      dst << src.rdbuf();
      if((!src.good() && !src.eof()) || dst.good()) {
        dst.close();
        unlink(dest);
        return false;
      } else {
        return chmod(dest, getmod(path)); // files are still open
      }
      #endif
    }
    #endif
    return false;
  }
 
  /**
   * Move a file, returns true on success. For renaming without implicit realpath checks
   * and without overwrite use `rename()`.
   *
   * Functionality similar to shell `mv`, means:
   *
   *  - moving an existing file to a nonexisting path will rename the file path,
   *    t.m. to moving it into the destination path parent directory.
   *
   *  - moving an existing directory to a nonexisting path will rename the directory path,
   *    t.m. to moving it into the destination path parent directory.
   *
   *  - moving an existing file to an existing path will replaces the file path if overwrite==true.
   *
   *  - moving an existing file to an existing directory will move the file into this directory.
   *
   *  - moving an existing directory to an existing directory will move the source directory into
   *    this destination directory.
   *
   *  - Realpath replacements apply, t.m. starting "~" == HOME, starting "./" or ".\\" == CWD,
   *    links are resolved.
   *
   *  - Refuses operation on source path "/"
   *  - Refuses moving directory into themselves.
   *  - Returns true if source and destination are equal and a regular file (would have no effect)
   *
   * @param str_type path
   * @param str_type newdest
   * @param bool overwrite =false
   * @return bool
   */
  static bool move(str_type path, str_type newdest, bool overwrite=false)
  {
    path = realpath(path);
    newdest = realpath(newdest);
    if(path.empty() || path == "/" || newdest.empty()) return false;
    if(path == newdest) return !is_dir(path); // cannot move dir into itself
    if(!exists(path)) return false;
    #if defined OS_WIN || defined OS_WIN_MINGW
    if(is_dir(newdest)) {
      newdest += std::string("\\") + basename(path);
      if(is_dir(newdest)) return false;
    }
    return MoveFileExA(path.c_str(), newdest.c_str(), MOVEFILE_COPY_ALLOWED |
            (overwrite ? MOVEFILE_REPLACE_EXISTING : 0)) != 0;
    #else
    if(newdest.find(path + "/") != std::string::npos) return false; // cannot move parent of self
    if(is_dir(newdest)) {
      newdest += std::string("/") + basename(path);
      if(is_dir(newdest)) return false;
    }
    if(is_file(newdest) && is_file(path) && ((!overwrite) || (!unlink(newdest)))) return false;
    return (::rename(path.c_str(), newdest.c_str()) == 0);
    #endif
  }
 
  /**
   * Touch a file, create if not existing, changes file modification time and access time to
   * the current time.
   * @param const str_type & path
   * @return bool
   */
  static inline bool touch(str_type path)
  {
    if(path.empty()) {
      return false;
    } else if(!exists(path)) {
      std::ofstream fs(path.c_str(), std::ofstream::binary|std::ofstream::ate|std::ofstream::app);
      return fs.good();
    }
    #if !defined OS_WIN
    return ::utime(path.c_str(), NULL) == 0; // NULL --> current times
    #else
    return _utime(path.c_str(), NULL) == 0;
    #endif
  }
 
  /**
   * Touch a file, create if not existing, changes file modification time and access time to
   * the specified local timestamp.
   * @param const str_type & path
   * @param time_t modification_and_access_time
   * @return bool
   */
  static inline bool touch(str_type path, time_t modification_and_access_time)
  {
    path = realpath(path);
    if(path.empty()) return false;
    if(!exists(path)) {
      std::ofstream fs(path.c_str(), std::ofstream::binary|std::ofstream::ate|std::ofstream::app);
      if(!fs.good()) { unlink(path); return false; }
    }
    #if !defined OS_WIN && !defined OS_WIN_MINGW
    struct ::utimbuf ut;
    ut.actime = ut.modtime = modification_and_access_time;
    return (::utime(path.c_str(), &ut) == 0);
    #else
    struct _utimbuf ut;
    ut.actime = ut.modtime = modification_and_access_time;
    return (_utime(path.c_str(), &ut) == 0);
    #endif
  }
 
  /**
   * Write text a file
   * @param const str_type & path
   * @param const str_type & contents
   * @return bool
   */
  static inline bool file_put_contents(const str_type & path, const str_type& contents)
  {
    std::ofstream fos(path.c_str(), std::ofstream::out);
    fos << contents;
    return fos.good();
  }
 
  /**
   * Read a text file
   * @param const str_type & path
   * @param str_type & contents
   * @return bool
   */
  static bool file_get_contents(const str_type & path, str_type & contents)
  {
    contents.clear();
    std::ifstream fis(path.c_str(), std::ios::in|std::ios::binary);
    fis.seekg(0, std::ios::end);
    typename std::ifstream::pos_type sz = fis.tellg();
    if(!fis.good() && !fis.eof()) return false; // only ensure, seek should not provoke eof()
    if(sz <= 0 || fis.eof()) return true;
    try { contents.reserve(sz); } catch(...) { return false; } // nothrow policy
    fis.seekg(0, std::ios::beg);
    contents.assign((std::istreambuf_iterator<char>(fis)), std::istreambuf_iterator<char>());
    if(!fis.good() && !fis.eof()) { str_type().swap(contents); return false; } // free allocated mem
    return true;
  }
 
  /**
   * Read a text file using file_get_contents, return the result on success, return "" on fail.
   * Note: Empty return is ambiguous, so use file_get_contents() if you read files that could
   * be empty.
   * @param const str_type & path
   * @return str_type
   */
  static inline str_type cat(const str_type & path)
  { str_type contents; file_get_contents(path, contents); return contents; }
 
  /**
   * Returns the file permissions as OCTAL number, <0 on fail, but not -1, which would set
   * all permission bits. The number is (-1 & (~(037777)).
   * @param const std::string& path
   * @return int
   */
  static int getmod(const std::string& path)
  {
    #ifdef OS_WIN
    return 0;
    #else
    #define invmode (((int)-1) & (~(037777)))
    if(path.empty()) return invmode;
    unsigned m;
    {
      struct ::stat st;
      if(::stat(path.c_str(), &st) != 0) return invmode;
      m = (unsigned) ((st.st_mode) & (S_IRWXU|S_IRWXG|S_IRWXO));
    }
    // Normally flags and macros should be equal, but no guarantee.
    return 0
      | ((m & S_IXOTH) ? (1<<0) : 0)
      | ((m & S_IWOTH) ? (1<<1) : 0)
      | ((m & S_IROTH) ? (1<<2) : 0)
      | ((m & S_IXGRP) ? (1<<3) : 0)
      | ((m & S_IWGRP) ? (1<<4) : 0)
      | ((m & S_IRGRP) ? (1<<5) : 0)
      | ((m & S_IXUSR) ? (1<<6) : 0)
      | ((m & S_IWUSR) ? (1<<7) : 0)
      | ((m & S_IRUSR) ? (1<<8) : 0)
      ;
    #undef invmode
    #endif
  }
 
  /**
   * Change file permissions by OCTAL mode, e.g. 0755, 0644 etc.
   * @param const std::string& path
   * @param int mode_octal
   * @return bool
   */
  static bool chmod(const std::string& path, int mode_octal)
  {
    #ifdef OS_WIN
    return true;
    #else
    // mode_octal and mod should be equal, but there is no guarantee, so
    // the standard flag macros are used.
    if(mode_octal <= 0) return -1;
    int u = (mode_octal >> 6) & 7;
    int g = (mode_octal >> 3) & 7;
    int o = (mode_octal >> 0) & 7;
    return 0 == ::chmod(path.c_str(), 0
      | ((u & 4) ? S_IRUSR : 0) | ((u & 2) ? S_IWUSR : 0) | ((u & 1) ? S_IXUSR : 0)
      | ((g & 4) ? S_IRGRP : 0) | ((g & 2) ? S_IWGRP : 0) | ((g & 1) ? S_IXGRP : 0)
      | ((o & 4) ? S_IROTH : 0) | ((o & 2) ? S_IWOTH : 0) | ((o & 1) ? S_IXOTH : 0)
    );
    #endif
  }
 
  /**
   * Change file permissions by string definition, e.g. "755", "0755", "rwxr-xr-x",
   * but not "u+rwx", "o-w,g+r" etc.
   * @param const std::string& path
   * @param std::string mode
   * @return bool
   */
  static bool chmod(const std::string& path, const std::string& mode)
  {
    #ifdef OS_WIN
    return true;
    #else
    if(mode.empty()) return false;
    if(mode.length() >= 3 && ::isdigit(mode[0]) ) {
      for(typename std::string::const_iterator it=mode.begin(); it != mode.end(); ++it) {
        if(!::isdigit(*it)) return false;
      }
      int u = *(mode.rbegin()+0) - '0';
      int g = *(mode.rbegin()+1) - '0';
      int o = *(mode.rbegin()+2) - '0';
      if(u < 0 || u > 7 || g < 0 || g > 7 || o < 0 || o > 7) return false;
      return 0 == ::chmod(path.c_str(), 0
        | ((u & 4) ? S_IRUSR : 0) | ((u & 2) ? S_IWUSR : 0) | ((u & 1) ? S_IXUSR : 0)
        | ((g & 4) ? S_IRGRP : 0) | ((g & 2) ? S_IWGRP : 0) | ((g & 1) ? S_IXGRP : 0)
        | ((o & 4) ? S_IROTH : 0) | ((o & 2) ? S_IWOTH : 0) | ((o & 1) ? S_IXOTH : 0)
      );
    } else if(mode.find_first_not_of("rwxX -") == std::string::npos) {
      if(mode.length() == 3) {
        return 0 == ::chmod(path.c_str(), 0
          | (mode[0]=='r' ? (S_IRUSR|S_IRGRP|S_IROTH) : 0)
          | (mode[1]=='w' ? (S_IWUSR|S_IWGRP|S_IWOTH) : 0)
          | (((mode[2]=='x') || ((mode[2]=='X' && is_dir(path)))) ? (S_IXUSR|S_IXGRP|S_IXOTH) : 0)
        );
      } else if(mode.length() == 9) {
        bool isdir = mode.find('X') == std::string::npos ? false : is_dir(path);
        return 0 == ::chmod(path.c_str(), 0
          | ((mode[0]=='r') ? S_IRUSR : 0)
          | ((mode[1]=='w') ? S_IWUSR : 0)
          | (((mode[2]=='x') || (isdir && (mode[2]=='X'))) ? S_IXUSR : 0)
          | ((mode[3]=='r') ? S_IRGRP : 0)
          | ((mode[4]=='w') ? S_IWGRP : 0)
          | (((mode[5]=='x') || (isdir && (mode[5]=='X'))) ? S_IXGRP : 0)
          | ((mode[6]=='r') ? S_IROTH : 0)
          | ((mode[7]=='w') ? S_IWOTH : 0)
          | (((mode[8]=='x') || (isdir && (mode[8]=='X'))) ? S_IXOTH : 0)
        );
      } else {
        // maybe implement u+r,g=r .... some time
        return false;
      }
    } else {
      return false;
    }
    #endif
  }
 
  /**
   * Gets an envoronment variable, returns "" if not found
   * @param const str_type & variable
   * @return str_type
   */
  static inline str_type getenv(const str_type & variable)
  {
    #ifdef OS_WIN
    char *p;
    size_t l;
    str_type s;
    s.reserve(256); // possible mem fault throw before allocating p.
    if(_dupenv_s(&p, &l, variable.c_str()) != 0 || !p || !l) return "";
    s = p;
    free(p);
      return s;
    #else
    const char* s = ::getenv(variable.c_str());
    return s ? s : "";
    #endif
  }
 
  /**
   * Sets an environment variable, the variable must not constain
   * a '='. Returns success.
   * @param const str_type & variable
   * @param const str_type & value
   * @return bool
   */
  static inline bool setenv(const str_type & variable, const str_type & value)
  {
    #if defined OS_WIN || defined OS_WIN_MINGW
    str_type s = variable + "=";
    s += value;
    return _putenv(s.c_str()) == 0;
    #else
    if(variable.empty()) return false;
    return ::setenv(variable.c_str(), value.c_str(), true) == 0;
    #endif
  }
 
  /**
   * Read a directory into a vector. Return success. By default, all entries except ".." and "."
   * are listed, including fifos and special nodes. When setting either `list_dirs=true` or
   * `list_reg_and_links=true` or both, only the selected types are included in the result.
   * @param str_type path
   * @param std::vector<str_type>& entries
   * @param bool list_dirs=false
   * @param bool list_regular_and_links=false
   * @return bool
   */
  static bool readdir(const str_type& path, std::vector<str_type>& entries,
                      bool list_reg_and_links=false, bool list_dirs=false)
  {
    #if !defined(OS_WIN) || defined(OS_WIN_MINGW)
    struct ::dirent* entry = 0;
    DIR* dptr = ::opendir(path.c_str());
    if(!dptr) return false;
    try {
      if(!list_dirs && !list_reg_and_links) {
        while(!!(entry = ::readdir(dptr))) {
          // note: if d_name[0]!='\0' d_name[1] cannot be a bad access (null terminated string).
          const char* s = entry->d_name;
          if((!s) || ((s[0]=='.') && ((s[1]=='.') || (s[1]=='\0')))) continue;
          entries.push_back(s);
        }
      } else {
        // OpenGroup guarantees only d_name and d_ino, not d_type
        #if defined(DT_DIR) && defined(DT_REG) && defined(DT_LNK)
        char mask = (list_dirs ? DT_DIR : 0) | (list_reg_and_links ? (DT_REG|DT_LNK) : 0);
        while(!!(entry = ::readdir(dptr))) {
          if(!(entry->d_type & mask)) continue;
          const char* s = entry->d_name;
          if((!s) || ((s[0]=='.') && ((s[1]=='.') || (s[1]=='\0')))) continue;
          entries.push_back(s);
        }
        #else
        struct ::stat st;
        while(!!(entry = ::readdir(dptr))) { // the inconvenient, slower way
          const char* s = entry->d_name;
          if((!s) || ((s[0]=='.') && ((s[1]=='.') || (s[1]=='\0')))) continue;
          if(::stat((path+"/"+s).c_str(), &st) != 0) {
            ::closedir(dptr);
            return false;
          }
          if(list_dirs && S_ISDIR(st.st_mode)) {
            entries.push_back(s);
          } else if(list_reg_and_links && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
            entries.push_back(s);
          }
        }
        #endif
      }
      ::closedir(dptr);
      return true;
    } catch(...) {
      ::closedir(dptr);
      return false; // no rethrow
    }
    #else
    HANDLE hf = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATAA fd;
    bool ldirs  = list_dirs || !list_reg_and_links;
    bool lfiles = list_reg_and_links || (!list_dirs && !list_reg_and_links);
    if((hf=FindFirstFileA(path.c_str(), &fd)) == INVALID_HANDLE_VALUE) return false;
    do {
      str_type s = fd.cFileName;
      if(s == "." || s == "..") continue;
      if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        if(ldirs) entries.push_back(s);
      } else if(lfiles) {
        entries.push_back(s);
      }
    } while(FindNextFileA(hf, &fd));
    if(GetLastError() != ERROR_NO_MORE_FILES) { FindCloseA(hf); return false; }
    FindCloseA(hf);
    return true;
    #endif
    return false;
  }
 
};
}}
 
namespace sw {
  typedef detail::basic_fs<std::string> fs;
}
 
#endif