# # patch "paths.cc" # from [c32ea754e4be001dc27f4dfc37a197c2c8b6ca6d] # to [5c924452a1eb1abc75ae3a5cca29cfafd1003caf] # ======================================================================== --- paths.cc c32ea754e4be001dc27f4dfc37a197c2c8b6ca6d +++ paths.cc 5c924452a1eb1abc75ae3a5cca29cfafd1003caf @@ -218,36 +218,33 @@ static interner pc_interner("", the_null_component); // This function takes a vector of path components and joins them into a -// single file_path. Valid input may be a single-element vector whose sole -// element is the empty path component (""); this represents the null path, -// which we use to represent non-existent files. Alternatively, input may be -// a multi-element vector, in which case all elements of the vector are -// required to be non-null. The following are valid inputs (with strings -// replaced by their interned version, of course): -// - [""] -// - ["foo"] -// - ["foo", "bar"] -// The following are not: -// - [] -// - ["foo", ""] -// - ["", "bar"] +// single file_path. This is the inverse to file_path::split. It takes a +// vector of the form: +// +// ["", p[0], p[1], ..., p[n]] +// +// and constructs the path: +// +// p[0]/p[1]/.../p[n] +// file_path::file_path(std::vector const & pieces) { std::vector::const_iterator i = pieces.begin(); I(i != pieces.end()); - - // FIXME: this is disabled for the moment, as rosters make - // paths of the form ["", "foo", "bar"] all the time... - // if (pieces.size() > 1) - // I(!null_name(*i)); - - std::string tmp = pc_interner.lookup(*i); - //I(tmp != bookkeeping_root.as_internal()); + I(null_name(*i)); + std::string tmp; + bool start = true; for (++i; i != pieces.end(); ++i) { I(!null_name(*i)); - tmp += "/"; + if (!start) + tmp += "/"; tmp += pc_interner.lookup(*i); + if (start) + { + I(tmp != bookkeeping_root.as_internal()); + start = false; + } } data = tmp; } @@ -257,25 +254,18 @@ // // "p[0]/p[1]/.../p[n-1]/p[n]" // -// and fills in a vector of paths corresponding to p[0] ... p[n-1] +// and fills in a vector of paths corresponding to p[0] ... p[n]. This is the +// inverse to the file_path::file_path(split_path) constructor. // -// FIXME: this code carefully duplicates the behavior of the old path -// splitting functions, in that it is _not_ the inverse to the above joining -// function. The difference is that if you pass a null path (the one -// represented by the empty string, or 'file_path()'), then it will return an -// _empty_ vector. This vector will not be suitable to pass to the above path -// joiner; to get the null path back again, you have to pass the above -// function a single-element vector containing a the null component. -// -// Why does it work this way? Because that's what the old code did, and -// that's what change_set.cc was written around; and it's much much easier to -// make this code do something weird than to try and fix change_set.cc. When -// change_set.cc is rewritten, however, you should revisit the semantics of -// this function. +// The first entry in this vector is always the null component, "". This path +// is the root of the tree. So we actually output a vector like: +// ["", p[0], p[1], ..., p[n]] +// with n+1 members. void file_path::split(std::vector & pieces) const { pieces.clear(); + pieces.push_back(the_null_component); if (empty()) return; std::string::size_type start, stop; @@ -575,16 +565,12 @@ BOOST_CHECK(file_path_internal(fp.as_internal()) == fp); std::vector split_test; fp.split(split_test); - if (fp.empty()) - BOOST_CHECK(split_test.empty()); - else - { - BOOST_CHECK(!split_test.empty()); - 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(!split_test.empty()); + file_path fp2(split_test); + BOOST_CHECK(fp == fp2); + BOOST_CHECK(null_name(split_test[0])); + for (std::vector::const_iterator + i = split_test.begin() + 1; 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); @@ -606,16 +592,12 @@ BOOST_CHECK(fp.as_external() == after); std::vector split_test; fp.split(split_test); - if (fp.empty()) - BOOST_CHECK(split_test.empty()); - else - { - BOOST_CHECK(!split_test.empty()); - 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(!split_test.empty()); + file_path fp2(split_test); + BOOST_CHECK(fp == fp2); + BOOST_CHECK(null_name(split_test[0])); + for (std::vector::const_iterator + i = split_test.begin() + 1; i != split_test.end(); ++i) BOOST_CHECK(!null_name(*i)); } @@ -738,33 +720,44 @@ 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.size() == 4); + BOOST_CHECK(split2.size() == 4); BOOST_CHECK(split1[1] != split1[2]); - BOOST_CHECK(!null_name(split1[0]) + BOOST_CHECK(split1[1] != split1[3]); + BOOST_CHECK(split1[2] != split1[3]); + 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]); + && !null_name(split1[2]) + && !null_name(split1[3])); + BOOST_CHECK(split1[1] == split2[3]); BOOST_CHECK(split1[2] == split2[1]); + BOOST_CHECK(split1[3] == split2[2]); file_path fp3 = file_path_internal(""); pcv split3; fp3.split(split3); - BOOST_CHECK(split3.empty()); + BOOST_CHECK(split3.size() == 1 && null_name(split3[0])); + // empty split_path is invalid pcv split4; // this comparison tricks the compiler into not completely eliminating this // code as dead... BOOST_CHECK_THROW(file_path(split4) == file_path(), std::logic_error); split4.push_back(the_null_component); BOOST_CHECK(file_path(split4) == file_path()); + + // split_path without null first item is invalid split4.clear(); - split4.push_back(split1[0]); + split4.push_back(split1[1]); + // this comparison tricks the compiler into not completely eliminating this + // code as dead... + BOOST_CHECK_THROW(file_path(split4) == file_path(), std::logic_error); + + // split_path with non-first item item null is invalid + split4.clear(); split4.push_back(the_null_component); split4.push_back(split1[0]); + split4.push_back(the_null_component); // this comparison tricks the compiler into not completely eliminating this // code as dead... BOOST_CHECK_THROW(file_path(split4) == file_path(), std::logic_error); @@ -773,11 +766,12 @@ // dir pcv split_mt1, split_mt2; file_path_internal("foo/MT").split(split_mt1); - BOOST_CHECK(split_mt1.size() == 2); - split_mt2.push_back(split_mt1[1]); + BOOST_CHECK(split_mt1.size() == 3); + split_mt2.push_back(the_null_component); + split_mt2.push_back(split_mt1[2]); // split_mt2 now contains the component "MT" BOOST_CHECK_THROW(file_path(split_mt2) == file_path(), std::logic_error); - split_mt2.push_back(split_mt1[0]); + split_mt2.push_back(split_mt1[1]); // split_mt2 now contains the components "MT", "foo" in that order // this comparison tricks the compiler into not completely eliminating this // code as dead...