# # # patch "configure.ac" # from [effce38de781662e7868d0de7698df5593692e02] # to [5282987db39b7d7b0cd8e918005309b776e7e972] # # patch "file_io.cc" # from [81e0e4bc3067116550da064c132e2d8b8b940851] # to [a57abb85fc9ed1d7fb14b4aa2c2e6b8d98c27f00] # # patch "file_io.hh" # from [2c7717337282a9f47e587ae7095b07d11b602f9c] # to [2909663275c526a0d3acb366a1a333ff493f0f75] # # patch "key_store.cc" # from [27bf014bed826d2dffc53b3358295c6c376e7fdc] # to [8aa35766f11519f5bc75d3787c5a261419fa13f9] # # patch "platform.hh" # from [40f6b29812eafb8b7232263ccfabd4796b67a354] # to [4591a65680cb8f6cbbf3b4087c306906914ef246] # # patch "unix/fs.cc" # from [50d2d4f37eb345a3f0a3ace3a00ec8adc3eda4d5] # to [7ea18b19c14fd6ce9e70657b03785e1da1c45b64] # # patch "win32/fs.cc" # from [0217f9579af7c234408ac3749be7f59308983a2c] # to [f7e4c47be8ae99b191aa36ecd70d423992bc7aba] # # patch "work.cc" # from [85abc0b883aecc18cb5a11b2c190080ac4337ba1] # to [a3065afa293f68402807da156ea7d2243c27ec6d] # ============================================================ --- configure.ac effce38de781662e7868d0de7698df5593692e02 +++ configure.ac 5282987db39b7d7b0cd8e918005309b776e7e972 @@ -118,7 +118,7 @@ AC_CHECK_FUNCS([atexit memset mkstemp st AC_FUNC_ICONV_TRANSLIT AC_CHECK_FUNCS([atexit memset mkstemp strptime lrint \ __cxa_current_exception_type __cxa_demangle \ - putenv setenv unsetenv]) + putenv setenv unsetenv dirfd fstatat]) # simple library checks AC_SEARCH_LIBS([deflate], [z], , AC_MSG_FAILURE([zlib is required])) ============================================================ --- file_io.cc 81e0e4bc3067116550da064c132e2d8b8b940851 +++ file_io.cc a57abb85fc9ed1d7fb14b4aa2c2e6b8d98c27f00 @@ -129,15 +129,26 @@ file_exists(any_path const & p) return get_path_status(p) == path::file; } +namespace +{ + struct directory_not_empty_exception {}; + struct directory_empty_helper : public dirent_consumer + { + virtual void consume(char const *) + { throw directory_not_empty_exception(); } + }; +} + bool directory_empty(any_path const & path) { - vector files; - vector subdirs; - - read_directory(path, files, subdirs); - - return files.empty() && subdirs.empty(); + directory_empty_helper h; + try { + do_read_directory(system_path(path).as_external(), h, h); + } catch (directory_not_empty_exception) { + return false; + } + return true; } static bool did_char_is_binary_init; @@ -315,32 +326,26 @@ read_data(any_path const & p, data & dat dat = data(pipe.read_all_as_string()); } -void read_directory(any_path const & path, - vector & files, - vector & dirs) +namespace { - files.clear(); - dirs.clear(); - fs::directory_iterator ei; - fs::path native_path = fs::path(system_path(path).as_external(), fs::native); - for (fs::directory_iterator di(native_path); - di != ei; ++di) - { - fs::path entry = *di; - if (!fs::exists(entry) - || entry.string() == "." - || entry.string() == "..") - continue; + struct fill_pc_vec : public dirent_consumer + { + fill_pc_vec(vector & v) : v(v) { v.clear(); } + virtual void consume(char const * s) + { v.push_back(path_component(s)); } - // FIXME: BUG: this screws up charsets (assumes blindly that the fs is - // utf8) - if (fs::is_directory(entry)) - dirs.push_back(utf8(entry.leaf())); - else - files.push_back(utf8(entry.leaf())); - } + private: + vector & v; + }; } +void read_directory(any_path const & path, + vector & files, + vector & dirs) +{ + fill_pc_vec ff(files), df(dirs); + do_read_directory(system_path(path).as_external(), ff, df); +} // This function can only be called once per run. void @@ -443,7 +448,7 @@ walk_tree_recursive(fs::path const & abs tree_walker & walker) { system_path root(absolute.string()); - vector files, dirs; + vector files, dirs; read_directory(root, files, dirs); @@ -460,7 +465,8 @@ walk_tree_recursive(fs::path const & abs // // [1] http://lkml.org/lkml/2006/2/24/215 - for (vector::const_iterator i = files.begin(); i != files.end(); ++i) + for (vector::const_iterator i = files.begin(); + i != files.end(); ++i) { // the fs::native is necessary here, or it will bomb out on any paths // that look at it funny. (E.g., rcs files with "," in the name.) @@ -473,7 +479,8 @@ walk_tree_recursive(fs::path const & abs walker.visit_file(p); } - for (vector::const_iterator i = dirs.begin(); i != dirs.end(); ++i) + for (vector::const_iterator i = dirs.begin(); + i != dirs.end(); ++i) { // the fs::native is necessary here, or it will bomb out on any paths // that look at it funny. (E.g., rcs files with "," in the name.) ============================================================ --- file_io.hh 2c7717337282a9f47e587ae7095b07d11b602f9c +++ file_io.hh 2909663275c526a0d3acb366a1a333ff493f0f75 @@ -71,8 +71,8 @@ void read_directory(any_path const & pat void read_data(any_path const & path, data & data); void read_directory(any_path const & path, - std::vector & files, - std::vector & dirs); + std::vector & files, + std::vector & dirs); void read_data_stdin(data & dat); ============================================================ --- key_store.cc 27bf014bed826d2dffc53b3358295c6c376e7fdc +++ key_store.cc 8aa35766f11519f5bc75d3787c5a261419fa13f9 @@ -74,7 +74,7 @@ key_store::read_key_dir() void key_store::read_key_dir() { - vector key_files, dirs; + vector key_files, dirs; if (directory_exists(key_dir)) { L(FL("reading key dir '%s'") % key_dir); @@ -83,7 +83,7 @@ key_store::read_key_dir() else L(FL("key dir '%s' does not exist") % key_dir); keyreader kr(*this); - for (vector::const_iterator i = key_files.begin(); + for (vector::const_iterator i = key_files.begin(); i != key_files.end(); ++i) { L(FL("reading keys from file '%s'") % (*i)); ============================================================ --- platform.hh 40f6b29812eafb8b7232263ccfabd4796b67a354 +++ platform.hh 4591a65680cb8f6cbbf3b4087c306906914ef246 @@ -118,6 +118,15 @@ path::status get_path_status(std::string }; path::status get_path_status(std::string const & path); +struct dirent_consumer +{ + virtual ~dirent_consumer() {} + virtual void consume(const char *) = 0; +}; +void do_read_directory(std::string const & path, + dirent_consumer & files, + dirent_consumer & dirs); + void rename_clobberingly(std::string const & from, std::string const & to); void do_remove(std::string const & path); ============================================================ --- unix/fs.cc 50d2d4f37eb345a3f0a3ace3a00ec8adc3eda4d5 +++ unix/fs.cc 7ea18b19c14fd6ce9e70657b03785e1da1c45b64 @@ -18,6 +18,7 @@ #include #include #include +#include #include "sanity.hh" #include "platform.hh" @@ -119,7 +120,102 @@ get_path_status(string const & path) } } +namespace +{ + // RAII object for DIRs. + struct dirhandle + { + dirhandle(string const & path) : d(opendir(path.c_str())) + { + E(d, F("could not open directory '%s': %s") % path % os_strerror(errno)); + } + // technically closedir can fail, but there's nothing we could do about it. + ~dirhandle() { closedir(d); } + + // accessors + struct dirent * next() { return readdir(d); } +#ifdef HAVE_DIRFD + int fd() { return dirfd(d); } +#endif + private: + DIR *d; + }; +} + void +do_read_directory(string const & path, + dirent_consumer & files, + dirent_consumer & dirs) +{ + dirhandle dir(path); + struct dirent *d; + struct stat st; + int st_result; + + while ((d = dir.next()) != 0) + { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; +#ifdef _DIRENT_HAVE_D_TYPE + switch (d->d_type) + { + case DT_REG: // regular file + files.consume(d->d_name); + continue; + case DT_DIR: // directory + dirs.consume(d->d_name); + continue; + + case DT_UNKNOWN: // unknown type + case DT_LNK: // symlink - must find out what's at the other end + break; + + default: + goto bad_special_file; + } +#endif + + // the use of stat rather than lstat here is deliberate. +#if defined HAVE_FSTATAT && defined HAVE_DIRFD + { + static bool fstatat_works = true; + if (fstatat_works) + { + st_result = fstatat(dir.fd(), d->d_name, &st, 0); + if (st_result == -1 && errno == ENOSYS) + fstatat_works = false; + } + if (!fstatat_works) + st_result = stat((path + "/" + d->d_name).c_str(), &st); + } +#else + st_result = stat((path + "/" + d->d_name).c_str(), &st); +#endif + + // silently ignore broken symlinks. + // ??? that was the historical behavior, but do we really want it? + if (st_result < 0 && errno == ENOENT) + continue; + + E(st_result == 0, + F("error accessing '%s/%s': %s") % path % d->d_name); + + if (S_ISREG(st.st_mode)) + files.consume(d->d_name); + else if (S_ISDIR(st.st_mode)) + dirs.consume(d->d_name); + else + goto bad_special_file; + } + return; + + bad_special_file: + E(false, F("cannot handle special file '%s/%s'") % path % d->d_name); +} + + + +void rename_clobberingly(string const & from, string const & to) { E(!rename(from.c_str(), to.c_str()), ============================================================ --- win32/fs.cc 0217f9579af7c234408ac3749be7f59308983a2c +++ win32/fs.cc f7e4c47be8ae99b191aa36ecd70d423992bc7aba @@ -141,7 +141,82 @@ get_path_status(std::string const & path return path::file; } +namespace +{ + // RAII wrapper for FindFirstFile/FindNextFile + struct dirhandle + { + dirhandle(std::string const & path) : first(true), last(false) + { + std::string p = path; + // Win98 requires this little dance + if (p.size() > 0 && p[p.size()-1] != '/' && p[p.size()-1] != '\\') + p += "/*"; + else + p += "*"; + + h = FindFirstFile(p.c_str(), &firstdata); + if (h == INVALID_HANDLE_VALUE) + { + if (GetLastError() == ERROR_FILE_NOT_FOUND) // zero files in dir + last = true; + else + E(false, F("could not open directory '%s': %s") + % path % os_strerror(GetLastError())); + } + } + ~dirhandle() + { + if (h != INVALID_HANDLE_VALUE) + FindClose(h); + } + bool next(WIN32_FIND_DATA * data) + { + if (last) + return false; + if (first) + { + *data = firstdata; + first = false; + return true; + } + if (FindNextFile(h, data)) + return true; + E(GetLastError() == ERROR_NO_MORE_FILES, + F("error while reading directory: %s") % os_strerror(errno)); + last = true; + return false; + } + + private: + bool first; + bool last; + HANDLE h; + WIN32_FIND_DATA firstdata; + }; +} + void +do_read_directory(std::string const & path, + dirent_consumer & files, + dirent_consumer & dirs) +{ + dirhandle dir(path); + WIN32_FIND_DATA d; + + while (dir.next(&d)) + { + if (!strcmp(d.cFileName, ".") || !strcmp(d.cFileName, "..")) + continue; + + if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + dirs.consume(d.cFileName); + else + files.consume(d.cFileName); + } +} + +void do_remove(std::string const & path) { switch (get_path_status(path)) ============================================================ --- work.cc 85abc0b883aecc18cb5a11b2c190080ac4337ba1 +++ work.cc a3065afa293f68402807da156ea7d2243c27ec6d @@ -765,15 +765,15 @@ editable_working_tree::detach_node(file_ { // root dir detach, so we move contents, rather than the dir itself mkdir_p(dst_pth); - vector files, dirs; + vector files, dirs; read_directory(src_pth, files, dirs); - for (vector::const_iterator i = files.begin(); i != files.end(); ++i) - move_file(src_pth / path_component(*i), - dst_pth / (*i)()); - for (vector::const_iterator i = dirs.begin(); i != dirs.end(); ++i) - if (!bookkeeping_path::internal_string_is_bookkeeping_path(*i)) - move_dir(src_pth / path_component(*i), - dst_pth / (*i)()); + for (vector::const_iterator i = files.begin(); + i != files.end(); ++i) + move_file(src_pth / *i, dst_pth / (*i)()); + for (vector::const_iterator i = dirs.begin(); + i != dirs.end(); ++i) + if (!bookkeeping_path::internal_string_is_bookkeeping_path(utf8((*i)()))) + move_dir(src_pth / *i, dst_pth / (*i)()); root_dir_attached = false; } else @@ -837,19 +837,19 @@ editable_working_tree::attach_node(node_ if (dst_pth == file_path()) { // root dir attach, so we move contents, rather than the dir itself - vector files, dirs; + vector files, dirs; read_directory(src_pth, files, dirs); - for (vector::const_iterator i = files.begin(); i != files.end(); ++i) + for (vector::const_iterator i = files.begin(); + i != files.end(); ++i) { - I(!bookkeeping_path::internal_string_is_bookkeeping_path(*i)); - move_file(src_pth / (*i)(), - dst_pth / path_component(*i)); + I(!bookkeeping_path::internal_string_is_bookkeeping_path(utf8((*i)()))); + move_file(src_pth / (*i)(), dst_pth / *i); } - for (vector::const_iterator i = dirs.begin(); i != dirs.end(); ++i) + for (vector::const_iterator i = dirs.begin(); + i != dirs.end(); ++i) { - I(!bookkeeping_path::internal_string_is_bookkeeping_path(*i)); - move_dir(src_pth / (*i)(), - dst_pth / path_component(*i)); + I(!bookkeeping_path::internal_string_is_bookkeeping_path(utf8((*i)()))); + move_dir(src_pth / (*i)(), dst_pth / *i); } delete_dir_shallow(src_pth); root_dir_attached = true;