# # patch "ChangeLog" # from [09fc1962e328b98b40e53a69fd715d68265279ff] # to [14c949f934bdc8eb70186ad02c92866c2ef5d006] # # patch "database.cc" # from [8d0f3f9f112b640c2eb877595fbce56e8b04ac02] # to [5d925a861c0fd595434f76a7d916e609fcfd4859] # # patch "database.hh" # from [14ba00bff0d68669687755c58a3b35172333cfe1] # to [8cc3e02dbed0ddc46ba6c32b8572c316340eb282] # # patch "file_io.cc" # from [442fc17a140ef95f024c4aa58a22aa4a924a3cb8] # to [a3744fefaa6ad87cfefecaed427b21adc16581bd] # # patch "file_io.hh" # from [5712c71aa04e18828ae63e33ed0e0030d4d40e8d] # to [c235365c658cc4716e935087ada543b663f7bb0c] # # patch "monotone.cc" # from [25377c90318075893420d6062e3471d6c14cd084] # to [604d5b80f18d40961ae6d589064c7791cc3def71] # # patch "paths.cc" # from [323736ebeb967accea41ef853150771414055d66] # to [1da5513341ff15b2887ebd5040934405aa5d4e00] # # patch "paths.hh" # from [ae48a5f399ef3c5e157acbce535415fca6b67715] # to [d1545b2288da082ff04bde7e36760748079f9053] # # patch "vocab.cc" # from [705631e329160226f4ef065c411ac42c22dd56c1] # to [cebf734fb6a83a66665786e2c1486d4934137066] # # patch "vocab_terms.hh" # from [fe3bf7a7700bdde82211ba0be39caf248025e00b] # to [9abc8225074b278d3b7a57c291ab5d8fd0a98ea6] # ======================================================================== --- ChangeLog 09fc1962e328b98b40e53a69fd715d68265279ff +++ ChangeLog 14c949f934bdc8eb70186ad02c92866c2ef5d006 @@ -1,5 +1,11 @@ 2005-08-23 Nathaniel Smith + * vocab_terms.hh, vocab.cc: Remove file_path, local_path. + * database.{hh,cc}, monotone.cc: Convert to paths.hh. + * file_io.{hh,cc}: Start to convert to paths.hh. + +2005-08-23 Nathaniel Smith + * paths.{cc,hh} (fully_normalized_path): Implement. (external_path): Rename to system_path. Misc. other updates. ======================================================================== --- database.cc 8d0f3f9f112b640c2eb877595fbce56e8b04ac02 +++ database.cc 5d925a861c0fd595434f76a7d916e609fcfd4859 @@ -15,8 +15,6 @@ #include #include -#include -#include #include @@ -61,7 +59,7 @@ const char *sqlite3_column_text_s(sqlite3_stmt*, int col); } -database::database(fs::path const & fn) : +database::database(system_path const & fn) : filename(fn), // nb. update this if you change the schema. unfortunately we are not // using self-digesting schemas due to comment irregularities and @@ -131,19 +129,22 @@ } static void -check_sqlite_format_version(fs::path const & filename) +check_sqlite_format_version(system_path const & filename) { - if (fs::exists(filename)) + switch (path_state(filename)) { - N(!fs::is_directory(filename), - F("database %s is a directory\n") % filename.string()); - + path::nonexistent: + return; + path::directory: + N(false, F("database %s is a directory") % filename); + break; + path::file: // sqlite 3 files begin with this constant string // (version 2 files begin with a different one) std::string version_string("SQLite format 3"); - std::ifstream file(filename.string().c_str()); - N(file, F("unable to probe database version in file %s") % filename.string()); + std::ifstream file(filename.as_external().c_str()); + N(file, F("unable to probe database version in file %s") % filename); for (std::string::const_iterator i = version_string.begin(); i != version_string.end(); ++i) @@ -151,7 +152,7 @@ char c; file.get(c); N(c == *i, F("database %s is not an sqlite version 3 file, " - "try dump and reload") % filename.string()); + "try dump and reload") % filename); } } } @@ -178,18 +179,19 @@ if (! init) { - N(fs::exists(filename), - F("database %s does not exist") % filename.string()); - N(!fs::is_directory(filename), - F("database %s is a directory") % filename.string()); + path::state state = path_state(filename); + N(state != path::nonexistent, + F("database %s does not exist") % filename); + N(state != path::directory, + F("database %s is a directory") % filename); } check_sqlite_format_version(filename); int error; - error = sqlite3_open(filename.string().c_str(), &__sql); + error = sqlite3_open(filename.as_external().c_str(), &__sql); if (error) - throw oops(string("could not open database: ") + filename.string() + - (": " + string(sqlite3_errmsg(__sql)))); + throw oops((F("could not open database: %s: %s") + % filename % sqlite3_errmsg(__sql)).str()); if (init) { sqlite3_exec(__sql, schema_constant, NULL, NULL, NULL); @@ -209,15 +211,15 @@ if (__sql) throw oops("cannot initialize database while it is open"); - N(!fs::exists(filename), + N(!path_state(filename), F("could not initialize database: %s: already exists") - % filename.string()); + % filename); - fs::path journal = mkpath(filename.string() + "-journal"); - N(!fs::exists(journal), + system_path journal(filename.as_external() + "-journal"); + N(!path_state(journal), F("existing (possibly stale) journal file '%s' " "has same stem as new database '%s'") - % journal.string() % filename.string()); + % journal % filename); sqlite3 *s = sql(true); I(s != NULL); @@ -330,14 +332,14 @@ char buf[constants::bufsz]; string tmp; - N(filename.string() != "", + N(!filename.empty(), F("need database name")); - N(!fs::exists(filename), - F("cannot create %s; it already exists\n") % filename.string()); - int error = sqlite3_open(filename.string().c_str(), &__sql); + N(!path_state(filename), + F("cannot create %s; it already exists") % filename); + int error = sqlite3_open(filename.as_external().c_str(), &__sql); if (error) - throw oops(string("could not open database: ") + filename.string() + - (string(sqlite3_errmsg(__sql)))); + throw oops((F("could not open database: %s: %s") + % filename % sqlite3_errmsg(__sql)).str()); while(in) { @@ -431,14 +433,14 @@ database::version(ostream & out) { string id; - N(filename.string() != "", + N(!filename.empty(), F("need database name")); - int error = sqlite3_open(filename.string().c_str(), &__sql); + int error = sqlite3_open(filename.as_external().c_str(), &__sql); if (error) { sqlite3_close(__sql); - throw oops(string("could not open database: ") + filename.string() + - (": " + string(sqlite3_errmsg(__sql)))); + throw oops((F("could not open database: %s: %s") + % filename % sqlite3_errmsg(__sql)).str()); } calculate_schema_id(__sql, id); sqlite3_close(__sql); @@ -448,14 +450,14 @@ void database::migrate() { - N(filename.string() != "", + N(!filename.empty(), F("need database name")); - int error = sqlite3_open(filename.string().c_str(), &__sql); + int error = sqlite3_open(filename.as_external().c_str(), &__sql); if (error) { sqlite3_close(__sql); - throw oops(string("could not open database: ") + filename.string() + - (": " + string(sqlite3_errmsg(__sql)))); + throw oops((F("could not open database: %s: %s") + % filename % sqlite3_errmsg(__sql)).str()); } migrate_monotone_schema(__sql); sqlite3_close(__sql); @@ -660,11 +662,11 @@ // general application-level logic void -database::set_filename(fs::path const & file) +database::set_filename(system_path const & file) { if (__sql) { - throw oops("cannot change filename to " + file.string() + " while db is open"); + throw oops("cannot change filename to " + file + " while db is open"); } filename = file; } ======================================================================== --- database.hh 14ba00bff0d68669687755c58a3b35172333cfe1 +++ database.hh 8cc3e02dbed0ddc46ba6c32b8572c316340eb282 @@ -17,12 +17,11 @@ #include #include -#include - #include "selectors.hh" #include "manifest.hh" #include "numeric_vocab.hh" #include "vocab.hh" +#include "paths.hh" struct revision_set; @@ -71,7 +70,7 @@ class database { - fs::path filename; + system_path filename; std::string const schema; void check_schema(); @@ -202,9 +201,9 @@ public: - database(fs::path const & file); + database(system_path const & file); - void set_filename(fs::path const & file); + void set_filename(system_path const & file); void initialize(); void debug(std::string const & sql, std::ostream & out); void dump(std::ostream &); ======================================================================== --- file_io.cc 442fc17a140ef95f024c4aa58a22aa4a924a3cb8 +++ file_io.cc a3744fefaa6ad87cfefecaed427b21adc16581bd @@ -31,13 +31,6 @@ #endif #include -void -save_initial_path() -{ - fs::initial_path(); - L(F("initial path is %s\n") % fs::initial_path().string()); -} - bool find_working_copy(fs::path const & search_root, fs::path & working_copy_root, @@ -97,13 +90,6 @@ return true; } -fs::path -mkpath(string const & s) -{ - fs::path p(s, fs::native); - return p; -} - string get_homedir() { ======================================================================== --- file_io.hh 5712c71aa04e18828ae63e33ed0e0030d4d40e8d +++ file_io.hh c235365c658cc4716e935087ada543b663f7bb0c @@ -37,26 +37,38 @@ struct lua_hooks; -void save_initial_path(); bool find_working_copy(fs::path const & search_root, fs::path & working_copy_root, fs::path & working_copy_restriction); -fs::path mkpath(std::string const & s); - std::string get_homedir(); std::string absolutify(std::string const & path); std::string absolutify_for_command_line(std::string const & path); std::string tilde_expand(std::string const & path); -extern std::string const book_keeping_dir; - // - file is inside the private MT/ directory bool book_keeping_file(local_path const & path); -bool directory_exists(local_path const & path); +// A path::state can be used as a boolean context for exists/doesn't exist, +// or can be used in a switch statement to consider all possibilities. +namespace path +{ + typedef enum + { + nonexistent = 0; + directory = 1; + file = 2; + } state; +} +path::state path_state_raw_(std::string const & path_raw); +template path::state path_state(T const & path) +{ + return path_state_raw_(path.as_external()); +} + +bool directory_exists(bookkeeping_path const & path); bool directory_exists(file_path const & path); -bool file_exists(local_path const & path); +bool file_exists(bookkeeping_path const & path); bool file_exists(file_path const & path); bool ident_existing_file(file_path const & p, file_id & ident, lua_hooks & lua); @@ -64,14 +76,14 @@ // returns true if the string content is binary according to monotone euristic bool guess_binary(std::string const & s); -void mkdir_p(local_path const & path); +void mkdir_p(bookkeeping_path const & path); void mkdir_p(file_path const & path); void make_dir_for(file_path const & p); void delete_file(file_path const & path); -void delete_file(local_path const & path); +void delete_file(bookkeeping_path const & path); void delete_dir_recursive(file_path const & path); -void delete_dir_recursive(local_path const & path); +void delete_dir_recursive(bookkeeping_path const & path); void move_file(file_path const & old_path, file_path const & new_path); ======================================================================== --- monotone.cc 25377c90318075893420d6062e3471d6c14cd084 +++ monotone.cc 604d5b80f18d40961ae6d589064c7791cc3def71 @@ -30,6 +30,7 @@ #include "ui.hh" #include "mt_version.hh" #include "options.hh" +#include "paths.hh" // main option processing and exception handling code ======================================================================== --- paths.cc 323736ebeb967accea41ef853150771414055d66 +++ paths.cc 1da5513341ff15b2887ebd5040934405aa5d4e00 @@ -14,6 +14,7 @@ save_initial_path() { initial_path = get_current_working_dir(); + L(F("initial path is: %s") % initial_path); } // path to prepend to external files, to convert them from pointing to the ======================================================================== --- paths.hh ae48a5f399ef3c5e157acbce535415fca6b67715 +++ paths.hh d1545b2288da082ff04bde7e36760748079f9053 @@ -8,6 +8,7 @@ // safe, portable, fast, simple file handling -- in that order +#include #include #include @@ -27,7 +28,7 @@ class file_path { public: - enum { internal, external } source_type; + typedef 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: @@ -43,19 +44,19 @@ file_path(std::vector const & pieces); // returns raw normalized path string - std::string const & as_internal(); + std::string const & as_internal() const; // converts to native charset and path syntax - std::string const & as_external(); + std::string const & as_external() const; - void split(std::vector & pieces); + void split(std::vector & pieces) const; - bool operator ==(const file_path & other) + bool operator ==(const file_path & other) const { return data == other.data; } - bool operator !=(const file_path & other) + bool operator !=(const file_path & other) const { return data != other.data; } - bool operator <(const file_path & other) + bool operator <(const file_path & other) const { return data < other.data; } private: @@ -64,17 +65,21 @@ std::string data; }; +std::ostream & operator<<(ostream & o, file_path const & a); + class bookkeeping_path { public: // path should _not_ contain the leading MT/ // and _should_ look like an internal path bookkeeping_path(std::string const & path); - std::string const & as_external(); + std::string const & as_external() const; private: std::string data; }; +std::ostream & operator<<(ostream & o, bookkeeping_path const & a); + class system_path { public: @@ -82,12 +87,15 @@ // tilde-expanded. it should be in utf8. system_path(std::string const & path); // this will always be an absolute path, in the local character set - std::string const & as_external(); + std::string const & as_external() const; + bool empty() const; private: std::string data; }; +std::ostream & operator<<(ostream & o, system_path const & a); + void save_initial_path(); ======================================================================== --- vocab.cc 705631e329160226f4ef065c411ac42c22dd56c1 +++ vocab.cc cebf734fb6a83a66665786e2c1486d4934137066 @@ -5,12 +5,8 @@ #include #include -#include -#include -#include #include "constants.hh" -#include "file_io.hh" #include "sanity.hh" #include "vocab.hh" @@ -139,118 +135,6 @@ } -inline void -verify(local_path & val) -{ - - if (val.ok) - return; - - using boost::filesystem::path; - boost::filesystem::path p; - try - { - p = mkpath(val()); - p = p.normalize(); - } - catch (std::runtime_error &re) - { - throw informative_failure(re.what()); - } - catch (fs::filesystem_error &fse) - { - throw informative_failure(fse.what()); - } - - N(! (p.has_root_path() || p.has_root_name() || p.has_root_directory()), - F("prohibited absolute path '%s'") % val); - - for(path::iterator i = p.begin(); i != p.end(); ++i) - { - N(!( *i == "" && (! p.empty())), - F("empty path component in '%s'") % val); - - N((*i != ".."), - F("prohibited path component '%s' in '%s'") % *i % val); - - string::size_type pos = i->find_first_of(constants::illegal_path_bytes); - N(pos == string::npos, - F("bad character '%d' in path component '%s' of '%s'") - % static_cast(i->at(pos)) % *i % val); - - string s = val(); - for (string::const_iterator j = s.begin(); j != s.end(); ++j) - N(*j != '\0', - F("null byte in path component '%s' of '%s'") % *i % val); - - } - - // save back the normalized string - val.s = p.string(); - - val.ok = true; -} - -// returns true if the given string is obviously a normalized file path (no -// . or .. components, a relative path, no doubled //s, does not end in /, -// does not start with MT) -inline bool -trivially_safe_file_path(std::string const & f) -{ - const static std::string bad_chars = std::string("\\:") + constants::illegal_path_bytes + std::string(1, '\0'); - const static char sep_char('/'); - const static std::string bad_after_sep_chars("./"); - if (f.empty()) - return true; - char prev = sep_char; - for (std::string::const_iterator i = f.begin(); i != f.end(); ++i) - { - if (bad_chars.find(*i) != std::string::npos) - return false; - if (prev == sep_char && bad_after_sep_chars.find(*i) != std::string::npos) - return false; - prev = *i; - } - if (prev == sep_char) - return false; - if (f.size() >= 2 && f[0] == 'M' && f[1] == 'T') - return false; - return true; -} - -inline void -verify(file_path & val) -{ - static std::map known_good; - - if (val.ok) - return; - - std::map::const_iterator j = known_good.find(val()); - if (j == known_good.end()) - { - if (trivially_safe_file_path(val())) - known_good.insert(std::make_pair(val(), val())); - else - { - local_path loc(val()); - verify(loc); - N(!book_keeping_file(loc), - F("prohibited book-keeping path in '%s'") % val); - const std::string & normalized_val = loc(); - val.s = normalized_val; - known_good.insert(std::make_pair(val(), normalized_val)); - } - } - else - { - val.s = j->second; - } - - val.ok = true; -} - - // instantiation of various vocab functions #define ATOMIC(ty) \ @@ -349,60 +233,10 @@ #ifdef BUILD_UNIT_TESTS #include "unit_tests.hh" -static void test_file_path_verification() -{ - char const * baddies [] = {"../escape", - "foo/../../escape", - "/rooted", - "foo//nonsense", - "MT/foo", -#ifdef _WIN32 - "c:\\windows\\rooted", - "c:/windows/rooted", - "c:thing", - "//unc/share", - "//unc", -#endif - 0 }; - - for (char const ** c = baddies; *c; ++c) - BOOST_CHECK_THROW(file_path p(*c), informative_failure); - - char const * bad = "\t\r\n\v\f\a\b"; - char badboy[] = "bad"; - for (char const * c = bad; *c; ++c) - { - badboy[1] = *c; - BOOST_CHECK_THROW(file_path p(badboy), informative_failure); - } - BOOST_CHECK_THROW(file_path p(std::string(1, '\0')), informative_failure); - - char const * goodies [] = {"unrooted", - "unrooted.txt", - "fun_with_underscore.png", - "fun-with-hyphen.tiff", - "unrooted/../unescaping", - "unrooted/general/path", - "here/..", - 0 }; - - for (char const ** c = goodies; *c; ++c) - BOOST_CHECK_NOT_THROW(file_path p(*c), informative_failure); -} - -static void test_file_path_normalization() -{ - BOOST_CHECK(file_path("./foo") == file_path("foo")); - BOOST_CHECK(file_path("foo/bar/./baz") == file_path("foo/bar/baz")); - BOOST_CHECK(file_path("foo/bar/../baz") == file_path("foo/baz")); - BOOST_CHECK(file_path("foo/bar/baz/") == file_path("foo/bar/baz")); -} - void add_vocab_tests(test_suite * suite) { I(suite); - suite->add(BOOST_TEST_CASE(&test_file_path_verification)); - suite->add(BOOST_TEST_CASE(&test_file_path_normalization)); + // None, ATM. } #endif // BUILD_UNIT_TESTS ======================================================================== --- vocab_terms.hh fe3bf7a7700bdde82211ba0be39caf248025e00b +++ vocab_terms.hh 9abc8225074b278d3b7a57c291ab5d8fd0a98ea6 @@ -16,9 +16,6 @@ ATOMIC_NOVERIFY(delta); // xdelta between 2 datas ATOMIC_NOVERIFY(inodeprint); // fingerprint of an inode -ATOMIC(local_path); // non-absolute file -ATOMIC(file_path); // non-absolute, non-bookeeping file - ATOMIC(cert_name); // symbol-of-your-choosing ATOMIC_NOVERIFY(cert_value); // symbol-of-your-choosing