# # # patch "NEWS" # from [add7f204c78b197d1899d9deb06e785684de3a44] # to [8d3a4f98df97c948cf5f4b66e3dc047bff0d6f87] # # patch "configure.ac" # from [8afceefff458845cded16d7b9aacac2e9e2b37e8] # to [4b8f7c0437872684abf38c76a26d34eb8053a29a] # # patch "debian/changelog" # from [ccdcd526a95d0ffe0c31277867cb5c14014dafbe] # to [27b8d6c10161e5aa71440bbe7ce6958596b7ca6f] # # patch "debian/control" # from [d38e0fb8f83f8ec74341d79917e61a98318260cf] # to [cd74e215b38293359880b4ed9367b05c4b72e08a] # # patch "file_io.hh" # from [2909663275c526a0d3acb366a1a333ff493f0f75] # to [1a250c948af12df05843eb0e1f4c02106a3c77dd] # # patch "m4/boost.m4" # from [e5f267509cb19516efa4424ccbfdee12e47f1911] # to [2e676e9269c5891b42d36657cb608fd787af4151] # # patch "paths.cc" # from [2de577ffde502496c4d1dcb088575fe00151ab6e] # to [5a81214a6d8ce81c997052ff7d17df925320a777] # # patch "paths.hh" # from [0966af72ddd577e28330fe2f1615e29760f869e1] # to [c2ae04d9e1e0ddf5d131c2a384eeb17433c5af62] # # patch "work_migration.cc" # from [ca32f50c101e495b3c549168a21926304a266fdd] # to [3d58c3332cd195309eacf5cc52d4e88ad66a6c81] # ============================================================ --- NEWS add7f204c78b197d1899d9deb06e785684de3a44 +++ NEWS 8d3a4f98df97c948cf5f4b66e3dc047bff0d6f87 @@ -47,6 +47,8 @@ databases with this problem can be fixed by pulling into a fresh database using 0.36. + - The Boost filesystem library is no longer required. + - Monotone can now be built successfully with Boost 1.34. Older versions of monotone would sometimes seem to work depending on the compiler used, but would have bugs in path normalization. ============================================================ --- configure.ac 8afceefff458845cded16d7b9aacac2e9e2b37e8 +++ configure.ac 4b8f7c0437872684abf38c76a26d34eb8053a29a @@ -127,8 +127,6 @@ BOOST_VERSION_SPECIFIC_BUGS # check for all things boost-related BOOST_VERSION_CHECK BOOST_VERSION_SPECIFIC_BUGS - -MTN_BOOST_LIB_FILESYSTEM MTN_BOOST_LIB_REGEX # more complex library checks ============================================================ --- debian/changelog ccdcd526a95d0ffe0c31277867cb5c14014dafbe +++ debian/changelog 27b8d6c10161e5aa71440bbe7ce6958596b7ca6f @@ -1,8 +1,9 @@ monotone (0.36-1) unstable; urgency=low monotone (0.36-1) unstable; urgency=low [ Zack Weinberg ] * Bump debhelper build-dep to (>= 4.2.0) per cdbs docs. * Run testsuite during build. + * Drop libboost-filesystem-dev build-dependency. -- final uploader's name and date here ============================================================ --- debian/control d38e0fb8f83f8ec74341d79917e61a98318260cf +++ debian/control cd74e215b38293359880b4ed9367b05c4b72e08a @@ -1,10 +1,10 @@ Build-Depends: cdbs (>= 0.4.28), debhelp Source: monotone Section: devel Priority: optional Maintainer: Debian Maintainers for Monotone Uploaders: Richard Levitte , Zack Weinberg , Ludovic Brenta Build-Depends: cdbs (>= 0.4.28), debhelper (>= 4.2.0), autotools-dev, - libboost-filesystem-dev, libboost-regex-dev, libboost-dev, libz-dev + libboost-regex-dev, libboost-dev, libz-dev Build-Depends-Indep: ps2eps, texlive-base, texlive-generic-recommended, texlive-latex-base, texinfo, xpdf-utils, po-debconf Standards-Version: 3.7.2.2 ============================================================ --- file_io.hh 2909663275c526a0d3acb366a1a333ff493f0f75 +++ file_io.hh 1a250c948af12df05843eb0e1f4c02106a3c77dd @@ -13,7 +13,7 @@ #include "vocab.hh" #include "paths.hh" #include "sanity.hh" -#include "platform.hh" +#include "platform-wrapped.hh" // this layer deals with talking to the filesystem, loading and saving // files, walking trees, etc. ============================================================ --- m4/boost.m4 e5f267509cb19516efa4424ccbfdee12e47f1911 +++ m4/boost.m4 2e676e9269c5891b42d36657cb608fd787af4151 @@ -163,16 +163,6 @@ AC_DEFUN([MTN_NEED_BOOST_LIB], AC_SUBST(BOOSTLIBS) ]) -AC_DEFUN([MTN_BOOST_LIB_FILESYSTEM], -[MTN_NEED_BOOST_LIB([filesystem], - [AC_LANG_PROGRAM([[ - #include - #include - using namespace boost::filesystem; - ]],[[ - exists(path("/boot")); - ]])])]) - AC_DEFUN([MTN_BOOST_LIB_REGEX], [MTN_NEED_BOOST_LIB([regex], [AC_LANG_PROGRAM([[ ============================================================ --- paths.cc 2de577ffde502496c4d1dcb088575fe00151ab6e +++ paths.cc 5a81214a6d8ce81c997052ff7d17df925320a777 @@ -9,21 +9,11 @@ #include "base.hh" #include +#include -#include -#include -#include -#include - -namespace fs = boost::filesystem; - -#include "constants.hh" #include "paths.hh" -#include "platform-wrapped.hh" -#include "sanity.hh" -#include "interner.hh" +#include "file_io.hh" #include "charset.hh" -#include "simplestring_xform.hh" using std::exception; using std::ostream; @@ -31,7 +21,6 @@ using std::vector; using std::string; using std::vector; - // some structure to ensure we aren't doing anything broken when resolving // filenames. the idea is to make sure // -- we don't depend on the existence of something before it has been set @@ -82,29 +71,23 @@ static access_tracker initi // initial_abs_path is for interpreting relative system_path's static access_tracker initial_abs_path; // initial_rel_path is for interpreting external file_path's -// for now we just make it an fs::path for convenience; we used to make it a -// file_path, but then you can't run monotone from inside the _MTN/ dir (even -// when referring to files outside the _MTN/ dir). -static access_tracker initial_rel_path; +// we used to make it a file_path, but then you can't run monotone from +// inside the _MTN/ dir (even when referring to files outside the _MTN/ +// dir). use of a bare string requires some caution but does work. +static access_tracker initial_rel_path; // working_root is for converting file_path's and bookkeeping_path's to // system_path's. static access_tracker working_root; bookkeeping_path const bookkeeping_root("_MTN"); path_component const bookkeeping_root_component("_MTN"); +path_component const old_bookkeeping_root_component("MT"); -// this is a file_path because it does not conform to the invariant that -// bookkeeping paths always start with the _current_ bookkeeping root. -file_path const old_bookkeeping_root = file_path_internal("MT"); - void save_initial_path() { // FIXME: BUG: this only works if the current working dir is in utf8 initial_abs_path.set(system_path(get_current_working_dir()), false); - // We still use boost::fs, so let's continue to initialize it properly. - fs::initial_path(); - fs::path::default_name_check(fs::native); L(FL("initial abs path is: %s") % initial_abs_path.get_but_unused()); } @@ -129,6 +112,7 @@ save_initial_path() // -- no doubled /'s // -- no trailing / // -- no "." or ".." path components + static inline bool bad_component(string const & component) { @@ -175,6 +159,36 @@ has_bad_component_chars(string const & p } +static bool +is_absolute_here(string const & path) +{ + if (path.empty()) + return false; + if (path[0] == '/') + return true; +#ifdef WIN32 + if (path[0] == '\\') + return true; + if (path.size() > 1 && path[1] == ':') + return true; +#endif + return false; +} + +static inline bool +is_absolute_somewhere(string const & path) +{ + if (path.empty()) + return false; + if (path[0] == '/') + return true; + if (path[0] == '\\') + return true; + if (path.size() > 1 && path[1] == ':') + return true; + return false; +} + // fully_normalized_path verifies a complete pathname for validity and // having been properly normalized (as if by normalize_path, below). static inline bool @@ -240,39 +254,86 @@ is_valid_internal(string const & path) && !in_bookkeeping_dir(path)); } -// path::normalize() is deprecated in Boost 1.34, and also -// doesn't remove leading or trailing dots any more. -static fs::path -normalize_path(fs::path const & in) +static string +normalize_path(string const & in) { -#if BOOST_VERSION < 103400 - return fs::path(in).normalize(); -#else - fs::path out; - vector stack; - for (fs::path::iterator i = in.begin(); i != in.end(); ++i) + string inT = in; + string leader; + MM(inT); + +#ifdef WIN32 + // the first thing we do is kill all the backslashes + for (string::iterator i = inT.begin(); i != inT.end(); i++) + if (*i == '\\') + *i = '/'; +#endif + + if (is_absolute_here (inT)) { - // remove . elements - if (*i == ".") - continue; - // remove foo/.. element pairs - if (*i == "..") + if (inT[0] == '/') { - if (!stack.empty()) + leader = "/"; + inT = inT.substr(1); + + if (inT.size() > 0 && inT[0] == '/') { - stack.pop_back(); - continue; + // if there are exactly two slashes at the beginning they + // are both preserved. three or more are the same as one. + string::size_type f = inT.find_first_not_of("/"); + if (f == string::npos) + f = inT.size(); + if (f == 1) + leader = "//"; + inT = inT.substr(f); } } - stack.push_back(*i); +#ifdef WIN32 + else + { + I(inT[1] == ':'); + if (inT.size() > 2 && inT[2] == '/') + { + leader = inT.substr(0, 3); + inT = inT.substr(3); + } + else + { + leader = inT.substr(0, 2); + inT = inT.substr(2); + } + } +#endif + + I(!is_absolute_here(inT)); + if (inT.size() == 0) + return leader; } - for (vector::const_iterator i = stack.begin(); - i != stack.end(); ++i) + + vector stack; + string::const_iterator head, tail; + for (head = inT.begin(); head != inT.end(); head = tail) { - out /= *i; + tail = head; + while (tail != inT.end() && *tail != '/') + tail++; + + string elt(head, tail); + if (tail != inT.end()) + tail++; + + if (elt == ".") + continue; + // remove foo/.. element pairs; leave leading .. components alone + if (elt == ".." && !stack.empty() && stack.back() != "..") + { + stack.pop_back(); + continue; + } + + stack.push_back(elt); } - return out; -#endif + + return leader + boost::algorithm::join(stack, "/"); } static void @@ -292,24 +353,24 @@ normalize_external_path(string const & p else { N(!path.empty(), F("empty path '%s' is invalid") % path); - fs::path out, base, relative; + N(!is_absolute_here(path), F("absolute path '%s' is invalid") % path); + string base; try { base = initial_rel_path.get(); - // the fs::native is needed to get it to accept paths like ".foo". - relative = fs::path(path, fs::native); - out = normalize_path(base / relative); + if (base == "") + normalized = normalize_path(path); + else + normalized = normalize_path(base + "/" + path); } catch (exception &) { N(false, F("path '%s' is invalid") % path); } - normalized = out.string(); if (normalized == ".") normalized = string(""); - N(!relative.has_root_path(), - F("absolute path '%s' is invalid") % relative.string()); - N(fully_normalized_path(normalized), F("path '%s' is invalid") % normalized); + N(fully_normalized_path(normalized), + F("path '%s' is invalid") % normalized); } } @@ -454,11 +515,8 @@ any_path::dirname() const return any_path(s, 0, sep); } -// this returns all but the last component of a file_path. it is only -// defined on file_paths because (a) that avoids problems at the root, -// and (b) that's the only version that we use. -// if there is only one component present, the dirname is the root -// (i.e. the empty string). +// these variations exist to get the return type right. also, +// file_path dirname() can be a little simpler. file_path file_path::dirname() const { @@ -469,6 +527,20 @@ file_path::dirname() const return file_path(s, 0, sep); } +system_path +system_path::dirname() const +{ + string const & s = data; + string::size_type sep = s.rfind('/'); + if (sep == string::npos) + return system_path(); + if (sep == s.size() - 1) // dirname() of the root directory is itself + return *this; + + return system_path(s, 0, sep); +} + + // produce dirname and basename at the same time void file_path::dirname_basename(file_path & dir, path_component & base) const @@ -565,36 +637,6 @@ void dump(bookkeeping_path const & p, st // this code's speed does not matter much /////////////////////////////////////////////////////////////////////////// -static bool -is_absolute_here(string const & path) -{ - if (path.empty()) - return false; - if (path[0] == '/') - return true; -#ifdef WIN32 - if (path[0] == '\\') - return true; - if (path.size() > 1 && path[1] == ':') - return true; -#endif - return false; -} - -static inline bool -is_absolute_somewhere(string const & path) -{ - if (path.empty()) - return false; - if (path[0] == '/') - return true; - if (path[0] == '\\') - return true; - if (path.size() > 1 && path[1] == ':') - return true; - return false; -} - // relies on its arguments already being validated, except that you may not // append the empty path component, and if you are appending to the empty // path, you may not create an absolute path or a path into the bookkeeping @@ -668,22 +710,12 @@ system_path::operator /(char const * to_ // system_path /////////////////////////////////////////////////////////////////////////// -static string -normalize_out_dots(string const & path) -{ -#ifdef WIN32 - return normalize_path(fs::path(path, fs::native)).string(); -#else - return normalize_path(fs::path(path, fs::native)).native_file_string(); -#endif -} - system_path::system_path(any_path const & other, bool in_true_workspace) { if (is_absolute_here(other.as_internal())) // another system_path. the normalizing isn't really necessary, but it // makes me feel warm and fuzzy. - data = normalize_out_dots(other.as_internal()); + data = normalize_path(other.as_internal()); else { system_path wr; @@ -691,7 +723,7 @@ system_path::system_path(any_path const wr = working_root.get(); else wr = working_root.get_but_unused(); - data = normalize_out_dots(wr.as_internal() + "/" + other.as_internal()); + data = normalize_path(wr.as_internal() + "/" + other.as_internal()); } } @@ -700,10 +732,10 @@ static inline string const_system_path(u N(!path().empty(), F("invalid path ''")); string expanded = tilde_expand(path()); if (is_absolute_here(expanded)) - return normalize_out_dots(expanded); + return normalize_path(expanded); else - return normalize_out_dots(initial_abs_path.get().as_internal() - + "/" + path()); + return normalize_path(initial_abs_path.get().as_internal() + + "/" + path()); } system_path::system_path(string const & path) @@ -721,76 +753,77 @@ static bool /////////////////////////////////////////////////////////////////////////// static bool -find_bookdir(fs::path const & root, fs::path const & bookdir, - fs::path & current, fs::path & removed) +find_bookdir(system_path const & root, path_component const & bookdir, + system_path & current, string & removed) { - current = fs::initial_path(); - fs::path check = current / bookdir; + current = initial_abs_path.get(); + removed.clear(); // check that the current directory is below the specified search root - - fs::path::iterator ri = root.begin(); - fs::path::iterator ci = current.begin(); - - while (ri != root.end() && ci != current.end() && *ri == *ci) + if (current.as_internal().find(root.as_internal()) != 0) { - ++ri; - ++ci; + W(F("current directory '%s' is not below root '%s'") % current % root); + return false; } - // if it's not then issue a warning and abort the search + L(FL("searching for '%s' directory with root '%s'") % bookdir % root); - if (ri != root.end()) + system_path check; + while (!(current == root)) { - W(F("current directory '%s' is not below root '%s'") - % current.string() - % root.string()); - return false; - } + check = current / bookdir; + switch (get_path_status(check)) + { + case path::nonexistent: + L(FL("'%s' not found in '%s' with '%s' removed") + % bookdir % current % removed); + if (removed.empty()) + removed = current.basename()(); + else + removed = current.basename()() + "/" + removed; + current = current.dirname(); + continue; - L(FL("searching for '%s' directory with root '%s'") - % bookdir.string() - % root.string()); + case path::file: + L(FL("'%s' is not a directory") % check); + return false; - while (current != root - && current.has_branch_path() - && current.has_leaf() - && !fs::exists(check)) - { - L(FL("'%s' not found in '%s' with '%s' removed") - % bookdir.string() % current.string() % removed.string()); - removed = fs::path(current.leaf(), fs::native) / removed; - current = current.branch_path(); - check = current / bookdir; + case path::directory: + goto found; + } } - L(FL("search for '%s' ended at '%s' with '%s' removed") - % bookdir.string() % current.string() % removed.string()); - - if (!fs::exists(check)) + // if we get here, we have hit the root; try once more + check = current / bookdir; + switch (get_path_status(check)) { - L(FL("'%s' does not exist") % check.string()); + case path::nonexistent: + L(FL("'%s' not found in '%s' with '%s' removed") + % bookdir % current % removed); return false; - } - if (!fs::is_directory(check)) - { - L(FL("'%s' is not a directory") % check.string()); + case path::file: + L(FL("'%s' is not a directory") % check); return false; + + case path::directory: + goto found; } - + return false; + + found: // check for _MTN/. and _MTN/.. to see if mt dir is readable try { - if (!fs::exists(check / ".") || !fs::exists(check / "..")) + if (!path_exists(check / ".") || !path_exists(check / "..")) { - L(FL("problems with '%s' (missing '.' or '..')") % check.string()); + L(FL("problems with '%s' (missing '.' or '..')") % check); return false; } } catch(exception &) { - L(FL("problems with '%s' (cannot check for '.' or '..')") % check.string()); + L(FL("problems with '%s' (cannot check for '.' or '..')") % check); return false; } return true; @@ -798,38 +831,60 @@ bool bool -find_and_go_to_workspace(std::string const & search_root) +find_and_go_to_workspace(string const & search_root) { - fs::path bookdir(bookkeeping_root.as_external(), fs::native); - fs::path oldbookdir(old_bookkeeping_root.as_external(), fs::native); - fs::path root, current, removed; + system_path root, current; + string removed; if (search_root.empty()) - root = fs::initial_path().root_path(); + { +#ifdef WIN32 + current = get_current_working_dir(); + if (current[0] == '/' || current[0] == '\\') + { + if (current.size() > 1 && (current[1] == '/' || current[1] == '\\')) + { + // UNC name + string::size_type uncend = current.find_first_of("\\/", 2); + if (uncend == string::npos) + root = system_path(current + "/"); + else + root = system_path(current.substr(0, uncend)); + } + else + root = system_path("/"); + } + else if (current.size() > 1 && current[1] == ':') + { + root = system_path(current.substr(0,2) + "/"); + } + else I(false); +#else + root = system_path("/"); +#endif + } else { - L(FL("limiting search for workspace to %s") % search_root); - // converting through system_path makes it absolute - root = fs::path(system_path(search_root).as_external(), fs::native); + root = system_path(search_root); + L(FL("limiting search for workspace to %s") % root); - N(fs::exists(root), - F("search root '%s' does not exist") % search_root); - N(fs::is_directory(root), - F("search root '%s' is not a directory") % search_root); + require_path_is_directory(root, + F("search root '%s' does not exist") % root, + F("search root '%s' is not a directory") % root); } // first look for the current name of the bookkeeping directory. // if we don't find it, look for it under the old name, so that // migration has a chance to work. - if (!find_bookdir(root, bookdir, current, removed)) - if (!find_bookdir(root, oldbookdir, current, removed)) + if (!find_bookdir(root, bookkeeping_root_component, current, removed)) + if (!find_bookdir(root, old_bookkeeping_root_component, current, removed)) return false; - working_root.set(current.native_file_string(), true); + working_root.set(current, true); initial_rel_path.set(removed, true); L(FL("working root is '%s'") % working_root.get_but_unused()); - L(FL("initial relative path is '%s'") % initial_rel_path.get_but_unused().string()); + L(FL("initial relative path is '%s'") % initial_rel_path.get_but_unused()); change_current_working_dir(working_root.get_but_unused()); @@ -840,7 +895,7 @@ go_to_workspace(system_path const & new_ go_to_workspace(system_path const & new_workspace) { working_root.set(new_workspace, true); - initial_rel_path.set(fs::path(), true); + initial_rel_path.set(string(), true); change_current_working_dir(new_workspace); } @@ -936,13 +991,13 @@ UNIT_TEST(paths, file_path_internal) "_mTN/foo", 0 }; initial_rel_path.unset(); - initial_rel_path.set(fs::path(), true); + initial_rel_path.set(string(), true); for (char const ** c = baddies; *c; ++c) { UNIT_TEST_CHECK_THROW(file_path_internal(*c), logic_error); } initial_rel_path.unset(); - initial_rel_path.set(fs::path("blah/blah/blah", fs::native), true); + initial_rel_path.set("blah/blah/blah", true); for (char const ** c = baddies; *c; ++c) { UNIT_TEST_CHECK_THROW(file_path_internal(*c), logic_error); @@ -968,8 +1023,8 @@ UNIT_TEST(paths, file_path_internal) for (int i = 0; i < 2; ++i) { initial_rel_path.unset(); - initial_rel_path.set(i ? fs::path() - : fs::path("blah/blah/blah", fs::native), + initial_rel_path.set(i ? string() + : string("blah/blah/blah"), true); for (char const ** c = goodies; *c; ++c) { @@ -997,7 +1052,7 @@ UNIT_TEST(paths, file_path_external_null UNIT_TEST(paths, file_path_external_null_prefix) { initial_rel_path.unset(); - initial_rel_path.set(fs::path(), true); + initial_rel_path.set(string(), true); char const * baddies[] = {"/foo", "../bar", @@ -1066,7 +1121,7 @@ UNIT_TEST(paths, file_path_external_pref UNIT_TEST(paths, file_path_external_prefix__MTN) { initial_rel_path.unset(); - initial_rel_path.set(fs::path("_MTN"), true); + initial_rel_path.set(string("_MTN"), true); UNIT_TEST_CHECK_THROW(file_path_external(utf8("foo")), informative_failure); UNIT_TEST_CHECK_THROW(file_path_external(utf8(".")), informative_failure); @@ -1078,7 +1133,7 @@ UNIT_TEST(paths, file_path_external_pref UNIT_TEST(paths, file_path_external_prefix_a_b) { initial_rel_path.unset(); - initial_rel_path.set(fs::path("a/b"), true); + initial_rel_path.set(string("a/b"), true); char const * baddies[] = {"/foo", "../../../bar", @@ -1401,7 +1456,7 @@ UNIT_TEST(paths, system) working_root.unset(); working_root.set(system_path("/working/root"), true); initial_rel_path.unset(); - initial_rel_path.set(fs::path("rel/initial"), true); + initial_rel_path.set(string("rel/initial"), true); UNIT_TEST_CHECK(system_path(system_path("foo/bar")).as_internal() == "/a/b/foo/bar"); UNIT_TEST_CHECK(!working_root.used); @@ -1667,7 +1722,7 @@ UNIT_TEST(paths, test_external_string_is UNIT_TEST(paths, test_external_string_is_bookkeeping_path_prefix_none) { initial_rel_path.unset(); - initial_rel_path.set(fs::path(), true); + initial_rel_path.set(string(), true); char const * yes[] = {"_MTN", "_MTN/foo", @@ -1689,7 +1744,7 @@ UNIT_TEST(paths, test_external_string_is UNIT_TEST(paths, test_external_string_is_bookkeeping_path_prefix_a_b) { initial_rel_path.unset(); - initial_rel_path.set(fs::path("a/b"), true); + initial_rel_path.set(string("a/b"), true); char const * yes[] = {"../../_MTN", "../../_MTN/foo", @@ -1713,7 +1768,7 @@ UNIT_TEST(paths, test_external_string_is UNIT_TEST(paths, test_external_string_is_bookkeeping_path_prefix__MTN) { initial_rel_path.unset(); - initial_rel_path.set(fs::path("_MTN"), true); + initial_rel_path.set(string("_MTN"), true); char const * yes[] = {".", "foo", ============================================================ --- paths.hh 0966af72ddd577e28330fe2f1615e29760f869e1 +++ paths.hh c2ae04d9e1e0ddf5d131c2a384eeb17433c5af62 @@ -318,7 +318,7 @@ extern path_component const bookkeeping_ extern bookkeeping_path const bookkeeping_root; extern path_component const bookkeeping_root_component; // for migration -extern file_path const old_bookkeeping_root; +extern path_component const old_bookkeeping_root_component; // this will always be an absolute path class system_path : public any_path @@ -326,6 +326,7 @@ public: public: system_path() {}; system_path(system_path const & other) : any_path(other) {}; + // the optional argument takes some explanation. this constructor takes a // path relative to the workspace root. the question is how to interpret // that path -- since it's possible to have multiple workspaces over the @@ -344,8 +345,12 @@ public: system_path(std::string const & path); system_path(utf8 const & path); + bool operator ==(const system_path & other) const + { return data == other.data; } + system_path operator /(path_component const & to_append) const; system_path operator /(char const * to_append) const; + system_path dirname() const; private: system_path(std::string const & path, ============================================================ --- work_migration.cc ca32f50c101e495b3c549168a21926304a266fdd +++ work_migration.cc 3d58c3332cd195309eacf5cc52d4e88ad66a6c81 @@ -59,7 +59,7 @@ get_ws_format() { if (directory_exists(bookkeeping_root)) format = 1; - else if (directory_exists(old_bookkeeping_root)) + else if (directory_exists(file_path() / old_bookkeeping_root_component)) format = 0; else N(false, F("workspace required but not found"));