# # patch "ChangeLog" # from [8f2a62bfcfeb4e9e940900bcb0a4f10369543fed] # to [454261476b91dc79a88e1459c196a54422cf3584] # # patch "commands.cc" # from [3e72d872034c018c68dd5ba798fc5e60fc365ac1] # to [ce68f95207372b740bfff6dbafcec35d06b74cfe] # # patch "paths.cc" # from [25d56ee1e15ec27d97d9c8740b99f870e1c5f686] # to [46c53a99110e170871cb92346f6d32e5995e852c] # # patch "paths.hh" # from [90103ad12a83f237c1fc7579e21562be87c533fd] # to [16661900264cb8d876b8a98b0adde390bda5d88c] # ======================================================================== --- ChangeLog 8f2a62bfcfeb4e9e940900bcb0a4f10369543fed +++ ChangeLog 454261476b91dc79a88e1459c196a54422cf3584 @@ -1,5 +1,13 @@ 2005-08-26 Nathaniel Smith + * paths.hh (file_path_internal_from_user): New constructor. + * paths.cc (test_file_path_internal): Test it. + (file_path::file_path): Implement it. + * commands.cc (cat): Use it to create/validate passed in + filenames. + +2005-08-26 Nathaniel Smith + * paths.cc (test_system_path): Require that system_path normalize out ..'s. (system_path): Do so. ======================================================================== --- commands.cc 3e72d872034c018c68dd5ba798fc5e60fc365ac1 +++ commands.cc ce68f95207372b740bfff6dbafcec35d06b74cfe @@ -1285,11 +1285,11 @@ N(app.db.file_version_exists(ident), F("no file version %s found in database") % ident); } - else + else if (args.size() == 3) { revision_id rid; complete(app, idx(args, 1)(), rid); - file_path fp = file_path_external(idx(args, 2)); + file_path fp = file_path_internal_from_user(idx(args, 2)); manifest_id mid; app.db.get_revision_manifest(rid, mid); manifest_map m; @@ -1298,6 +1298,8 @@ N(i != m.end(), F("no file '%s' found in revision '%s'\n") % fp % rid); ident = manifest_entry_id(i); } + else + throw usage(name); file_data dat; L(F("dumping file %s\n") % ident); ======================================================================== --- paths.cc 25d56ee1e15ec27d97d9c8740b99f870e1c5f686 +++ paths.cc 46c53a99110e170871cb92346f6d32e5995e852c @@ -149,6 +149,13 @@ return path == "MT" || (path.size() >= 3 && (path.substr(0, 3) == "MT/")); } +static inline bool +is_valid_internal(std::string const & path) +{ + return (fully_normalized_path(path) + && !in_bookkeeping_dir(path)); +} + file_path::file_path(file_path::source_type type, std::string const & path) { switch (type) @@ -156,6 +163,11 @@ case internal: data = path; break; + case internal_from_user: + data = path; + N(is_valid_internal(path), + F("path '%s' is invalid") % path); + break; case external: N(!path.empty(), F("empty path '%s' is invalid") % path); fs::path out, base, relative; @@ -177,9 +189,9 @@ F("absolute path '%s' is invalid") % relative.string()); N(fully_normalized_path(data()), F("path '%s' is invalid") % data); N(!in_bookkeeping_dir(data()), F("path '%s' is in bookkeeping dir") % data); + break; } - I(fully_normalized_path(data())); - I(!in_bookkeeping_dir(data())); + I(is_valid_internal(data())); } bookkeeping_path::bookkeeping_path(std::string const & path) @@ -503,11 +515,19 @@ initial_rel_path.unset(); initial_rel_path.set(file_path(), true); for (char const ** c = baddies; *c; ++c) - BOOST_CHECK_THROW(file_path_internal(*c), std::logic_error); + { + BOOST_CHECK_THROW(file_path_internal(*c), std::logic_error); + BOOST_CHECK_THROW(file_path_internal_from_user(std::string(*c)), + informative_failure); + } initial_rel_path.unset(); initial_rel_path.set(file_path_internal("blah/blah/blah"), true); for (char const ** c = baddies; *c; ++c) - BOOST_CHECK_THROW(file_path_internal(*c), std::logic_error); + { + BOOST_CHECK_THROW(file_path_internal(*c), std::logic_error); + BOOST_CHECK_THROW(file_path_internal_from_user(std::string(*c)), + informative_failure); + } BOOST_CHECK(file_path().empty()); BOOST_CHECK(file_path_internal("").empty()); @@ -546,6 +566,8 @@ for (std::vector::const_iterator i = split_test.begin(); i != split_test.end(); ++i) BOOST_CHECK(!null_name(*i)); + file_path fp_user = file_path_internal_from_user(std::string(*c)); + BOOST_CHECK(fp == fp_user); } } ======================================================================== --- paths.hh 90103ad12a83f237c1fc7579e21562be87c533fd +++ paths.hh 16661900264cb8d876b8a98b0adde390bda5d88c @@ -69,7 +69,7 @@ { return data < other.data; } private: - typedef enum { internal, external } source_type; + typedef enum { internal, external, internal_from_user } source_type; // input is always in utf8, because everything in our world is always in // utf8 (except interface code itself). // external paths: @@ -83,6 +83,7 @@ file_path(source_type type, std::string const & path); friend file_path file_path_internal(std::string const & path); friend file_path file_path_external(utf8 const & path); + friend file_path file_path_internal_from_user(utf8 const & path); }; // these are the public file_path constructors @@ -94,6 +95,15 @@ { return file_path(file_path::external, path()); } +// this is rarely used; it is for when the user provides not a path relative +// to their position in the working directory, but instead a project-root +// relative path (e.g., in 'cat REV PATH'). It is exactly like +// file_path_internal, but counts invalid paths as naughtiness rather than +// bugs. +inline file_path file_path_internal_from_user(utf8 const & path) +{ + return file_path(file_path::internal_from_user, path()); +} class bookkeeping_path : public any_path