#
# add_file "paths.cc"
#
# add_file "paths.hh"
#
# add_file "unix/fs.cc"
#
# add_file "win32/fs.cc"
#
# patch "ChangeLog"
# from [9dcbe4e861ed3436560161fb779f303b2fa3a15e]
# to [2705e63d60282d94817d19fca6ccd87d2746db79]
#
# patch "Makefile.am"
# from [1a1de954d58c4511fc0a01186c70ac86164c51bf]
# to [533ac8ab8266031422f25ff175ce7a27404a897d]
#
# patch "paths.cc"
# from []
# to [cef3c99e8f544ab5e9e1a0df448924749b7c9269]
#
# patch "paths.hh"
# from []
# to [3d3e0c5a761656484c22ad1d30ee0ed46cfcdee1]
#
# patch "platform.hh"
# from [5f5c469fcbb9f1748ed15af94855a4402b95f0c2]
# to [dab8fd68046464428f582e09be90daf606bc0161]
#
# patch "unit_tests.cc"
# from [5088d7c068c79caf69f553a295b8dbf496b6b442]
# to [88c26630e9d9d3235cd551da4d30905745300cf0]
#
# patch "unit_tests.hh"
# from [fa58467ac77ae1ee84f966c620eecf917be527b7]
# to [4bce57e49e422091790f80c5ae70b27b239c3c55]
#
# patch "unix/fs.cc"
# from []
# to [53f1074bb04337ad52b1d75f6dfa6a3c844c7309]
#
# patch "win32/fs.cc"
# from []
# to [e9d82d6608ef2f9cac4295157a2dcb298c5aecee]
#
========================================================================
--- ChangeLog 9dcbe4e861ed3436560161fb779f303b2fa3a15e
+++ ChangeLog 2705e63d60282d94817d19fca6ccd87d2746db79
@@ -1,3 +1,13 @@
+2005-08-20 Nathaniel Smith
+
+ * paths.{cc,hh}: New files.
+ * Makefile.am (MOST_SOURCES): Add them.
+ * unit_tests.hh (add_paths_tests): Declare.
+ * unit_tests.cc (init_unit_test_suite): Add them.
+ * platform.hh (get_current_working_dir)
+ (change_current_working_dir): New functions.
+ * {unix,win32}/fs.cc: New files.
+
2005-08-19 Nathaniel Smith
* monotone.texi (Tutorial): Tweak wording, use --db at more
========================================================================
--- Makefile.am 1a1de954d58c4511fc0a01186c70ac86164c51bf
+++ Makefile.am 533ac8ab8266031422f25ff175ce7a27404a897d
@@ -42,7 +42,8 @@
restrictions.cc restrictions.hh \
hmac.cc hmac.hh \
globish.cc globish.hh \
- string_queue.cc string_queue.hh \
+ string_queue.cc string_queue.hh \
+ paths.cc paths.hh \
\
cleanup.hh unit_tests.hh interner.hh \
cycle_detector.hh randomfile.hh adler32.hh quick_alloc.hh \
========================================================================
--- paths.cc
+++ paths.cc cef3c99e8f544ab5e9e1a0df448924749b7c9269
@@ -0,0 +1,149 @@
+#include "paths.hh"
+
+
+
+
+#ifdef BUILD_UNIT_TESTS
+#include "unit_tests.hh"
+
+static void test_null_name()
+{
+ BOOST_CHECK(null_name(the_null_component));
+}
+
+static void test_file_path_internal()
+{
+ char const * baddies[] = {"/foo",
+ "foo//bar",
+ "foo/../bar",
+ "../bar",
+ "MT/blah",
+ "foo/bar/",
+ "foo/./bar",
+ "./foo",
+ ".",
+ "..",
+#ifdef _WIN32
+ "c:\\foo",
+ "c:foo",
+ "c:/foo",
+#endif
+ 0 };
+ for (char const ** c = baddies; *c; ++c)
+ BOOST_CHECK_THROW(file_path(internal, *c), logic_error);
+
+ char const * goodies[] = {"",
+ "foo",
+ "foo/bar/baz",
+ "foo/bar.baz",
+ "foo/with-hyphen/bar",
+ "foo/with_underscore/bar",
+ ".foo/bar",
+ "..foo/bar",
+ 0 };
+
+ for (char const ** c = baddies; *c; ++c)
+ {
+ file_path fp(internal, *c);
+ BOOST_CHECK(fp.as_internal() == *c);
+ std::vector split_test;
+ fp.split(split_test);
+ file_path fp2(split_test);
+ BOOST_CHECK(fp == fp2);
+ for (std::vector::const_iterator i = split_test.begin();
+ i != split_test.end(); ++i)
+ BOOST_CHECK(!null_name(*i));
+ }
+}
+
+static void check_normalizes_to(char * before, char * after)
+{
+ file_path fp(external, before);
+ BOOST_CHECK(fp.as_internal() == after);
+ // we compare after to the external form too, since as far as we know
+ // relative normalized posix paths are always good win32 paths too
+ BOOST_CHECK(fp.as_external() == after);
+ std::vector split_test;
+ fp.split(split_test);
+ file_path fp2(split_test);
+ BOOST_CHECK(fp == fp2);
+ for (std::vector::const_iterator i = split_test.begin();
+ i != split_test.end(); ++i)
+ BOOST_CHECK(!null_name(*i));
+}
+
+static void test_file_path_external()
+{
+ char const * baddies[] = {"/foo",
+ "../bar",
+ "MT/blah",
+ "//blah",
+ "..",
+#ifdef _WIN32
+ "c:\\foo",
+ "c:foo",
+ "c:/foo",
+#endif
+ 0 };
+ for (char const ** c = baddies; *c; ++c)
+ BOOST_CHECK_THROW(file_path(internal, *c), logic_error);
+
+ check_normalizes_to("", "", "");
+ check_normalizes_to("foo", "foo");
+ check_normalizes_to("foo/bar", "foo/bar");
+ check_normalizes_to("foo/bar/baz", "foo/bar/baz");
+ check_normalizes_to("foo/bar.baz", "foo/bar.baz");
+ check_normalizes_to("foo/with-hyphen/bar", "foo/with-hyphen/bar");
+ check_normalizes_to("foo/with_underscore/bar", "foo/with_underscore/bar");
+ check_normalizes_to(".foo/bar", ".foo/bar");
+ check_normalizes_to("..foo/bar", "..foo/bar");
+ check_normalizes_to(".", "");
+
+ check_normalizes_to("foo//bar", "foo/bar");
+ check_normalizes_to("foo/../bar", "bar");
+ check_normalizes_to("foo/bar/", "foo/bar");
+ check_normalizes_to("foo/./bar/", "foo/bar");
+ check_normalizes_to("./foo", "foo");
+ check_normalizes_to("foo///.//", "foo");
+}
+
+static void test_split_join()
+{
+ file_path fp1(internal, "foo/bar/baz");
+ file_path fp2(internal, "bar/baz/foo");
+ typedef std::vector pcv;
+ pcv split1, split2;
+ fp1.split(split1);
+ fp2.split(split2);
+ BOOST_CHECK(fp1 == file_path(split1));
+ BOOST_CHECK(fp2 == file_path(split2));
+ BOOST_CHECK(fp1 != file_path(split2));
+ BOOST_CHECK(fp2 != file_path(split1));
+ BOOST_CHECK(split1.size() == 3);
+ BOOST_CHECK(split2.size() == 3);
+ BOOST_CHECK(split1[0] != split1[1]);
+ BOOST_CHECK(split1[0] != split1[2]);
+ BOOST_CHECK(split1[1] != split1[2]);
+ BOOST_CHECK(!null_name(split1[0])
+ && !null_name(split1[1])
+ && !null_name(split1[2]));
+ BOOST_CHECK(split1[0] == split2[2]);
+ BOOST_CHECK(split1[1] == split2[0]);
+ BOOST_CHECK(split1[2] == split2[1]);
+
+ file_path fp3(internal, "");
+ pcv split3;
+ fp3.split(split3);
+ BOOST_CHECK(split3.size() == 0);
+ BOOST_CHECK(fp3 == file_path(split3));
+}
+
+void add_paths_tests(test_suite * suite)
+{
+ I(suite);
+ suite->add(BOOST_TEST_CASE(&test_null_name));
+ suite->add(BOOST_TEST_CASE(&test_file_path_internal));
+ suite->add(BOOST_TEST_CASE(&test_file_path_external));
+}
+
+#endif // BUILD_UNIT_TESTS
========================================================================
--- paths.hh
+++ paths.hh 3d3e0c5a761656484c22ad1d30ee0ed46cfcdee1
@@ -0,0 +1,95 @@
+#ifndef __PATHS_H__
+#define __PATHS_H__
+
+// copyright (C) 2005 nathaniel smith
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+// safe, portable, fast, simple file handling -- in that order
+
+#include
+#include
+
+#include "numeric_vocab.hh"
+#include "vocab.hh"
+
+typedef u32 path_component;
+
+const path_component the_null_component = 0;
+
+inline bool
+null_name(path_component pc)
+{
+ return pc == the_null_component;
+}
+
+void
+save_initial_path();
+
+// returns true if working copy found, in which case cwd has been changed
+// returns false if working copy not found
+bool
+find_and_go_to_working_copy(external_path const & search_root);
+
+
+class file_path
+{
+public:
+ enum { internal, external } source_type;
+ // input is always in utf8, because everything in our world is always in
+ // utf8 (except interface code itself).
+ // external paths:
+ // -- are converted to internal syntax (/ rather than \, etc.)
+ // -- normalized
+ // -- assumed to be relative to the user's cwd, and munged
+ // to become relative to root of the working copy instead
+ // both types of paths:
+ // -- are confirmed to be normalized and relative
+ // -- not to be in MT/
+ file_path(source_type type, std::string const & path);
+ // join a file_path out of pieces
+ file_path(std::vector const & pieces);
+
+ // returns raw normalized path string
+ std::string const & as_internal();
+ // converts to native charset and path syntax
+ std::string const & as_external();
+
+ void split(std::vector & pieces);
+
+ bool operator ==(const file_path & other)
+ { return data == other.data; }
+
+ bool operator !=(const file_path & other)
+ { return data != other.data; }
+
+ bool operator <(const file_path & other)
+ { return data < other.data; }
+
+private:
+ // this string is always stored in normalized etc. form; so the generated
+ // copy constructor and assignment operator are fine.
+ std::string data;
+};
+
+class bookkeeping_path
+{
+public:
+ // path should _not_ contain the leading MT/
+ bookkeeping_path(std::string const & path);
+ std::string const & as_external();
+private:
+ std::string data;
+}
+
+class external_path
+{
+public:
+ // this path will
+ external_path(std::string const & path);
+ // this will always be an absolute path
+ std::string const & as_external();
+private:
+ std::string data;
+}
========================================================================
--- platform.hh 5f5c469fcbb9f1748ed15af94855a4402b95f0c2
+++ platform.hh dab8fd68046464428f582e09be90daf606bc0161
@@ -46,4 +46,8 @@
// for netsync 'serve' pidfile support
pid_t get_process_id();
+// filesystem stuff
+std::string get_current_working_dir();
+void change_current_working_dir(std::string const & to);
+
#endif // __PLATFORM_HH__
========================================================================
--- unit_tests.cc 5088d7c068c79caf69f553a295b8dbf496b6b442
+++ unit_tests.cc 88c26630e9d9d3235cd551da4d30905745300cf0
@@ -86,6 +86,9 @@
if (t.empty() || t.find("string_queue") != t.end())
add_string_queue_tests(suite);
+ if (t.empty() || t.find("paths") != t.end())
+ add_paths_tests(suite);
+
// all done, add our clean-shutdown-indicator
suite->add(BOOST_TEST_CASE(&clean_shutdown_dummy_test));
========================================================================
--- unit_tests.hh fa58467ac77ae1ee84f966c620eecf917be527b7
+++ unit_tests.hh 4bce57e49e422091790f80c5ae70b27b239c3c55
@@ -35,5 +35,6 @@
void add_globish_tests(test_suite * suite);
void add_crypto_tests(test_suite * suite);
void add_string_queue_tests(test_suite * suite);
+void add_paths_tests(test_suite * suite);
#endif
========================================================================
--- unix/fs.cc
+++ unix/fs.cc 53f1074bb04337ad52b1d75f6dfa6a3c844c7309
@@ -0,0 +1,24 @@
+// copyright (C) 2005 nathaniel smith
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+#include
+#include
+
+#include "sanity.hh"
+#include "platform.hh"
+
+std::string get_current_working_dir()
+{
+ char buffer[4096];
+ E(getcwd(buffer, 4096),
+ F("cannot get working directory: %s") % strerror(errno));
+ return std::string(buffer);
+}
+
+void change_current_working_dir(std::string const & to)
+{
+ E(!chdir(to.c_str()),
+ F("cannot change to directory %s: %s") % to % strerror(errno));
+}
========================================================================
--- win32/fs.cc
+++ win32/fs.cc e9d82d6608ef2f9cac4295157a2dcb298c5aecee
@@ -0,0 +1,24 @@
+// copyright (C) 2005 nathaniel smith
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+#include
+#include
+
+#include "sanity.hh"
+#include "platform.hh"
+
+std::string get_current_working_dir()
+{
+ char buffer[4096];
+ E(getcwd(buffer, 4096),
+ F("cannot get working directory: %s") % strerror(errno));
+ return std::string(buffer);
+}
+
+void change_current_working_dir(std::string const & to)
+{
+ E(!chdir(to.c_str()),
+ F("cannot change to directory %s: %s") % to % strerror(errno));
+}