642 lines
14 KiB
Text
642 lines
14 KiB
Text
|
/**
|
||
|
* PANDA 3D SOFTWARE
|
||
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||
|
*
|
||
|
* All use of this software is subject to the terms of the revised BSD
|
||
|
* license. You should have received a copy of this license along
|
||
|
* with this source code in a file named "LICENSE."
|
||
|
*
|
||
|
* @file filename.I
|
||
|
* @author drose
|
||
|
* @date 1999-01-18
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename::
|
||
|
Filename(const std::string &filename) {
|
||
|
_flags = 0;
|
||
|
(*this) = filename;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename::
|
||
|
Filename(const std::wstring &filename) {
|
||
|
_flags = 0;
|
||
|
(*this) = filename;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename::
|
||
|
Filename(const char *filename) {
|
||
|
_flags = 0;
|
||
|
(*this) = filename;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename::
|
||
|
Filename(const Filename ©) :
|
||
|
_filename(copy._filename.c_str()),
|
||
|
_dirname_end(copy._dirname_end),
|
||
|
_basename_start(copy._basename_start),
|
||
|
_basename_end(copy._basename_end),
|
||
|
_extension_start(copy._extension_start),
|
||
|
_hash_start(copy._hash_start),
|
||
|
_hash_end(copy._hash_end),
|
||
|
_flags(copy._flags)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename::
|
||
|
Filename(std::string &&filename) noexcept : _flags(0) {
|
||
|
(*this) = std::move(filename);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename::
|
||
|
Filename(Filename &&from) noexcept :
|
||
|
_filename(std::move(from._filename)),
|
||
|
_dirname_end(from._dirname_end),
|
||
|
_basename_start(from._basename_start),
|
||
|
_basename_end(from._basename_end),
|
||
|
_extension_start(from._extension_start),
|
||
|
_hash_start(from._hash_start),
|
||
|
_hash_end(from._hash_end),
|
||
|
_flags(from._flags)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates an empty Filename.
|
||
|
*/
|
||
|
INLINE Filename::
|
||
|
Filename() :
|
||
|
_dirname_end(0),
|
||
|
_basename_start(0),
|
||
|
_basename_end(std::string::npos),
|
||
|
_extension_start(std::string::npos),
|
||
|
_hash_start(std::string::npos),
|
||
|
_hash_end(std::string::npos),
|
||
|
_flags(0) {
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
text_filename(const Filename &filename) {
|
||
|
Filename result(filename);
|
||
|
result.set_text();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
text_filename(const std::string &filename) {
|
||
|
Filename result(filename);
|
||
|
result.set_text();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
binary_filename(const Filename &filename) {
|
||
|
Filename result(filename);
|
||
|
result.set_binary();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
binary_filename(const std::string &filename) {
|
||
|
Filename result(filename);
|
||
|
result.set_binary();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
dso_filename(const std::string &filename) {
|
||
|
Filename result(filename);
|
||
|
result.set_type(T_dso);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
executable_filename(const std::string &filename) {
|
||
|
Filename result(filename);
|
||
|
result.set_type(T_executable);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructs a filename that represents a sequence of numbered files. See
|
||
|
* set_pattern().
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
pattern_filename(const std::string &filename) {
|
||
|
Filename result(filename);
|
||
|
result.set_pattern(true);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename &Filename::
|
||
|
operator = (const std::string &filename) {
|
||
|
_filename = filename;
|
||
|
|
||
|
locate_basename();
|
||
|
locate_extension();
|
||
|
locate_hash();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename &Filename::
|
||
|
operator = (const std::wstring &filename) {
|
||
|
TextEncoder encoder;
|
||
|
encoder.set_encoding(get_filesystem_encoding());
|
||
|
encoder.set_wtext(filename);
|
||
|
return operator = (encoder.get_text());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename &Filename::
|
||
|
operator = (const char *filename) {
|
||
|
assert(filename != nullptr);
|
||
|
return (*this) = std::string(filename);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename &Filename::
|
||
|
operator = (const Filename ©) {
|
||
|
_filename = copy._filename;
|
||
|
_dirname_end = copy._dirname_end;
|
||
|
_basename_start = copy._basename_start;
|
||
|
_basename_end = copy._basename_end;
|
||
|
_extension_start = copy._extension_start;
|
||
|
_hash_start = copy._hash_start;
|
||
|
_hash_end = copy._hash_end;
|
||
|
_flags = copy._flags;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename &Filename::
|
||
|
operator = (std::string &&filename) noexcept {
|
||
|
_filename = std::move(filename);
|
||
|
|
||
|
locate_basename();
|
||
|
locate_extension();
|
||
|
locate_hash();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename &Filename::
|
||
|
operator = (Filename &&from) noexcept {
|
||
|
_filename = std::move(from._filename);
|
||
|
_dirname_end = from._dirname_end;
|
||
|
_basename_start = from._basename_start;
|
||
|
_basename_end = from._basename_end;
|
||
|
_extension_start = from._extension_start;
|
||
|
_hash_start = from._hash_start;
|
||
|
_hash_end = from._hash_end;
|
||
|
_flags = from._flags;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE Filename::
|
||
|
operator const std::string & () const {
|
||
|
return _filename;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE const char *Filename::
|
||
|
c_str() const {
|
||
|
return _filename.c_str();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
empty() const {
|
||
|
return _filename.empty();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE size_t Filename::
|
||
|
length() const {
|
||
|
return _filename.length();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE char Filename::
|
||
|
operator [] (size_t n) const {
|
||
|
assert(n < _filename.length());
|
||
|
return _filename[n];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
substr(size_t begin) const {
|
||
|
return _filename.substr(begin);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
substr(size_t begin, size_t end) const {
|
||
|
return _filename.substr(begin, end);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Appends the other filename onto the end of this one. This does not
|
||
|
* introduce an intervening slash, but see the Filename constructor that takes
|
||
|
* two parameters.
|
||
|
*/
|
||
|
INLINE void Filename::
|
||
|
operator += (const std::string &other) {
|
||
|
_filename += other;
|
||
|
locate_basename();
|
||
|
locate_extension();
|
||
|
locate_hash();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a new Filename representing the concatenation of the two filenames.
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
operator + (const std::string &other) const {
|
||
|
Filename a(*this);
|
||
|
a += other;
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a new Filename that is composed of the other filename added to the
|
||
|
* end of this filename, with an intervening slash added if necessary.
|
||
|
*/
|
||
|
INLINE Filename Filename::
|
||
|
operator / (const Filename &other) const {
|
||
|
return Filename(*this, other);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the entire filename: directory, basename, extension. This is the
|
||
|
* same thing returned by the string typecast operator.
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
get_fullpath() const {
|
||
|
return _filename;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the entire filename as a wide-character string.
|
||
|
*/
|
||
|
INLINE std::wstring Filename::
|
||
|
get_fullpath_w() const {
|
||
|
TextEncoder encoder;
|
||
|
encoder.set_encoding(get_filesystem_encoding());
|
||
|
encoder.set_text(get_fullpath());
|
||
|
return encoder.get_wtext();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the directory part of the filename. This is everything in the
|
||
|
* filename up to, but not including the rightmost slash.
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
get_dirname() const {
|
||
|
return _filename.substr(0, _dirname_end);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the basename part of the filename. This is everything in the
|
||
|
* filename after the rightmost slash, including any extensions.
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
get_basename() const {
|
||
|
return _filename.substr(_basename_start);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns the full filename--directory and basename parts--except for the
|
||
|
* extension.
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
get_fullpath_wo_extension() const {
|
||
|
return _filename.substr(0, _basename_end);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns the basename part of the filename, without the file extension.
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
get_basename_wo_extension() const {
|
||
|
if (_basename_end == std::string::npos) {
|
||
|
return _filename.substr(_basename_start);
|
||
|
} else {
|
||
|
return _filename.substr(_basename_start, _basename_end - _basename_start);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns the file extension. This is everything after the rightmost dot, if
|
||
|
* there is one, or the empty string if there is not.
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
get_extension() const {
|
||
|
if (_extension_start == std::string::npos) {
|
||
|
return std::string();
|
||
|
} else {
|
||
|
return _filename.substr(_extension_start);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Indicates that the filename represents a binary file. This is primarily
|
||
|
* relevant to the read_file() and write_file() methods, so they can set the
|
||
|
* appropriate flags to the OS.
|
||
|
*/
|
||
|
INLINE void Filename::
|
||
|
set_binary() {
|
||
|
_flags = (_flags & ~F_text) | F_binary;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Indicates that the filename represents a text file. This is primarily
|
||
|
* relevant to the read_file() and write_file() methods, so they can set the
|
||
|
* appropriate flags to the OS.
|
||
|
*/
|
||
|
INLINE void Filename::
|
||
|
set_text() {
|
||
|
_flags = (_flags & ~F_binary) | F_text;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the Filename has been indicated to represent a binary file
|
||
|
* via a previous call to set_binary(). It is possible that neither
|
||
|
* is_binary() nor is_text() will be true, if neither set_binary() nor
|
||
|
* set_text() was ever called.
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
is_binary() const {
|
||
|
return ((_flags & F_binary) != 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true either is_binary() or is_text() is true; that is, that the
|
||
|
* filename has been specified as either binary or text. If this is false,
|
||
|
* the filename has not been specified.
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
is_binary_or_text() const {
|
||
|
return ((_flags & (F_binary | F_text)) != 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the Filename has been indicated to represent a text file
|
||
|
* via a previous call to set_text(). It is possible that neither is_binary()
|
||
|
* nor is_text() will be true, if neither set_binary() nor set_text() was ever
|
||
|
* called.
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
is_text() const {
|
||
|
return ((_flags & F_text) != 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the type of the file represented by the filename. This is useful for
|
||
|
* to_os_specific(), resolve_filename(), test_existence(), and all such real-
|
||
|
* world access functions. It helps the Filename know how to map the internal
|
||
|
* filename to the OS-specific filename (for instance, maybe executables
|
||
|
* should have an .exe extension).
|
||
|
*/
|
||
|
INLINE void Filename::
|
||
|
set_type(Filename::Type type) {
|
||
|
_flags = (_flags & ~F_type) | type;
|
||
|
switch (type) {
|
||
|
case T_dso:
|
||
|
case T_executable:
|
||
|
set_binary();
|
||
|
|
||
|
case T_general:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the type of the file represented by the filename, as previously set
|
||
|
* by set_type().
|
||
|
*/
|
||
|
INLINE Filename::Type Filename::
|
||
|
get_type() const {
|
||
|
return (Type)(_flags & (int)F_type);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the flag indicating whether this is a filename pattern. When this is
|
||
|
* true, the filename is understood to be a placeholder for a numbered
|
||
|
* sequence of filename, such as an image sequence. In this case, a sequence
|
||
|
* of one or more hash characters ("#") should appear in the filename string;
|
||
|
* these characters will be filled in with the corresponding number (or more)
|
||
|
* of digits representing the sequence number. Sequence numbers always begin
|
||
|
* counting at 0.
|
||
|
*
|
||
|
* When this is true, methods like has_hash() and get_hash_to_end() and
|
||
|
* get_filename_index() may be called. Methods like is_exists() will
|
||
|
* implicitly test for existance of filename sequence 0.
|
||
|
*/
|
||
|
INLINE void Filename::
|
||
|
set_pattern(bool pattern) {
|
||
|
if (pattern != get_pattern()) {
|
||
|
if (pattern) {
|
||
|
_flags |= F_pattern;
|
||
|
} else {
|
||
|
_flags &= ~F_pattern;
|
||
|
}
|
||
|
locate_hash();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the flag indicating whether this is a filename pattern. See
|
||
|
* set_pattern().
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
get_pattern() const {
|
||
|
return (_flags & F_pattern) != 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the filename is indicated to be a filename pattern (that
|
||
|
* is, set_pattern(true) was called), and the filename pattern did include a
|
||
|
* sequence of hash marks, or false if it was not a filename pattern or did
|
||
|
* not include hash marks. If this is true, then get_filename_index() will
|
||
|
* return a different filename each time.
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
has_hash() const {
|
||
|
return (_hash_start != _hash_end);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the part of the filename beginning at the hash sequence (if any),
|
||
|
* and continuing to the end of the filename.
|
||
|
*/
|
||
|
INLINE std::string Filename::
|
||
|
get_hash_to_end() const {
|
||
|
return _filename.substr(_hash_start);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the filename is local, e.g. does not begin with a slash,
|
||
|
* or false if the filename is fully specified from the root.
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
is_local() const {
|
||
|
return _filename.empty() || _filename[0] != '/';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the filename is fully qualified, e.g. begins with a slash.
|
||
|
* This is almost, but not quite, the same thing as !is_local(). It's not
|
||
|
* exactly the same because a special case is made for filenames that begin
|
||
|
* with a single dot followed by a slash--these are considered to be fully
|
||
|
* qualified (they are explicitly relative to the current directory, and do
|
||
|
* not refer to a filename on a search path somewhere).
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
is_fully_qualified() const {
|
||
|
return
|
||
|
(_filename.size() > 2 && _filename[0] == '.' && _filename[1] == '/') ||
|
||
|
(!_filename.empty() && _filename[0] == '/');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
operator == (const std::string &other) const {
|
||
|
return (*(std::string *)this) == other;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
operator != (const std::string &other) const {
|
||
|
return (*(std::string *)this) != other;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
operator < (const std::string &other) const {
|
||
|
return (*(std::string *)this) < other;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE int Filename::
|
||
|
compare_to(const Filename &other) const {
|
||
|
return strcmp(_filename.c_str(), other._filename.c_str());
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns true if the Filename is valid (not empty), or false if it is an
|
||
|
* empty string.
|
||
|
*
|
||
|
* This implements the Python equivalent to operator bool. Defining an actual
|
||
|
* operator bool method for C++ use would work too, but it seems to cause too
|
||
|
* many ambiguities for the C++ compiler, so we use this Python-only approach
|
||
|
* instead.
|
||
|
*/
|
||
|
INLINE bool Filename::
|
||
|
__nonzero__() const {
|
||
|
return !_filename.empty();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE void Filename::
|
||
|
output(std::ostream &out) const {
|
||
|
out << _filename;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Specifies the default encoding to be used for all subsequent Filenames.
|
||
|
* This is used to represent wide-character (Unicode) filenames internally.
|
||
|
* On non-Windows-based systems, the encoded filename is also passed to the
|
||
|
* underlying operating system.
|
||
|
*/
|
||
|
INLINE void Filename::
|
||
|
set_filesystem_encoding(TextEncoder::Encoding encoding) {
|
||
|
_filesystem_encoding = encoding;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Specifies the default encoding to be used for all subsequent Filenames
|
||
|
* objects. See set_filesystem_encoding().
|
||
|
*/
|
||
|
INLINE TextEncoder::Encoding Filename::
|
||
|
get_filesystem_encoding() {
|
||
|
return _filesystem_encoding;
|
||
|
}
|