aboutsummaryrefslogtreecommitdiff
path: root/tests/path-entry/driver.cxx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-03-07 14:07:28 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-03-09 14:18:20 +0300
commitdcccba655fe848564e961b3f285ce3a82d3ac73a (patch)
tree598ced3b406d80c23798672930e1a17cfe112b75 /tests/path-entry/driver.cxx
parent63b2988e4f2630cc688ff43b7e5f0d4f977896cd (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.cxx212
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;
}