/* * (c) Copyright, Real-Time Innovations, 2018. * All rights reserved. * * No duplications, whole or partial, manual or electronic, may be made * without express written permission. Any such copies, or * revisions thereof, must display this notice unaltered. * This code contains trade secrets of Real-Time Innovations, Inc. */ #ifndef HPP_APPUTILS_PATH_HPP_ #define HPP_APPUTILS_PATH_HPP_ // IMPORTANT: macros.hpp must be the first RTI header included in every header // file so that symbols are exported correctly on Windows #include #include #include #include #include #include #include #include #include #include #include #if defined(RTI_UNIX) || defined(RTI_QNX) #include #include #endif #if defined(RTI_UNIX) || defined(RTI_QNX) #if !defined(_XOPEN_SOURCE) && !defined(RTI_DARWIN) #define _XOPEN_SOURCE 600 #endif #include #include #include #include #endif #ifdef RTI_WIN32 #include #endif #include "osapi/osapi_type.h" #include "osapi/osapi_utility.h" #include #include namespace rti { namespace apputils { namespace util { class Path { public: Path() { } Path(const std::string& path) : path_string_(path) { path_string_ = remove_double_separators(path_string_); path_string_ = remove_trailing_separators(path_string_); } Path(const Path& path) : path_string_(path.path_string_) { } operator const std::string &() const { return path_string_; } operator const char *() const { return path_string_.c_str(); } const char *c_str() const { return path_string_.c_str(); } bool empty() const { return path_string_.empty(); } void append(const std::string& fragment) { if (!path_string_.empty()) { path_string_ += "/"; } path_string_ += fragment; path_string_ = remove_double_separators(path_string_); path_string_ = remove_trailing_separators(path_string_); } void append(const Path& fragment) { append((const std::string&) fragment); } void operator+=(const std::string& fragment) { append(fragment); } void operator+=(const Path& fragment) { append((const std::string&) fragment); } Path operator+(const std::string& other) const { Path path(*this); path.append(other); return path; } Path operator+(const Path& other) const { Path path(*this); path.append(other); return path; } int compare(const Path& other) { return path_string_.compare((const std::string&) other); } int compare(const std::string& other) { return path_string_.compare(other); } int compare(const char *other) { return path_string_.compare(other); } /** * Path is interpreted as a file. Return whether the file exists or not. */ bool exists() const { // Check if the path is a file and if it exists if (RTIOsapiFile_exists(path_string_.c_str()) == RTI_TRUE) { return true; } else { #if defined(RTI_UNIX) || defined(RTI_QNX) // Otherwise check if it's a directory and if it exists struct stat info; if (stat(path_string_.c_str(), &info) == 0 && S_ISDIR(info.st_mode)) { return true; } else { return false; } #elif defined(RTI_WIN32) #ifndef S_ISDIR #define S_ISDIR(mode) (((mode) &_S_IFMT) == _S_IFDIR) #endif // Otherwise check if it's a directory and if it exists struct _stat info; if (_stat(path_string_.c_str(), &info) == 0 && S_ISDIR(info.st_mode)) { return true; } else { return false; } #endif } } static void create_directory(const Path& path) { #if defined(RTI_UNIX) || defined(RTI_QNX) errno = 0; if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && (errno != EEXIST)) { std::stringstream error_msg; error_msg << "!create directory: " << path << "\n" << "CreateDirectory() error code: " << errno << "\n"; throw dds::core::IllegalOperationError(error_msg.str()); } #elif defined(RTI_WIN32) if (!CreateDirectory(path, NULL)) { DWORD last_error = GetLastError(); if (last_error != ERROR_ALREADY_EXISTS) { std::stringstream error_msg; error_msg << "!create directory: " << path << "\n" << "CreateDirectory() error code: " << last_error << "\n"; throw dds::core::IllegalOperationError(error_msg.str()); } } #else throw dds::core::IllegalOperationError("unsupported"); #endif } /** * Interpreting the path as a directory, create any non-existing directories * in the path. */ void create_directory() { // If the path is a file, and it exists, exit if (exists()) { return; } // Get splits, build a std::vector splits = split(); Path aux; for (std::vector::iterator split_it = splits.begin(); split_it != splits.end(); split_it++) { aux += *split_it; if (!aux.exists()) { create_directory(aux); } } } static void remove_directory(const Path& path) { #if defined(RTI_UNIX) || defined(RTI_QNX) errno = 0; if (nftw(path, remove_file, 20, FTW_DEPTH) == -1) { if (errno != ENOENT) { perror("nftw"); RTI_THROW_SERVICE_EXCEPTION( &RTI_LOG_ANY_FAILURE_s, "remove_directory() error"); } } #else if (!RTIOsapiFile_removeFileOrDirectory(path.c_str())) { RTI_THROW_SERVICE_EXCEPTION( &RTI_LOG_ANY_ss, "Failed to delete directory: ", path.c_str()); } #endif } void copy_to(const Path& destination_path) { if (!exists()) { return; } try { std::ifstream src(path_string_.c_str(), std::ios::binary); std::ofstream dst( destination_path.path_string_.c_str(), std::ios::binary); dst << src.rdbuf(); } catch (std::exception& ex) { std::stringstream stream; stream << "Failed to copy file" << std::endl; stream << " origin : " << path_string_ << std::endl; stream << " destination: " << destination_path << std::endl; stream << " error message: " << ex.what() << std::endl; DDSLog_exception(&RTI_LOG_ANY_s, stream.str().c_str()); } } void append_extension(const std::string& extension) { path_string_ += extension; } /** * This method detects and returns a (last) "." in the last split of a * string. The substring after this dot is considered to be the file * extension. */ std::string detect_file_extension() { std::string file_extension; std::vector splits = split(); std::string last_split = splits[splits.size() - 1]; std::string::size_type pos = last_split.find_last_of('.'); if (pos != std::string::npos) { file_extension = last_split.substr(pos + 1); } return file_extension; } /** * This method returns the file name (the last split in the path). Depending * on the parameter, it will strip out the file extension or not. */ std::string detect_file_name(bool include_extension = true) const { std::vector splits = split(); std::string last_split = splits[splits.size() - 1]; if (!include_extension) { std::string::size_type pos = last_split.find_last_of('.'); if (pos != std::string::npos) { return last_split.substr(0, pos); } } return last_split; } std::vector split() const { std::vector splits; std::istringstream stream(path_string_); std::string string_split; bool prepend_separator = false; while (getline(stream, string_split, '/')) { if (string_split.empty()) { // This can happen if the path is absolute (starts with a // separator). The first split should contain that separator. // Signal this with a boolean variable prepend_separator = true; continue; } if (prepend_separator) { splits.push_back("/" + string_split); prepend_separator = false; } else { splits.push_back(string_split); } } return splits; } static Path from_splits(const std::vector& splits) { Path path; for (size_t i = 0; i < splits.size(); i++) { path += splits[i]; } return path; } uint64_t file_size() const { #ifdef RTI_WIN32 HANDLE file_handle = CreateFile( path_string_.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { DDSLog_warn( &RTI_LOG_ANY_ss, "Failed to get file size for file: ", path_string_.c_str()); return 0; } LARGE_INTEGER size; if (!GetFileSizeEx(file_handle, &size)) { CloseHandle(file_handle); DDSLog_warn( &RTI_LOG_ANY_ss, "Failed to get file size for file: ", path_string_.c_str()); return 0; } CloseHandle(file_handle); return size.QuadPart; #else struct stat buffer; if (stat(path_string_.c_str(), &buffer) != 0) { DDSLog_warn( &RTI_LOG_ANY_ss, "Failed to get file size for file: ", path_string_.c_str()); return 0; } return (uint64_t) buffer.st_size; #endif } #if !defined(RTI_INTY) /* * CORE-10727 [Antonio L]: * cwd() is not supported in all the architectures. We have to * port it. */ static Path cwd() { char working_directory[RTI_OSAPI_STRING_SEQ_STRING_MAX_SIZE] = { 0 }; bool no_error = false; no_error = (RTIOsapiUtility_getCurrentDirectory( working_directory, RTI_OSAPI_STRING_SEQ_STRING_MAX_SIZE) != NULL); if (!no_error) { int errnum = RTIOsapiUtility_getError(); char error_string[RTI_OSAPI_ERROR_STRING_MAX_SIZE] = { 0 }; RTI_THROW_SERVICE_EXCEPTION( &RTI_LOG_OS_FAILURE_sXs, "getcwd", errnum, RTIOsapiUtility_getErrorString( error_string, RTI_OSAPI_ERROR_STRING_MAX_SIZE, errnum)); } return Path(working_directory); } #endif private: std::string path_string_; #define SEPARATOR_STR "/" /** * Converter Windows-style separators into Unix-style ones */ std::string normalize(const std::string& path) { std::string normalized(path); const std::string SEPARATOR(SEPARATOR_STR); std::string::size_type pos = normalized.find_first_of('\\'); while (pos != std::string::npos) { normalized = normalized.replace(pos, 1, SEPARATOR); pos = normalized.find_first_of('\\', pos + 1); } return normalized; } std::string remove_trailing_separators(const std::string& path) { std::string modified(normalize(path)); if (!modified.empty() && modified.at(modified.size() - 1) == '/') { modified.erase(modified.size() - 1); } return modified; } std::string remove_double_separators(std::string& path) { const std::string DOUBLE_SEPARATOR_STR("//"); std::string modified(normalize(path)); std::string::size_type pos = modified.find(DOUBLE_SEPARATOR_STR); while (pos != std::string::npos) { modified = modified.replace( pos, DOUBLE_SEPARATOR_STR.size(), SEPARATOR_STR); pos = modified.find(DOUBLE_SEPARATOR_STR, pos + 1); } return modified; } static int remove_file( const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) { RTIOsapiUtility_unusedParameter(sb); RTIOsapiUtility_unusedParameter(tflag); RTIOsapiUtility_unusedParameter(ftwbuf); #ifdef RTI_QNX /** * In QNX we create the working directory in the NFS. Because the NFS * have important delays, if we try to remove all the directories, * the remove will fail because the NFS will accept the request to * remove the files, but they won't be removed once we reach the upper * level. Therefore, we will not remove the directories of the QNX * testers */ struct stat buf; int err = stat(fpath, &buf); errno = 0; if (err == 0 && (buf.st_mode & _S_IFMT) != S_IFDIR) errno = remove(fpath); return errno; #else return remove(fpath); #endif } }; }}} // namespace rti::apputils::util #endif /* HPP_APPUTILS_PATH_HPP_ */