|
Boost : |
From: Ross Smith (r-smith_at_[hidden])
Date: 2002-03-27 02:29:09
Beman Dawes wrote:
>
> At 04:12 PM 3/26/2002, Ross Smith wrote:
>
> >-- create_directory() needs an option to create any necessary parent
> >directories (mkdir -p). At least, I don't think it does that now; it's
> >hard to tell from your description (branch? leaf?).
>
> That's what the "recurse" option would do.
Thanks, that wasn't clear from the comments. I'd be inclined to give
"recurse downwards" (as in remove()) and "recurse upwards" (as in
create_directory()) different option names (mkdir uses "parents" for the
latter), but no examples spring to mind where both options would be
meaningful, so it's probably not important.
> >-- Function to resolve a relative path into a fully qualified absolute
> >path.
>
> Probably. I need to do some research first.
My implementation for Unix is attached below.
-- Ross Smith ...................................... Auckland, New Zealand r-smith_at_[hidden] .................................................... "We need a new cosmology. New gods. New sacraments. Another drink." -- Patti Smith #include <cstring> #include <cstdlib> #include <exception> #include <iostream> #include <stdexcept> #include <sstream> #include <string> #include <vector> #include <limits.h> #include <pwd.h> #include <sys/types.h> #include <unistd.h> #if defined(__CYGWIN__) #include <sys/cygwin.h> #endif // Exception for reporting Unix API function errors class SystemError: public std::exception { public: SystemError(const std::string& details, int error): message_() { std::ostringstream out; out << details << '\n' << "System error " << error << '\n' << std::strerror(error); message_ = out.str(); } ~SystemError() throw() {} virtual const char* what() const throw() { return message_.c_str(); } private: std::string message_; }; // File path resolution function // Argument: A path which may be relative, and include ~ references. // Returns: The corresponding fully qualified absolute path. // Throws: std::invalid_argument if the argument is an empty string; // SystemError if anyting goes wrong with the API calls. // Notes: It is not required that the named file, or any directory // component of the path argument, actually exists. std::string resolve_path(const std::string& path) { // Some of this code is based on item 2.9 of the Unix programming FAQ // (http://www.erlenstar.demon.co.uk/unix/faq_toc.html) // First, check for the trivial case if (path.empty()) throw std::invalid_argument("The path is empty"); std::string resolved(path); // Special case for Cygwin, in case we get handed a native path #if defined(__CYGWIN__) std::vector<char> buffer(PATH_MAX); cygwin_conv_to_full_posix_path(path.c_str(), &buffer[0]); resolved = &buffer[0]; #endif // Check for a leading ~, and expand this to the corresponding home // directory; if the directory doesn't exist, assume it's a real // file name with a leading ~ if (resolved[0] == '~') { std::string prefix; std::string::size_type slash(resolved.find_first_of('/')); if (resolved.size() == 1 || slash == 1) { const char* home(std::getenv("HOME")); if (home == 0) { std::vector<char> buffer(sysconf(_SC_GETPW_R_SIZE_MAX)); passwd temp_pwd; passwd* pwd(0); int rc(getpwuid_r(getuid(), &temp_pwd, &buffer[0], buffer.size(), &pwd)); if (rc != 0) throw SystemError("getpwuid_r(getuid()) failed", rc); if (pwd != 0) prefix = pwd->pw_dir; } else { prefix = home; } } else { std::string user; if (slash == std::string::npos) user = resolved.substr(1); else user = resolved.substr(1, slash - 1); std::vector<char> buffer(sysconf(_SC_GETPW_R_SIZE_MAX)); passwd temp_pwd; passwd* pwd(0); int rc(getpwnam_r(user.c_str(), &temp_pwd, &buffer[0], buffer.size(), &pwd)); if (rc != 0 && rc != ENOENT) throw SystemError("getpwnam_r(" + user + ") failed", rc); if (rc != ENOENT && pwd != 0) prefix = pwd->pw_dir; } if (!prefix.empty()) { if (prefix[prefix.size() - 1] == '/') prefix.resize(prefix.size() - 1); resolved.replace(0, slash, prefix); } } // If the first character isn't /, prefix with the current directory if (resolved[0] != '/') { std::vector<char> buffer(PATH_MAX); const char* rc(getcwd(&buffer[0], PATH_MAX)); if (rc == 0) throw SystemError("getcwd() failed", errno); std::size_t size(std::strlen(&buffer[0])); if (buffer[size - 1] != '/') { buffer[size] = '/'; ++size; } resolved.insert(0, &buffer[0], size); } // Collapse any occurrences of . and .. in the path if (resolved[resolved.size() - 1] != '/') resolved += '/'; std::string::size_type pos(0); for (;;) { pos = resolved.find("/./", pos); if (pos == std::string::npos) break; resolved.erase(pos, 2); ++pos; } pos = 0; for (;;) { pos = resolved.find("/../", pos); if (pos == std::string::npos) break; std::string::size_type cut(pos); while (cut > 0 && resolved[cut - 1] != '/') --cut; if (cut != 0) resolved.erase(cut, pos - cut + 4); pos = cut; } // Strip any trailing slash if (resolved.size() > 1 && resolved[resolved.size() - 1] == '/') resolved.resize(resolved.size() - 1); return resolved; } int main(int argc, char** argv) { try { for (int a(1); a < argc; ++a) std::cout << argv[a] << " -> " << resolve_path(argv[a]) << '\n'; return 0; } catch (const std::exception& ex) { std::cerr << "*** " << ex.what() << '\n'; return EXIT_FAILURE; } }
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk