# # 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)); +}