diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2020-03-07 14:07:28 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2020-03-09 14:18:20 +0300 |
commit | dcccba655fe848564e961b3f285ce3a82d3ac73a (patch) | |
tree | 598ced3b406d80c23798672930e1a17cfe112b75 /tests/path-entry/driver.cxx | |
parent | 63b2988e4f2630cc688ff43b7e5f0d4f977896cd (diff) |
Add more support for symlinks on Windows
See mksymlink() for details of the symlinks support on Windows.
Diffstat (limited to 'tests/path-entry/driver.cxx')
-rw-r--r-- | tests/path-entry/driver.cxx | 212 |
1 files changed, 188 insertions, 24 deletions
diff --git a/tests/path-entry/driver.cxx b/tests/path-entry/driver.cxx index d48bf49..51ac04d 100644 --- a/tests/path-entry/driver.cxx +++ b/tests/path-entry/driver.cxx @@ -4,7 +4,9 @@ #include <cassert> #ifndef __cpp_lib_modules_ts +#include <string> #include <iostream> +#include <stdexcept> // invalid_argument #include <system_error> #endif @@ -15,48 +17,210 @@ import std.core; import std.io; #endif +import butl.path; import butl.utility; // operator<<(ostream, exception) +import butl.optional; +import butl.timestamp; import butl.filesystem; #else +#include <libbutl/path.mxx> #include <libbutl/utility.mxx> +#include <libbutl/optional.mxx> +#include <libbutl/timestamp.mxx> #include <libbutl/filesystem.mxx> #endif using namespace std; using namespace butl; -// Usage: argv[0] <path> +// Usage: argv[0] [-l] [-t] [-p <permissions>] [-m <time>] [-a <time>] <path> // -// If path entry exists then print it's type and size (meaningful for the -// regular file only) to STDOUT, and exit with the zero code. Otherwise exit -// with the one code. Don't follow symlink. On failure print the error -// description to STDERR and exit with the two code. +// If path entry exists then optionally modify its meta-information and print +// its type, size (meaningful for the regular file only), permissions, +// modification and access times to STDOUT, one value per line, and exit with +// the zero code. Otherwise exit with the one code. Don't follow symlink by +// default. On failure print the error description to STDERR and exit with +// the two code. +// +// -l +// Follow symlinks. +// +// -t +// Assume the path is a file and touch it. Implies -l. +// +// -p <permissions> +// Set path permissions specified in the chmod utility octal form. Implies +// -l. +// +// -m <time> +// Set path modification time specified in the "%Y-%m-%d %H:%M:%S%[.N]" +// format. Implies -l. +// +// -a <time> +// As -m but set the access time. // int main (int argc, const char* argv[]) -try { - assert (argc == 2); + string stage; + + try + { + using butl::optional; + + bool follow_symlinks (false); + optional<permissions> perms; + optional<timestamp> mtime; + optional<timestamp> atime; + bool touch (false); + + auto time = [] (const char* v) + { + return from_string (v, "%Y-%m-%d %H:%M:%S%[.N]", true /* local */); + }; + + int i (1); + for (; i != argc; ++i) + { + string v (argv[i]); + + if (v == "-l") + follow_symlinks = true; + else if (v == "-t") + { + touch = true; + follow_symlinks = true; + } + else if (v == "-p") + { + assert (++i != argc); + v = argv[i]; + + size_t n; + perms = static_cast<permissions> (stoull (v, &n, 8)); + assert (n == v.size ()); + + follow_symlinks = true; + } + else if (v == "-m") + { + assert (++i != argc); + mtime = time (argv[i]); + + follow_symlinks = true; + } + else if (v == "-a") + { + assert (++i != argc); + atime = time (argv[i]); + + follow_symlinks = true; + } + else + break; + } + + assert (i == argc - 1); + + path p (argv[i]); + + if (touch) + { + stage = "touch"; + touch_file (p); + } + + stage = "stat entry"; + pair<bool, entry_stat> es (path_entry (p, follow_symlinks)); - auto es (path_entry (argv[1])); + if (!es.first) + return 1; - if (!es.first) - return 1; + // The entry is a directory with a symlink followed. + // + bool tdir; - switch (es.second.type) + if (follow_symlinks || es.second.type != entry_type::symlink) + tdir = (es.second.type == entry_type::directory); + else + { + stage = "stat target"; + pair<bool, entry_stat> ts (path_entry (p, true /* follow_symlinks */)); + + if (!ts.first) + return 1; + + tdir = (ts.second.type == entry_type::directory); + } + + if (perms) + { + stage = "set permissions"; + path_permissions (p, *perms); + } + + if (mtime) + { + if (tdir) + { + stage = "set directory mtime"; + dir_mtime (path_cast<dir_path> (p), *mtime); + } + else + { + stage = "set file mtime"; + file_mtime (p, *mtime); + } + } + + if (atime) + { + if (tdir) + { + stage = "set directory atime"; + dir_atime (path_cast<dir_path> (p), *atime); + } + else + { + stage = "set file atime"; + file_atime (p, *atime); + } + } + + cout << "type: "; + + switch (es.second.type) + { + case entry_type::unknown: cout << "unknown"; break; + case entry_type::regular: cout << "regular"; break; + case entry_type::directory: cout << "directory"; break; + case entry_type::symlink: cout << "symlink"; break; + case entry_type::other: cout << "other"; break; + } + + stage = "get permissions"; + + cout << endl + << "size: " << es.second.size << endl + << "permissions: " << oct + << static_cast<size_t> (path_permissions (p)) << endl; + + stage = tdir ? "get directory times" : "get file times"; + + entry_time et (tdir ? dir_time (path_cast<dir_path> (p)) : file_time (p)); + cout << "mtime: " << et.modification << endl + << "atime: " << et.access << endl; + + return 0; + } + catch (const invalid_argument& e) { - case entry_type::unknown: cout << "unknown"; break; - case entry_type::regular: cout << "regular"; break; - case entry_type::directory: cout << "directory"; break; - case entry_type::symlink: cout << "symlink"; break; - case entry_type::other: cout << "other"; break; + cerr << e << endl; + return 2; + } + catch (const system_error& e) + { + cerr << stage << " failed: " << e << endl; + return 2; } - - cout << endl << es.second.size << endl; - return 0; -} -catch (const system_error& e) -{ - cerr << e << endl; - return 2; } |