- Unify filename.h and file_utilities.h under filesystem.h.
- Rename Filename class to Path since it more accurately represents
it.
- Make its interface as close as possible to that of std::filesystem::path.
- Remove unused method expandWithDefault.
- Rename useAsDefault to replaceExtension since it was only used in the
purpose of replacing extension.
- Unroll the split method in the constructor.
;;
esac
- # Extract the name portion (from last slash to last dot) of the config file name
- # File name and extension are documented in src/lib/util/filename.h
+ # Extract the name portion (from last slash to last dot) of the config file name.
local conf_name
conf_name=$(basename -- "${kea_config_file}" | rev | cut -f2- -d'.' | rev)
#include <ha_log.h>
#include <ha_service_states.h>
#include <cc/dhcp_config_error.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
#include <boost/make_shared.hpp>
#include <limits>
#include <set>
#include <asiolink/asio_wrapper.h>
#include <asiolink/crypto_tls.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
using namespace isc::cryptolink;
using namespace isc::util;
#include <http/auth_log.h>
#include <http/basic_auth_config.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
#include <util/strutil.h>
using namespace isc;
using namespace isc::http;
using namespace isc::http::test;
using namespace isc::util;
-namespace ph = std::placeholders;
/// @todo: put the common part of client and server tests in its own file(s).
#include <exceptions/exceptions.h>
-#include <util/filename.h>
+#include <util/filesystem.h>
#include <util/strutil.h>
#include <log/log_messages.h>
using namespace std;
using namespace isc::log;
-using namespace isc::util;
+using namespace isc::util::file;
/// \file log/compiler/message.cc
/// \brief Message Compiler
/// <name>_<ext>, where <name> is the name of the file, and <ext>
/// is the extension less the leading period. The sentinel will be upper-case.
///
-/// \param file Filename object representing the file.
+/// \param file path to the file.
///
/// \return Sentinel name
string
-sentinel(Filename& file) {
-
- string name = file.name();
+sentinel(Path& file) {
+ string name = file.stem();
string ext = file.extension();
string sentinel_text = name + "_" + ext.substr(1);
isc::util::str::uppercase(sentinel_text);
const vector<string>& ns_components,
MessageDictionary& dictionary,
const char* output_directory) {
- Filename message_file(file);
- Filename header_file(Filename(message_file.name()).useAsDefault(".h"));
+ Path message_file(file);
+ Path header_file(Path(file).replaceExtension(".h"));
if (output_directory != NULL) {
- header_file.setDirectory(output_directory);
+ header_file.replaceParentPath(output_directory);
}
// Text to use as the sentinels.
errno = 0;
// Open the output file for writing
- ofstream hfile(header_file.fullName().c_str());
+ ofstream hfile(header_file.str());
if (hfile.fail()) {
isc_throw_4(MessageException, "Failed to open output file",
- LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
+ LOG_OPEN_OUTPUT_FAIL, header_file.str(),
strerror(errno), 0);
}
// after the last write.
hfile <<
- "// File created from " << message_file.fullName() << "\n" <<
+ "// File created from " << message_file.str() << "\n" <<
"\n" <<
"#ifndef " << sentinel_text << "\n" <<
"#define " << sentinel_text << "\n" <<
// Report errors (if any) and exit
if (hfile.fail()) {
isc_throw_4(MessageException, "Error writing to output file",
- LOG_WRITE_ERROR, header_file.fullName(), strerror(errno),
+ LOG_WRITE_ERROR, header_file.str(), strerror(errno),
0);
}
const vector<string>& ns_components,
MessageDictionary& dictionary,
const char* output_directory) {
- Filename message_file(file);
- Filename program_file(Filename(message_file.name()).useAsDefault(".cc"));
+ Path message_file(file);
+ Path program_file(Path(file).replaceExtension(".cc"));
if (output_directory) {
- program_file.setDirectory(output_directory);
+ program_file.replaceParentPath(output_directory);
}
// zero out the errno to be safe
errno = 0;
// Open the output file for writing
- ofstream ccfile(program_file.fullName().c_str());
+ ofstream ccfile(program_file.str());
if (ccfile.fail()) {
isc_throw_4(MessageException, "Error opening output file",
- LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
+ LOG_OPEN_OUTPUT_FAIL, program_file.str(),
strerror(errno), 0);
}
// the last write.
ccfile <<
- "// File created from " << message_file.fullName() << "\n" <<
+ "// File created from " << message_file.str() << "\n" <<
"\n" <<
"#include <cstddef>\n" <<
"#include <log/message_types.h>\n" <<
// Report errors (if any) and exit
if (ccfile.fail()) {
isc_throw_4(MessageException, "Error writing to output file",
- LOG_WRITE_ERROR, program_file.fullName(), strerror(errno),
+ LOG_WRITE_ERROR, program_file.str(), strerror(errno),
0);
}
#include <database/db_log.h>
#include <exceptions/exceptions.h>
#include <mysql/mysql_connection.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
#include <boost/lexical_cast.hpp>
#include <database/db_exceptions.h>
#include <database/db_log.h>
#include <pgsql/pgsql_connection.h>
-#include <util/file_utilities.h>
+#include <util/filesystem.h>
#include <exception>
#include <unordered_map>
#include <log/logger_support.h>
#include <process/config_base.h>
#include <process/redact_config.h>
-#include <util/filename.h>
+#include <util/filesystem.h>
#include <functional>
#include <sstream>
#include <errno.h>
using namespace isc::data;
-namespace ph = std::placeholders;
+using namespace isc::util::file;
/// @brief provides default implementation for basic daemon operations
///
isc_throw(isc::BadValue, "config file name is not set");
}
- // Create Filename instance from the config_file_ pathname, and
+ // Create Path instance from the config_file_ pathname, and
// check the file name component.
- isc::util::Filename file(config_file_);
- if (file.name().empty()) {
+ Path file(config_file_);
+ if (file.stem().empty()) {
isc_throw(isc::BadValue, "config file:" << config_file_
<< " is missing file name");
}
"Daemon::makePIDFileName config file name is not set");
}
- // Create Filename instance from the config_file_ pathname, so we can
+ // Create Path instance from the config_file_ pathname, so we can
// extract the fname component.
- isc::util::Filename file(config_file_);
- if (file.name().empty()) {
+ Path file(config_file_);
+ if (file.stem().empty()) {
isc_throw(isc::BadValue, "Daemon::makePIDFileName config file:"
<< config_file_ << " is missing file name");
}
// Make the pathname for the PID file from the runtime directory,
// configuration name and process name.
std::ostringstream stream;
- stream << pid_file_dir_ << "/" << file.name()
+ stream << pid_file_dir_ << "/" << file.stem()
<< "." << proc_name_ << ".pid";
return(stream.str());
libkea_util_la_SOURCES += csv_file.h csv_file.cc
libkea_util_la_SOURCES += dhcp_space.h dhcp_space.cc
libkea_util_la_SOURCES += doubles.h
-libkea_util_la_SOURCES += file_utilities.h file_utilities.cc
-libkea_util_la_SOURCES += filename.h filename.cc
+libkea_util_la_SOURCES += filesystem.h filesystem.cc
libkea_util_la_SOURCES += hash.h
libkea_util_la_SOURCES += labeled_value.h labeled_value.cc
libkea_util_la_SOURCES += memory_segment.h
chrono_time_utils.h \
dhcp_space.h \
doubles.h \
- file_utilities.h \
- filename.h \
+ filesystem.h \
hash.h \
io_utilities.h \
labeled_value.h \
+++ /dev/null
-// Copyright (C) 2021-2024 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <exceptions/exceptions.h>
-#include <util/filename.h>
-#include <cerrno>
-#include <cstring>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-using namespace std;
-
-namespace isc {
-namespace util {
-namespace file {
-
-string
-getContent(const string& file_name) {
- // Open the file.
- int fd = ::open(file_name.c_str(), O_RDONLY);
- if (fd < 0) {
- isc_throw(BadValue, "can't open file '" << file_name << "': "
- << std::strerror(errno));
- }
- try {
- struct stat stats;
- if (fstat(fd, &stats) < 0) {
- isc_throw(BadValue, "can't stat file '" << file_name << "': "
- << std::strerror(errno));
- }
- if ((stats.st_mode & S_IFMT) != S_IFREG) {
- isc_throw(BadValue, "'" << file_name
- << "' must be a regular file");
- }
- string content(stats.st_size, ' ');
- ssize_t got = ::read(fd, &content[0], stats.st_size);
- if (got < 0) {
- isc_throw(BadValue, "can't read file '" << file_name << "': "
- << std::strerror(errno));
- }
- if (got != stats.st_size) {
- isc_throw(BadValue, "can't read whole file '" << file_name
- << "' (got " << got << " of " << stats.st_size << ")");
- }
- static_cast<void>(close(fd));
- return (content);
- } catch (const std::exception&) {
- static_cast<void>(close(fd));
- throw;
- }
-}
-
-bool
-isDir(const string& name) {
- struct stat stats;
- if (::stat(name.c_str(), &stats) < 0) {
- return (false);
- }
- return ((stats.st_mode & S_IFMT) == S_IFDIR);
-}
-
-bool
-isFile(const string& name) {
- struct stat stats;
- if (::stat(name.c_str(), &stats) < 0) {
- return (false);
- }
- return ((stats.st_mode & S_IFMT) == S_IFREG);
-}
-
-} // namespace file
-} // namespace log
-} // namespace isc
+++ /dev/null
-// Copyright (C) 2021-2024 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef FILE_UTILITIES_H
-#define FILE_UTILITIES_H
-
-#include <string>
-
-namespace isc {
-namespace util {
-namespace file {
-
-/// @brief Get the content of a regular file.
-///
-/// @param file_name The file name.
-/// @return The content of the file_name file.
-/// @throw BadValue when the file can't be opened or is not a regular one.
-std::string getContent(const std::string& file_name);
-
-/// @brief Check if there is a directory at the given path.
-///
-/// @param name the path being checked.
-/// @return True if the name points to a directory, false otherwise including
-/// if the pointed location does not exist.
-bool isDir(const std::string& name);
-
-/// @brief Check if there is a file at the given path.
-///
-/// @param name the path being checked.
-/// @return True if the name points to a file, false otherwise including
-/// if the pointed location does not exist.
-bool isFile(const std::string& name);
-
-} // namespace file
-} // namespace util
-} // namespace isc
-
-#endif // FILE_UTILITIES_H
+++ /dev/null
-// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <iostream>
-#include <algorithm>
-#include <string>
-
-#include <ctype.h>
-
-#include <util/filename.h>
-
-using namespace std;
-
-
-namespace isc {
-namespace util {
-
-// Split string into components. Any backslashes are assumed to have
-// been replaced by forward slashes.
-
-void
-Filename::split(const string& full_name, string& directory, string& name,
- string& extension) const {
- directory = name = extension = "";
- if (!full_name.empty()) {
-
- bool dir_present = false;
- // Find the directory.
- size_t last_slash = full_name.find_last_of('/');
- if (last_slash != string::npos) {
-
- // Found the last slash, so extract directory component and
- // set where the scan for the last_dot should terminate.
- directory = full_name.substr(0, last_slash + 1);
- if (last_slash == full_name.size()) {
-
- // The entire string was a directory, so exit not and don't
- // do any more searching.
- return;
- }
-
- // Found a directory so note the fact.
- dir_present = true;
- }
-
- // Now search backwards for the last ".".
- size_t last_dot = full_name.find_last_of('.');
- if ((last_dot == string::npos) ||
- (dir_present && (last_dot < last_slash))) {
-
- // Last "." either not found or it occurs to the left of the last
- // slash if a directory was present (so it is part of a directory
- // name). In this case, the remainder of the string after the slash
- // is the name part.
- name = full_name.substr(last_slash + 1);
- return;
- }
-
- // Did find a valid dot, so it and everything to the right is the
- // extension...
- extension = full_name.substr(last_dot);
-
- // ... and the name of the file is everything in between.
- if ((last_dot - last_slash) > 1) {
- name = full_name.substr(last_slash + 1, last_dot - last_slash - 1);
- }
- }
-
-}
-
-// Expand the stored filename with the default.
-
-string
-Filename::expandWithDefault(const string& defname) const {
-
- string def_directory("");
- string def_name("");
- string def_extension("");
-
- // Normalize the input string.
- string copy_defname = isc::util::str::trim(defname);
-#ifdef WIN32
- isc::util::str::normalizeSlash(copy_defname);
-#endif
-
- // Split into the components
- split(copy_defname, def_directory, def_name, def_extension);
-
- // Now construct the result.
- string retstring =
- (directory_.empty() ? def_directory : directory_) +
- (name_.empty() ? def_name : name_) +
- (extension_.empty() ? def_extension : extension_);
- return (retstring);
-}
-
-// Use the stored name as default for a given name
-
-string
-Filename::useAsDefault(const string& name) const {
-
- string name_directory("");
- string name_name("");
- string name_extension("");
-
- // Normalize the input string.
- string copy_name = isc::util::str::trim(name);
-#ifdef WIN32
- isc::util::str::normalizeSlash(copy_name);
-#endif
-
- // Split into the components
- split(copy_name, name_directory, name_name, name_extension);
-
- // Now construct the result.
- string retstring =
- (name_directory.empty() ? directory_ : name_directory) +
- (name_name.empty() ? name_ : name_name) +
- (name_extension.empty() ? extension_ : name_extension);
- return (retstring);
-}
-
-void
-Filename::setDirectory(const std::string& new_directory) {
- std::string directory(new_directory);
-
- if (directory.length() > 0) {
- // append '/' if necessary
- size_t sep = directory.rfind('/');
- if (sep == std::string::npos || sep < directory.size() - 1) {
- directory += "/";
- }
- }
- // and regenerate the full name
- std::string full_name = directory + name_ + extension_;
-
- directory_.swap(directory);
- full_name_.swap(full_name);
-}
-
-
-} // namespace log
-} // namespace isc
+++ /dev/null
-// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef FILENAME_H
-#define FILENAME_H
-
-#include <string>
-
-#include <util/strutil.h>
-
-namespace isc {
-namespace util {
-
-/// \brief Class to Manipulate Filenames
-///
-/// This is a utility class to manipulate filenames. It repeats some of the
-/// features found in the Boost filename class, but is self-contained so avoids
-/// the need to link in the Boost library.
-///
-/// A Unix-style filename comprises three parts:
-///
-/// Directory - everything up to and including the last "/". If there is no
-/// "/" in the string, there is no directory component. Note that the
-/// requirement of a trailing slash eliminates the ambiguity of whether a
-/// component is a directory or not, e.g. in /alpha/beta", "beta" could be the
-/// name of a directory or is could be a file. The interpretation here is that
-/// "beta" is the name of a file (although that file could be a directory).
-///
-/// Note: Under Windows, the drive letter is considered to be part of the
-/// directory specification. Unless this class becomes more widely-used on
-/// Windows, there is no point in adding redundant code.
-///
-/// Name - everything from the character after the last "/" up to but not
-/// including the last ".".
-///
-/// Extension - everything from the right-most "." (after the right-most "/") to
-/// the end of the string. If there is no "." after the last "/", there is
-/// no file extension.
-///
-/// (Note that on Windows, this function will replace all "\" characters
-/// with "/" characters on input strings.)
-///
-/// This class provides functions for extracting the components and for
-/// substituting components.
-
-
-class Filename {
-public:
-
- /// \brief Constructor
- Filename(const std::string& name) :
- full_name_(""), directory_(""), name_(""), extension_("") {
- setName(name);
- }
-
- /// \brief Sets Stored Filename
- ///
- /// \param name New name to replaced currently stored name
- void setName(const std::string& name) {
- full_name_ = isc::util::str::trim(name);
-#ifdef WIN32
- isc::util::str::normalizeSlash(full_name_);
-#endif
- split(full_name_, directory_, name_, extension_);
- }
-
- /// \return Stored Filename
- std::string fullName() const {
- return (full_name_);
- }
-
- /// \return Directory of Given File Name
- std::string directory() const {
- return (directory_);
- }
-
- /// \brief Set directory for the file
- ///
- /// \param new_directory The directory to set. If this is an empty
- /// string, the directory this filename object currently
- /// has will be removed.
- void setDirectory(const std::string& new_directory);
-
- /// \return Name of Given File Name
- std::string name() const {
- return (name_);
- }
-
- /// \return Extension of Given File Name
- std::string extension() const {
- return (extension_);
- }
-
- /// \return Name + extension of Given File Name
- std::string nameAndExtension() const {
- return (name_ + extension_);
- }
-
- /// \brief Expand Name with Default
- ///
- /// A default file specified is supplied and used to fill in any missing
- /// fields. For example, if the name stored is "/a/b" and the supplied
- /// name is "c.d", the result is "/a/b.d": the only field missing from the
- /// stored name is the extension, which is supplied by the default.
- /// Another example would be to store "a.b" and to supply a default of
- /// "/c/d/" - the result is "/c/d/a.b". (Note that if the supplied default
- /// was "/c/d", the result would be "/c/a.b", even if "/c/d" were actually
- /// a directory.)
- ///
- /// \param defname Default name
- ///
- /// \return Name expanded with defname.
- std::string expandWithDefault(const std::string& defname) const;
-
- /// \brief Use as Default and Substitute into String
- ///
- /// Does essentially the inverse of expand(); that filled in the stored
- /// name with a default and returned the result. This treats the stored
- /// name as the default and uses it to fill in a given name. In essence,
- /// the code:
- /// \code
- /// Filename f("/a/b");
- /// result = f.expandWithdefault("c.d");
- /// \endcode
- /// gives as a result "/a/b.d". This is the same as:
- /// \code
- /// Filename f("c.d");
- /// result = f.useAsDefault("/a/b");
- /// \endcode
- ///
- /// \param name Name to expand
- ///
- /// \return Name expanded with stored name
- std::string useAsDefault(const std::string& name) const;
-
-private:
- /// \brief Split Name into Components
- ///
- /// Splits the file name into the directory, name and extension parts.
- /// The name is assumed to have had back slashes replaced by forward
- /// slashes (if appropriate).
- ///
- /// \param full_name Name to split
- /// \param directory Returned directory part
- /// \param name Returned name part
- /// \param extension Returned extension part
- void split(const std::string& full_name, std::string& directory,
- std::string& name, std::string& extension) const;
-
- // Members
-
- std::string full_name_; ///< Given name
- std::string directory_; ///< Directory part
- std::string name_; ///< Name part
- std::string extension_; ///< Extension part
-};
-
-} // namespace util
-} // namespace isc
-
-#endif // FILENAME_H
--- /dev/null
+// Copyright (C) 2021-2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <util/filesystem.h>
+#include <util/str.h>
+
+#include <algorithm>
+#include <cctype>
+#include <cerrno>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+using namespace isc::util::str;
+using namespace std;
+
+namespace isc {
+namespace util {
+namespace file {
+
+string
+getContent(string const& file_name) {
+ if (!exists(file_name)) {
+ isc_throw(BadValue, "Expected a file at path '" << file_name << "'");
+ }
+ if (!isFile(file_name)) {
+ isc_throw(BadValue, "Expected '" << file_name << "' to be a regular file");
+ }
+ ifstream file(file_name, ios::in);
+ if (!file.is_open()) {
+ isc_throw(BadValue, "Cannot open '" << file_name);
+ }
+ string content;
+ file >> content;
+ return content;
+}
+
+bool
+exists(string const& path) {
+ struct stat statbuf;
+ return (::stat(path.c_str(), &statbuf) == 0);
+}
+
+bool
+isDir(string const& path) {
+ struct stat statbuf;
+ if (::stat(path.c_str(), &statbuf) < 0) {
+ return (false);
+ }
+ return ((statbuf.st_mode & S_IFMT) == S_IFDIR);
+}
+
+bool
+isFile(string const& path) {
+ struct stat statbuf;
+ if (::stat(path.c_str(), &statbuf) < 0) {
+ return (false);
+ }
+ return ((statbuf.st_mode & S_IFMT) == S_IFREG);
+}
+
+Path::Path(string const& full_name) {
+ if (!full_name.empty()) {
+ bool dir_present = false;
+ // Find the directory.
+ size_t last_slash = full_name.find_last_of('/');
+ if (last_slash != string::npos) {
+ // Found the last slash, so extract directory component and
+ // set where the scan for the last_dot should terminate.
+ parent_path_ = full_name.substr(0, last_slash + 1);
+ if (last_slash == full_name.size()) {
+ // The entire string was a directory, so exit not and don't
+ // do any more searching.
+ return;
+ }
+
+ // Found a directory so note the fact.
+ dir_present = true;
+ }
+
+ // Now search backwards for the last ".".
+ size_t last_dot = full_name.find_last_of('.');
+ if ((last_dot == string::npos) || (dir_present && (last_dot < last_slash))) {
+ // Last "." either not found or it occurs to the left of the last
+ // slash if a directory was present (so it is part of a directory
+ // name). In this case, the remainder of the string after the slash
+ // is the name part.
+ stem_ = full_name.substr(last_slash + 1);
+ return;
+ }
+
+ // Did find a valid dot, so it and everything to the right is the
+ // extension...
+ extension_ = full_name.substr(last_dot);
+
+ // ... and the name of the file is everything in between.
+ if ((last_dot - last_slash) > 1) {
+ stem_ = full_name.substr(last_slash + 1, last_dot - last_slash - 1);
+ }
+ }
+}
+
+string
+Path::str() const {
+ return (parent_path_ + stem_ + extension_);
+}
+
+string
+Path::parentPath() const {
+ return (parent_path_);
+}
+
+string
+Path::stem() const {
+ return (stem_);
+}
+
+string
+Path::extension() const {
+ return (extension_);
+}
+
+string
+Path::filename() const {
+ return (stem_ + extension_);
+}
+
+Path&
+Path::replaceExtension(string const& replacement) {
+ string const trimmed_replacement(trim(replacement));
+ if (trimmed_replacement.empty()) {
+ extension_ = string();
+ } else {
+ size_t const last_dot(trimmed_replacement.find_last_of('.'));
+ if (last_dot == string::npos) {
+ extension_ = "." + trimmed_replacement;
+ } else {
+ extension_ = trimmed_replacement.substr(last_dot);
+ }
+ }
+ return *this;
+}
+
+Path&
+Path::replaceParentPath(string const& replacement) {
+ string const trimmed_replacement(trim(replacement));
+ if (trimmed_replacement.empty()) {
+ parent_path_ = string();
+ } else if (trimmed_replacement.at(trimmed_replacement.size() - 1) == '/') {
+ parent_path_ = trimmed_replacement;
+ } else {
+ parent_path_ = trimmed_replacement + '/';
+ }
+ return *this;
+}
+
+} // namespace file
+} // namespace util
+} // namespace isc
--- /dev/null
+// Copyright (C) 2021-2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef KEA_UTIL_FILESYSTEM_H
+#define KEA_UTIL_FILESYSTEM_H
+
+#include <string>
+
+namespace isc {
+namespace util {
+namespace file {
+
+/// \brief Get the content of a regular file.
+///
+/// \param file_name The file name.
+///
+/// \return The content of the file.
+/// \throw BadValue when the file can't be opened or is not a regular one.
+std::string
+getContent(const std::string& file_name);
+
+/// \brief Check if there is a file or directory at the given path.
+///
+/// \param path The path being checked.
+///
+/// \return True if the path points to a file or a directory, false otherwise.
+bool
+exists(const std::string& path);
+
+/// \brief Check if there is a directory at the given path.
+///
+/// \param path The path being checked.
+///
+/// \return True if the path points to a directory, false otherwise including
+/// if the pointed location does not exist.
+bool
+isDir(const std::string& path);
+
+/// \brief Check if there is a file at the given path.
+///
+/// \param path The path being checked.
+///
+/// \return True if the path points to a file, false otherwise including
+/// if the pointed location does not exist.
+bool
+isFile(const std::string& path);
+
+/// \brief Paths on a filesystem
+struct Path {
+ /// \brief Constructor
+ ///
+ /// Splits the full name into components.
+ Path(std::string const& path);
+
+ /// \brief Get the path in textual format.
+ ///
+ /// Counterpart for std::filesystem::path::string.
+ ///
+ /// \return stored filename.
+ std::string str() const;
+
+ /// \brief Get the parent path.
+ ///
+ /// Counterpart for std::filesystem::path::parent_path.
+ ///
+ /// \return parent path of current path.
+ std::string parentPath() const;
+
+ /// \brief Get the base name of the file without the extension.
+ ///
+ /// Counterpart for std::filesystem::path::stem.
+ ///
+ /// \return the base name of the file without the extension.
+ std::string stem() const;
+
+ /// \brief Get the extension of the file.
+ ///
+ /// Counterpart for std::filesystem::path::extension.
+ ///
+ /// \return extension of current path.
+ std::string extension() const;
+
+ /// \brief Get the extension of the file.
+ ///
+ /// Counterpart for std::filesystem::path::extension.
+ ///
+ /// \return name + extension of current path.
+ std::string filename() const;
+
+ /// \brief Identifies the extension in {replacement}, trims it, and
+ /// replaces this instance's extension with it.
+ ///
+ /// Counterpart for std::filesystem::path::replace_extension.
+ ///
+ /// The change is done in the members and {this} is returned to allow call
+ /// chaining.
+ ///
+ /// \param replacement The extension to replace with.
+ ///
+ /// \return The current instance after the replacement was done.
+ Path& replaceExtension(std::string const& replacement = std::string());
+
+ /// \brief Trims {replacement} and replaces this instance's parent path with
+ /// it.
+ ///
+ /// The change is done in the members and {this} is returned to allow call
+ /// chaining.
+ ///
+ /// \param replacement The parent path to replace with.
+ ///
+ /// \return The current instance after the replacement was done.
+ Path& replaceParentPath(std::string const& replacement = std::string());
+
+private:
+ /// \brief Parent path.
+ std::string parent_path_;
+
+ /// \brief Stem.
+ std::string stem_;
+
+ /// \brief File name extension.
+ std::string extension_;
+};
+
+} // namespace file
+} // namespace util
+} // namespace isc
+
+#endif // KEA_UTIL_FILESYSTEM_H
+++ /dev/null
-// Copyright (C) 2015-2024 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <exceptions/exceptions.h>
-#include <util/file_utilities.h>
-#include <gtest/gtest.h>
-#include <fstream>
-
-using namespace isc;
-using namespace isc::util::file;
-using namespace std;
-
-namespace {
-
-/// @brief Test fixture class for testing operations on files.
-class FileUtilTest : public ::testing::Test {
-public:
-
- /// @brief Destructor.
- ///
- /// Deletes the test file if any.
- virtual ~FileUtilTest();
-};
-
-FileUtilTest::~FileUtilTest() {
- string test_file_name(TEST_DATA_BUILDDIR "/fu.test");
- static_cast<void>(remove(test_file_name.c_str()));
-}
-
-/// @brief Check an error is returned by getContent on not existent file.
-TEST_F(FileUtilTest, notExists) {
- string file_name("/this/does/not/exists");
- try {
- string c = getContent(file_name);
- FAIL() << "this test must throw before this line";
- } catch (const BadValue& ex) {
- string expected = "can't open file '" + file_name;
- expected += "': No such file or directory";
- EXPECT_EQ(string(ex.what()), expected);
- } catch (const std::exception& ex) {
- FAIL() << "unexpected exception: " << ex.what();
- }
-}
-
-/// @note No easy can't stat.
-
-/// @brief Check an error is returned by getContent on not regular file.
-TEST_F(FileUtilTest, notRegular) {
- string file_name("/");
- try {
- string c = getContent(file_name);
- FAIL() << "this test must throw before this line";
- } catch (const BadValue& ex) {
- string expected = "'" + file_name + "' must be a regular file";
- EXPECT_EQ(string(ex.what()), expected);
- } catch (const std::exception& ex) {
- FAIL() << "unexpected exception: " << ex.what();
- }
-}
-
-/// @brief Check getContent works.
-TEST_F(FileUtilTest, basic) {
- string file_name(TEST_DATA_BUILDDIR "/fu.test");
- ofstream fs(file_name.c_str(), ofstream::out | ofstream::trunc);
- ASSERT_TRUE(fs.is_open());
- fs << "abdc";
- fs.close();
- string content;
- EXPECT_NO_THROW(content = getContent(file_name));
- EXPECT_EQ("abdc", content);
-}
-
-/// @brief Check isDir works.
-TEST_F(FileUtilTest, isDir) {
- EXPECT_TRUE(isDir("/dev"));
- EXPECT_FALSE(isDir("/dev/null"));
- EXPECT_FALSE(isDir("/this/does/not/exist"));
- EXPECT_FALSE(isDir("/etc/hosts"));
-}
-
-/// @brief Check isFile.
-TEST_F(FileUtilTest, isFile) {
- EXPECT_TRUE(isFile(ABS_SRCDIR "/file_utilities_unittest.cc"));
- EXPECT_FALSE(isFile(TEST_DATA_BUILDDIR));
-}
-
-} // namespace
+++ /dev/null
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <util/filename.h>
-
-using namespace isc;
-using namespace isc::util;
-using namespace std;
-
-class FilenameTest : public ::testing::Test {
-protected:
- FilenameTest()
- {
- }
-};
-
-
-// Check that the name can be changed
-
-TEST_F(FilenameTest, SetName) {
- Filename fname("/a/b/c.d");
- EXPECT_EQ("/a/b/c.d", fname.fullName());
-
- fname.setName("test.txt");
- EXPECT_EQ("test.txt", fname.fullName());
-}
-
-
-// Check that the components are split correctly. This is a check of the
-// private member split() method.
-
-TEST_F(FilenameTest, Components) {
-
- // Complete name
- Filename fname("/alpha/beta/gamma.delta");
- EXPECT_EQ("/alpha/beta/", fname.directory());
- EXPECT_EQ("gamma", fname.name());
- EXPECT_EQ(".delta", fname.extension());
- EXPECT_EQ("gamma.delta", fname.nameAndExtension());
-
- // Directory only
- fname.setName("/gamma/delta/");
- EXPECT_EQ("/gamma/delta/", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ("", fname.extension());
- EXPECT_EQ("", fname.nameAndExtension());
-
- // Filename only
- fname.setName("epsilon");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("epsilon", fname.name());
- EXPECT_EQ("", fname.extension());
- EXPECT_EQ("epsilon", fname.nameAndExtension());
-
- // Extension only
- fname.setName(".zeta");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ(".zeta", fname.extension());
- EXPECT_EQ(".zeta", fname.nameAndExtension());
-
- // Missing directory
- fname.setName("eta.theta");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("eta", fname.name());
- EXPECT_EQ(".theta", fname.extension());
- EXPECT_EQ("eta.theta", fname.nameAndExtension());
-
- // Missing filename
- fname.setName("/iota/.kappa");
- EXPECT_EQ("/iota/", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ(".kappa", fname.extension());
- EXPECT_EQ(".kappa", fname.nameAndExtension());
-
- // Missing extension
- fname.setName("lambda/mu/nu");
- EXPECT_EQ("lambda/mu/", fname.directory());
- EXPECT_EQ("nu", fname.name());
- EXPECT_EQ("", fname.extension());
- EXPECT_EQ("nu", fname.nameAndExtension());
-
- // Check that the decomposition can occur in the presence of leading and
- // trailing spaces
- fname.setName(" lambda/mu/nu\t ");
- EXPECT_EQ("lambda/mu/", fname.directory());
- EXPECT_EQ("nu", fname.name());
- EXPECT_EQ("", fname.extension());
- EXPECT_EQ("nu", fname.nameAndExtension());
-
- // Empty string
- fname.setName("");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ("", fname.extension());
- EXPECT_EQ("", fname.nameAndExtension());
-
- // ... and just spaces
- fname.setName(" ");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ("", fname.extension());
- EXPECT_EQ("", fname.nameAndExtension());
-
- // Check corner cases - where separators are present, but strings are
- // absent.
- fname.setName("/");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ("", fname.extension());
- EXPECT_EQ("", fname.nameAndExtension());
-
- fname.setName(".");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ(".", fname.extension());
- EXPECT_EQ(".", fname.nameAndExtension());
-
- fname.setName("/.");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ(".", fname.extension());
- EXPECT_EQ(".", fname.nameAndExtension());
-
- // Note that the space is a valid filename here; only leading and trailing
- // spaces should be trimmed.
- fname.setName("/ .");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ(" ", fname.name());
- EXPECT_EQ(".", fname.extension());
- EXPECT_EQ(" .", fname.nameAndExtension());
-
- fname.setName(" / . ");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ(" ", fname.name());
- EXPECT_EQ(".", fname.extension());
- EXPECT_EQ(" .", fname.nameAndExtension());
-}
-
-// Check that the expansion with a default works.
-
-TEST_F(FilenameTest, ExpandWithDefault) {
- Filename fname("a.b");
-
- // These tests also check that the trimming of the default component is
- // done properly.
- EXPECT_EQ("/c/d/a.b", fname.expandWithDefault(" /c/d/ "));
- EXPECT_EQ("/c/d/a.b", fname.expandWithDefault("/c/d/e.f"));
- EXPECT_EQ("a.b", fname.expandWithDefault("e.f"));
-
- fname.setName("/a/b/c");
- EXPECT_EQ("/a/b/c.d", fname.expandWithDefault(".d"));
- EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("x.d"));
- EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("/s/t/u.d"));
- EXPECT_EQ("/a/b/c", fname.expandWithDefault("/s/t/u"));
-
- fname.setName(".h");
- EXPECT_EQ("/a/b/c.h", fname.expandWithDefault("/a/b/c.msg"));
-}
-
-// Check that we can use this as a default in expanding a filename
-
-TEST_F(FilenameTest, UseAsDefault) {
-
- Filename fname("a.b");
-
- // These tests also check that the trimming of the default component is
- // done properly.
- EXPECT_EQ("/c/d/a.b", fname.useAsDefault(" /c/d/ "));
- EXPECT_EQ("/c/d/e.f", fname.useAsDefault("/c/d/e.f"));
- EXPECT_EQ("e.f", fname.useAsDefault("e.f"));
-
- fname.setName("/a/b/c");
- EXPECT_EQ("/a/b/c.d", fname.useAsDefault(".d"));
- EXPECT_EQ("/a/b/x.d", fname.useAsDefault("x.d"));
- EXPECT_EQ("/s/t/u.d", fname.useAsDefault("/s/t/u.d"));
- EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u"));
- EXPECT_EQ("/a/b/c", fname.useAsDefault(""));
-}
-
-TEST_F(FilenameTest, setDirectory) {
- Filename fname("a.b");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("a.b", fname.fullName());
- EXPECT_EQ("a.b", fname.expandWithDefault(""));
-
- fname.setDirectory("/just/some/dir/");
- EXPECT_EQ("/just/some/dir/", fname.directory());
- EXPECT_EQ("/just/some/dir/a.b", fname.fullName());
- EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault(""));
-
- fname.setDirectory("/just/some/dir");
- EXPECT_EQ("/just/some/dir/", fname.directory());
- EXPECT_EQ("/just/some/dir/a.b", fname.fullName());
- EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault(""));
-
- fname.setDirectory("/");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ("/a.b", fname.fullName());
- EXPECT_EQ("/a.b", fname.expandWithDefault(""));
-
- fname.setDirectory("");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("a.b", fname.fullName());
- EXPECT_EQ("a.b", fname.expandWithDefault(""));
-
- fname = Filename("/first/a.b");
- EXPECT_EQ("/first/", fname.directory());
- EXPECT_EQ("/first/a.b", fname.fullName());
- EXPECT_EQ("/first/a.b", fname.expandWithDefault(""));
-
- fname.setDirectory("/just/some/dir");
- EXPECT_EQ("/just/some/dir/", fname.directory());
- EXPECT_EQ("/just/some/dir/a.b", fname.fullName());
- EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault(""));
-}
--- /dev/null
+// Copyright (C) 2015-2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <testutils/gtest_utils.h>
+#include <util/filesystem.h>
+
+#include <fstream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::util::file;
+using namespace std;
+
+namespace {
+
+/// @brief Test fixture class for testing operations on files.
+struct FileUtilTest : ::testing::Test {
+ /// @brief Destructor.
+ ///
+ /// Deletes the test file if any.
+ virtual ~FileUtilTest() {
+ string test_file_name(TEST_DATA_BUILDDIR "/fu.test");
+ static_cast<void>(remove(test_file_name.c_str()));
+ }
+};
+
+/// @brief Check that an error is returned by getContent on non-existent file.
+TEST_F(FileUtilTest, notExist) {
+ EXPECT_THROW_MSG(getContent("/does/not/exist"), BadValue,
+ "Expected a file at path '/does/not/exist'");
+}
+
+/// @brief Check that an error is returned by getContent on not regular file.
+TEST_F(FileUtilTest, notRegular) {
+ EXPECT_THROW_MSG(getContent("/"), BadValue, "Expected '/' to be a regular file");
+}
+
+/// @brief Check getContent.
+TEST_F(FileUtilTest, getContent) {
+ string file_name(TEST_DATA_BUILDDIR "/fu.test");
+ ofstream fs(file_name.c_str(), ofstream::out | ofstream::trunc);
+ ASSERT_TRUE(fs.is_open());
+ fs << "abdc";
+ fs.close();
+ string content;
+ EXPECT_NO_THROW_LOG(content = getContent(file_name));
+ EXPECT_EQ("abdc", content);
+}
+
+/// @brief Check isDir.
+TEST_F(FileUtilTest, isDir) {
+ EXPECT_TRUE(isDir("/dev"));
+ EXPECT_FALSE(isDir("/dev/null"));
+ EXPECT_FALSE(isDir("/this/does/not/exist"));
+ EXPECT_FALSE(isDir("/etc/hosts"));
+}
+
+/// @brief Check isFile.
+TEST_F(FileUtilTest, isFile) {
+ EXPECT_TRUE(isFile(ABS_SRCDIR "/filesystem_unittests.cc"));
+ EXPECT_FALSE(isFile(TEST_DATA_BUILDDIR));
+}
+
+/// @brief Check that the components are split correctly.
+TEST(PathTest, components) {
+ // Complete name
+ Path fname("/alpha/beta/gamma.delta");
+ EXPECT_EQ("/alpha/beta/", fname.parentPath());
+ EXPECT_EQ("gamma", fname.stem());
+ EXPECT_EQ(".delta", fname.extension());
+ EXPECT_EQ("gamma.delta", fname.filename());
+}
+
+/// @brief Check replaceExtension.
+TEST(PathTest, replaceExtension) {
+ Path fname("a.b");
+
+ EXPECT_EQ("a", fname.replaceExtension("").str());
+ EXPECT_EQ("a.f", fname.replaceExtension(".f").str());
+ EXPECT_EQ("a.f", fname.replaceExtension("f").str());
+ EXPECT_EQ("a./c/d/", fname.replaceExtension(" /c/d/ ").str());
+ EXPECT_EQ("a.f", fname.replaceExtension("/c/d/e.f").str());
+ EXPECT_EQ("a.f", fname.replaceExtension("e.f").str());
+}
+
+/// @brief Check replaceParentPath.
+TEST(PathTest, replaceParentPath) {
+ Path fname("a.b");
+ EXPECT_EQ("", fname.parentPath());
+ EXPECT_EQ("a.b", fname.str());
+
+ fname.replaceParentPath("/just/some/dir/");
+ EXPECT_EQ("/just/some/dir/", fname.parentPath());
+ EXPECT_EQ("/just/some/dir/a.b", fname.str());
+
+ fname.replaceParentPath("/just/some/dir");
+ EXPECT_EQ("/just/some/dir/", fname.parentPath());
+ EXPECT_EQ("/just/some/dir/a.b", fname.str());
+
+ fname.replaceParentPath("/");
+ EXPECT_EQ("/", fname.parentPath());
+ EXPECT_EQ("/a.b", fname.str());
+
+ fname.replaceParentPath("");
+ EXPECT_EQ("", fname.parentPath());
+ EXPECT_EQ("a.b", fname.str());
+
+ fname = Path("/first/a.b");
+ EXPECT_EQ("/first/", fname.parentPath());
+ EXPECT_EQ("/first/a.b", fname.str());
+
+ fname.replaceParentPath("/just/some/dir");
+ EXPECT_EQ("/just/some/dir/", fname.parentPath());
+ EXPECT_EQ("/just/some/dir/a.b", fname.str());
+}
+
+} // namespace