diff --git a/cbp/Module.cbp b/cbp/Module.cbp index c3865803..afb62105 100644 --- a/cbp/Module.cbp +++ b/cbp/Module.cbp @@ -459,10 +459,10 @@ + + - - diff --git a/source/Base/Buffer.cpp b/source/Base/Buffer.cpp index 2698a74d..a68e57b4 100644 --- a/source/Base/Buffer.cpp +++ b/source/Base/Buffer.cpp @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------------------------ -#include "Buffer.hpp" +#include "Base/Buffer.hpp" // ------------------------------------------------------------------------------------------------ #include @@ -40,7 +40,9 @@ void ThrowMemExcept(const char * msg, ...) int ret = vsnprintf(buffer, sizeof(buffer), msg, args); // Check for formatting errors if (ret < 0) + { throw std::runtime_error("Unknown memory error"); + } // Throw the actual exception throw std::runtime_error(buffer); } @@ -54,7 +56,9 @@ static Buffer::Pointer AllocMem(Buffer::SzType size) Buffer::Pointer ptr = reinterpret_cast< Buffer::Pointer >(malloc(size)); // Validate the allocated memory if (!ptr) + { ThrowMemExcept("Unable to allocate (%u) bytes of memory", size); + } // Return the allocated memory return ptr; } @@ -100,7 +104,9 @@ private: * Base constructor. */ Node(Node * next) - : mCap(0), mPtr(nullptr), mNext(next) + : mCap(0) + , mPtr(nullptr) + , mNext(next) { /* ... */ } @@ -130,7 +136,9 @@ private: { // Free the memory (if any) if (node->mPtr) + { free(node->mPtr); + } // Save the next node next = node->mNext; // Release the node instance @@ -149,7 +157,9 @@ private: { // Free the memory (if any) if (node->mPtr) + { free(node->mPtr); + } // Save the next node next = node->mNext; // Release the node instance @@ -173,10 +183,14 @@ private: { // Was there a previous node? if (prev) + { prev->mNext = node->mNext; + } // Probably this was the head else + { m_Head = node->mNext; + } // Assign the memory ptr = node->mPtr; // Assign the size @@ -208,7 +222,9 @@ private: void Drop(Pointer & ptr, SzType & size) { if (!ptr) + { ThrowMemExcept("Cannot store invalid memory buffer"); + } // Request a node instance Node * node = Pull(); // Assign the specified memory @@ -232,7 +248,9 @@ private: s_Nodes = new Node(s_Nodes); // Validate the head node if (!s_Nodes) + { ThrowMemExcept("Unable to allocate memory nodes"); + } } } @@ -243,7 +261,9 @@ private: { // Are there any nodes available? if (!s_Nodes) + { Make(); // Make some! + } // Grab the head node Node * node = s_Nodes; // Promote the next node as the head @@ -259,7 +279,9 @@ private: { // See if the node is even valid if (!node) + { ThrowMemExcept("Attempting to push invalid node"); + } // Demote the current head node node->mNext = s_Nodes; // Promote as the head node @@ -324,7 +346,9 @@ MemRef MemRef::s_Mem; void MemRef::Grab() { if (m_Ptr) + { ++(*m_Ref); + } } // ------------------------------------------------------------------------------------------------ @@ -354,7 +378,8 @@ const MemRef & MemRef::Get() // ------------------------------------------------------------------------------------------------ Buffer::Buffer(const Buffer & o) : m_Ptr(nullptr) - , m_Cap(0) + , m_Cap(o.m_Cap) + , m_Cur(o.m_Cur) , m_Mem(o.m_Mem) { if (m_Cap) @@ -369,7 +394,9 @@ Buffer::~Buffer() { // Do we have a buffer? if (m_Ptr) + { Release(); // Release it! + } } // ------------------------------------------------------------------------------------------------ @@ -379,30 +406,51 @@ Buffer & Buffer::operator = (const Buffer & o) { // Can we work in the current buffer? if (m_Cap && o.m_Cap <= m_Cap) + { // It's safe to copy the data - memcpy(m_Ptr, o.m_Ptr, m_Cap); + memcpy(m_Ptr, o.m_Ptr, o.m_Cap); + } // Do we even have data to copy? else if (!o.m_Cap) { // Do we have a buffer? if (m_Ptr) + { Release(); // Release it! + } } else { // Do we have a buffer? if (m_Ptr) + { Release(); // Release it! + } // Request a larger buffer Request(o.m_Cap); // Now it's safe to copy the data memcpy(m_Ptr, o.m_Ptr, o.m_Cap); } + // Also copy the edit cursor + m_Cur = o.m_Cur; } return *this; } +// ------------------------------------------------------------------------------------------------ +void Buffer::Grow(SzType n) +{ + // Backup the current memory + Buffer bkp(m_Ptr, m_Cap, m_Cur, m_Mem); + // Acquire a bigger buffer + Request(bkp.m_Cap + n); + // Copy the data from the old buffer + memcpy(m_Ptr, bkp.m_Ptr, bkp.m_Cap); + // Copy the previous edit cursor + m_Cur = bkp.m_Cur; +} + // ------------------------------------------------------------------------------------------------ void Buffer::Request(SzType n) { @@ -417,11 +465,17 @@ void Buffer::Request(SzType n) } // Find out in which category does this buffer reside else if (n <= 1024) + { m_Mem->m_Small.Grab(m_Ptr, n); + } else if (n <= 4096) + { m_Mem->m_Medium.Grab(m_Ptr, n); + } else + { m_Mem->m_Large.Grab(m_Ptr, n); + } // If no errors occurred then we can set the size m_Cap = n; } @@ -432,32 +486,41 @@ void Buffer::Release() // TODO: Implement a limit on how much memory can actually be pooled. // Is there a memory manager available? if (!m_Mem) + { free(m_Ptr); // Deallocate the memory directly + } // Find out to which category does this buffer belong else if (m_Cap <= 1024) + { m_Mem->m_Small.Drop(m_Ptr, m_Cap); + } else if (m_Cap <= 4096) + { m_Mem->m_Medium.Drop(m_Ptr, m_Cap); + } else + { m_Mem->m_Large.Drop(m_Ptr, m_Cap); + } // Explicitly reset the buffer m_Ptr = nullptr; m_Cap = 0; + m_Cur = 0; } // ------------------------------------------------------------------------------------------------ Buffer::SzType Buffer::Write(SzType pos, ConstPtr data, SzType size) { - // Make sure the position is not out of bounds - if (pos > m_Cap || !data || !size) + // Do we have what to write? + if (!data || !size) + { return 0; + } // See if the buffer size must be adjusted else if ((pos + size) >= m_Cap) { - // Allocate a larger memory chunk and backup old data - Buffer bkp(Adjust< Value >(NextPow2(pos + size))); - // Copy data back from the old buffer - memcpy(m_Ptr, bkp.m_Ptr, bkp.m_Cap); + // Acquire a larger buffer + Grow((pos + size) - m_Cap + 32); } // Copy the data into the internal buffer memcpy(m_Ptr + pos, data, size); @@ -472,7 +535,7 @@ Buffer::SzType Buffer::WriteF(SzType pos, const char * fmt, ...) va_list args; va_start(args, fmt); // Call the function that takes the variable argument list - SzType ret = WriteF(pos, fmt, args); + const SzType ret = WriteF(pos, fmt, args); // Finalize the variable argument list va_end(args); // Return the result @@ -482,9 +545,12 @@ Buffer::SzType Buffer::WriteF(SzType pos, const char * fmt, ...) // ------------------------------------------------------------------------------------------------ Buffer::SzType Buffer::WriteF(SzType pos, const char * fmt, va_list args) { - // Make sure the position is not out of bounds - if (pos > m_Cap) - return 0; + // Is the specified position within range? + if (pos >= m_Cap) + { + // Acquire a larger buffer + Grow(pos - m_Cap + 32); + } // Backup the variable argument list va_list args_cpy; va_copy(args_cpy, args); @@ -494,18 +560,53 @@ Buffer::SzType Buffer::WriteF(SzType pos, const char * fmt, va_list args) // Do we need a bigger buffer? if ((pos + ret) >= m_Cap) { - // Allocate a larger memory chunk and backup old data - Buffer bkp(Adjust< Value >(NextPow2(pos + ret))); - // Copy data back from the old buffer - memcpy(m_Ptr, bkp.m_Ptr, bkp.m_Cap); + // Acquire a larger buffer + Grow((pos + ret) - m_Cap + 32); // Retry writing the requested information ret = vsnprintf(m_Ptr + pos, m_Cap, fmt, args_cpy); } // Return the value 0 if data could not be written if (ret < 0) + { return 0; + } // Return the number of written characters return static_cast< SzType >(ret); } +// ------------------------------------------------------------------------------------------------ +Buffer::SzType Buffer::WriteS(SzType pos, ConstPtr str) +{ + // Is there any string to write? + if (str && *str != '\0') + { + // Forward this to the regular write function + return Write(pos, str, strlen(str)); + } + // Nothing to write + return 0; +} + +// ------------------------------------------------------------------------------------------------ +void Buffer::AppendF(const char * fmt, ...) +{ + // Initialize the variable argument list + va_list args; + va_start(args, fmt); + // Forward this to the regular write function + m_Cur += WriteF(m_Cur, fmt, args); + // Finalize the variable argument list + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Buffer::AppendS(const char * str) +{ + // Is there any string to write? + if (str) + { + m_Cur += Write(m_Cur, str, strlen(str)); + } +} + } // Namespace:: SqMod diff --git a/source/Base/Buffer.hpp b/source/Base/Buffer.hpp index f719f93e..8b319838 100644 --- a/source/Base/Buffer.hpp +++ b/source/Base/Buffer.hpp @@ -52,7 +52,8 @@ public: * Default constructor (null). */ MemRef() - : m_Ptr(s_Mem.m_Ptr), m_Ref(s_Mem.m_Ref) + : m_Ptr(s_Mem.m_Ptr) + , m_Ref(s_Mem.m_Ref) { Grab(); } @@ -61,7 +62,8 @@ public: * Copy constructor. */ MemRef(const MemRef & o) - : m_Ptr(o.m_Ptr), m_Ref(o.m_Ref) + : m_Ptr(o.m_Ptr) + , m_Ref(o.m_Ref) { Grab(); @@ -189,13 +191,15 @@ private: /* -------------------------------------------------------------------------------------------- * Construct and take ownership of the specified buffer. */ - Buffer(Pointer & ptr, SzType & cap, const MemRef & mem) + Buffer(Pointer & ptr, SzType & cap, SzType & cur, const MemRef & mem) : m_Ptr(ptr) , m_Cap(cap) + , m_Cur(cur) , m_Mem(mem) { ptr = nullptr; cap = 0; + cur = 0; } public: @@ -206,6 +210,7 @@ public: Buffer() : m_Ptr(nullptr) , m_Cap(0) + , m_Cur(0) , m_Mem(MemRef::Get()) { /* ... */ @@ -217,6 +222,7 @@ public: Buffer(SzType n) : m_Ptr(nullptr) , m_Cap(0) + , m_Cur(0) , m_Mem(MemRef::Get()) { Request(n < 8 ? 8 : n); @@ -231,7 +237,10 @@ public: * Move constructor. */ Buffer(Buffer && o) - : m_Ptr(o.m_Ptr), m_Cap(o.m_Cap), m_Mem(o.m_Mem) + : m_Ptr(o.m_Ptr) + , m_Cap(o.m_Cap) + , m_Cur(o.m_Cur) + , m_Mem(o.m_Mem) { o.m_Ptr = nullptr; } @@ -254,9 +263,12 @@ public: if (m_Ptr != o.m_Ptr) { if (m_Ptr) + { Release(); + } m_Ptr = o.m_Ptr; m_Cap = o.m_Cap; + m_Cur = o.m_Cur; m_Mem = o.m_Mem; o.m_Ptr = nullptr; } @@ -319,72 +331,6 @@ public: return m_Ptr; } - /* -------------------------------------------------------------------------------------------- - * Retrieve the internal buffer casted as a different type. - */ - template < typename T = Value> T * Get() - { - return reinterpret_cast< T * >(m_Ptr); - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the internal buffer casted as a different type. - */ - template < typename T = Value> const T * Get() const - { - return reinterpret_cast< const T * >(m_Ptr); - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the a certain element. - */ - template < typename T = Value> T & At(SzType n) - { - assert(n < m_Cap); - return reinterpret_cast< T * >(m_Ptr)[n]; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the a certain element. - */ - template < typename T = Value> const T & At(SzType n) const - { - assert(n < m_Cap); - return reinterpret_cast< const T * >(m_Ptr)[n]; - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the internal buffer casted as a different type. - */ - template < typename T = Value> T * Begin() - { - return reinterpret_cast< T * >(m_Ptr); - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the internal buffer casted as a different type. - */ - template < typename T = Value> const T * Begin() const - { - return reinterpret_cast< const T * >(m_Ptr); - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the internal buffer casted as a different type. - */ - template < typename T = Value> T * End() - { - return reinterpret_cast< T * >(m_Ptr) + (m_Cap / sizeof(T)); - } - - /* -------------------------------------------------------------------------------------------- - * Retrieve the internal buffer casted as a different type. - */ - template < typename T = Value> const T * End() const - { - return reinterpret_cast< const T * >(m_Ptr) + (m_Cap / sizeof(T)); - } - /* -------------------------------------------------------------------------------------------- * Retrieve the internal buffer. */ @@ -401,20 +347,273 @@ public: return m_Ptr; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the internal buffer casted as a different type. + */ + template < typename T = Value > T * Get() + { + return reinterpret_cast< T * >(m_Ptr); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the internal buffer casted as a different type. + */ + template < typename T = Value > const T * Get() const + { + return reinterpret_cast< const T * >(m_Ptr); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the a certain element. + */ + template < typename T = Value > T & At(SzType n) + { + assert(n < m_Cap); + return reinterpret_cast< T * >(m_Ptr)[n]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the a certain element. + */ + template < typename T = Value > const T & At(SzType n) const + { + assert(n < m_Cap); + return reinterpret_cast< const T * >(m_Ptr)[n]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the internal buffer casted as a different type. + */ + template < typename T = Value > T * Begin() + { + return reinterpret_cast< T * >(m_Ptr); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the internal buffer casted as a different type. + */ + template < typename T = Value > const T * Begin() const + { + return reinterpret_cast< const T * >(m_Ptr); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the internal buffer casted as a different type. + */ + template < typename T = Value > T * End() + { + return reinterpret_cast< T * >(m_Ptr) + (m_Cap / sizeof(T)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the internal buffer casted as a different type. + */ + template < typename T = Value > const T * End() const + { + return reinterpret_cast< const T * >(m_Ptr) + (m_Cap / sizeof(T)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element at the front of the buffer. + */ + template < typename T = Value > T & Front() + { + assert(m_Cap >= sizeof(T)); + return reinterpret_cast< T * >(m_Ptr)[0]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element at the front of the buffer. + */ + template < typename T = Value > const T & Front() const + { + assert(m_Cap >= sizeof(T)); + return reinterpret_cast< const T * >(m_Ptr)[0]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element after the first element in the buffer. + */ + template < typename T = Value > T & Next() + { + assert(m_Cap >= (sizeof(T) * 2)); + return reinterpret_cast< T * >(m_Ptr)[1]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element after the first element in the buffer. + */ + template < typename T = Value > const T & Next() const + { + assert(m_Cap >= (sizeof(T) * 2)); + return reinterpret_cast< const T * >(m_Ptr)[1]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element at the back of the buffer. + */ + template < typename T = Value > T & Back() + { + assert(m_Cap >= sizeof(T)); + return reinterpret_cast< T * >(m_Ptr)[m_Cap-1]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element at the back of the buffer. + */ + template < typename T = Value > const T & Back() const + { + assert(m_Cap >= sizeof(T)); + return reinterpret_cast< const T * >(m_Ptr)[m_Cap-1]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element before the last element in the buffer. + */ + template < typename T = Value > T & Prev() + { + assert(m_Cap >= (sizeof(T) * 2)); + return reinterpret_cast< T * >(m_Ptr)[m_Cap-2]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element before the last element in the buffer. + */ + template < typename T = Value > const T & Prev() const + { + assert(m_Cap >= (sizeof(T) * 2)); + return reinterpret_cast< const T * >(m_Ptr)[m_Cap-2]; + } + + /* -------------------------------------------------------------------------------------------- + * Reposition the edit cursor to the specified number of elements ahead. + */ + template < typename T = Value > void Advance(SzType n) + { + // Do we need to scale the buffer? + if ((m_Cur + (n * sizeof(T))) >= m_Cap) + { + Grow(m_Cur + (n * sizeof(T))); + } + // Advance to the specified position + m_Cur += (n * sizeof(T)); + } + + /* -------------------------------------------------------------------------------------------- + * Reposition the edit cursor to the specified number of elements behind. + */ + template < typename T = Value > void Retreat(SzType n) + { + // Can we move that much backward? + if ((n * sizeof(T)) <= m_Cur) + { + m_Cur -= (n * sizeof(T)); + } + // Just got to the beginning + else + { + m_Cur = 0; + } + } + + /* -------------------------------------------------------------------------------------------- + * Reposition the edit cursor to a fixed position within the buffer. + */ + template < typename T = Value > void Move(SzType n) + { + // Do we need to scale the buffer? + if ((n * sizeof(T)) >= m_Cap) + { + Grow(n * sizeof(T)); + } + // Move to the specified position + m_Cur = (n * sizeof(T)); + } + + /* -------------------------------------------------------------------------------------------- + * Reposition the edit cursor to a fixed position within the buffer. + */ + template < typename T = Value > void Push(T v) + { + // Do we need to scale the buffer? + if ((m_Cur + sizeof(T)) >= m_Cap) + { + Grow(m_Cap + sizeof(T)); + } + // Assign the specified value + reinterpret_cast< T * >(m_Ptr)[m_Cur] = v; + // Move to the next element + m_Cur += sizeof(T); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element at the cursor position. + */ + template < typename T = Value > T & Cursor() + { + assert((m_Cur / sizeof(T)) < (m_Cap / sizeof(T))); + return reinterpret_cast< T * >(m_Ptr)[m_Cur]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element at the cursor position. + */ + template < typename T = Value > const T & Cursor() const + { + assert((m_Cur / sizeof(T)) < (m_Cap / sizeof(T))); + return reinterpret_cast< const T * >(m_Ptr)[m_Cur]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element before the cursor position. + */ + template < typename T = Value > T & Before() + { + assert(m_Cur >= sizeof(T)); + return reinterpret_cast< T * >(m_Ptr)[m_Cur-1]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element before the cursor position. + */ + template < typename T = Value > const T & Before() const + { + assert(m_Cur >= sizeof(T)); + return reinterpret_cast< const T * >(m_Ptr)[m_Cur-1]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element after the cursor position. + */ + template < typename T = Value > T & After() + { + assert((m_Cur + sizeof(T)) <= (m_Cap - sizeof(T))); + return reinterpret_cast< T * >(m_Ptr)[m_Cur+1]; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the element after the cursor position. + */ + template < typename T = Value > const T & After() const + { + assert((m_Cur + sizeof(T)) <= (m_Cap - sizeof(T))); + return reinterpret_cast< const T * >(m_Ptr)[m_Cur+1]; + } + /* -------------------------------------------------------------------------------------------- * Retrieve maximum elements it can hold for a certain type. */ - template < typename T = Value> static SzType Max() + template < typename T = Value > static SzType Max() { - return (0xFFFFFFFF / sizeof(T)); + return static_cast< SzType >(0xFFFFFFFF / sizeof(T)); } /* -------------------------------------------------------------------------------------------- * Retrieve the current buffer capacity in element count. */ - template < typename T = Value> SzType Size() const + template < typename T = Value > SzType Size() const { - return (m_Cap / sizeof(T)); + return static_cast< SzType >(m_Cap / sizeof(T)); } /* -------------------------------------------------------------------------------------------- @@ -425,25 +624,52 @@ public: return m_Cap; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the current position of the cursor in the buffer. + */ + template < typename T = Value > SzType Position() const + { + return static_cast< SzType >(m_Cur / sizeof(T)); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the amount of unused buffer after the edit cursor. + */ + template < typename T = Value > SzType Remaining() const + { + return static_cast< SzType >((m_Cap - m_Cur) / sizeof(T)); + } + + /* -------------------------------------------------------------------------------------------- + * Grow the size of the internal buffer by the specified amount of bytes. + */ + void Grow(SzType n); + /* -------------------------------------------------------------------------------------------- * Makes sure there is enough capacity to hold the specified element count. */ - template < typename T = Value> Buffer Adjust(SzType n) + template < typename T = Value > Buffer Adjust(SzType n) { // Do we meet the minimum size? if (n < 8) + { n = 8; // Adjust to minimum size + } // See if the requested capacity doesn't exceed the limit if (n > Max< T >()) + { ThrowMemExcept("Requested buffer of (%u) elements exceeds the (%u) limit", n, Max< T >()); + } // Is there an existing buffer? else if (n && !m_Cap) + { Request(n * sizeof(T)); // Request the memory + } // Should the size be increased? else if (n > m_Cap) { // Backup the current memory - Buffer bkp(m_Ptr, m_Cap, m_Mem); + Buffer bkp(m_Ptr, m_Cap, m_Cur, m_Mem); // Request the memory Request(n * sizeof(T)); // Return the backup @@ -459,7 +685,9 @@ public: void Reset() { if (m_Ptr) + { Release(); + } } /* -------------------------------------------------------------------------------------------- @@ -476,10 +704,18 @@ public: } /* -------------------------------------------------------------------------------------------- - * Write a portion of a buffet to the internal buffer. + * Write a portion of a buffer to the internal buffer. */ SzType Write(SzType pos, ConstPtr data, SzType size); + /* -------------------------------------------------------------------------------------------- + * Write another buffer to the internal buffer. + */ + SzType Write(SzType pos, const Buffer & b) + { + return Write(pos, b.m_Ptr, b.m_Cur); + } + /* -------------------------------------------------------------------------------------------- * Write a formatted string to the internal buffer. */ @@ -490,6 +726,61 @@ public: */ SzType WriteF(SzType pos, const char * fmt, va_list args); + /* -------------------------------------------------------------------------------------------- + * Write a string to the internal buffer. + */ + SzType WriteS(SzType pos, const char * str); + + /* -------------------------------------------------------------------------------------------- + * Write a portion of a string to the internal buffer. + */ + SzType WriteS(SzType pos, const char * str, SzType size) + { + return Write(pos, str, size); + } + + /* -------------------------------------------------------------------------------------------- + * Append a portion of a buffer to the internal buffer. + */ + void Append(ConstPtr data, SzType size) + { + m_Cur += Write(m_Cur, data, size); + } + + /* -------------------------------------------------------------------------------------------- + * Append another buffer to the internal buffer. + */ + void Append(const Buffer & b) + { + m_Cur += Write(m_Cur, b.m_Ptr, b.m_Cur); + } + + /* -------------------------------------------------------------------------------------------- + * Append a formatted string to the internal buffer. + */ + void AppendF(const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Append a formatted string to the internal buffer. + */ + void AppendF(const char * fmt, va_list args) + { + m_Cur += WriteF(m_Cur, fmt, args); + } + + /* -------------------------------------------------------------------------------------------- + * Append a string to the internal buffer. + */ + void AppendS(const char * str); + + /* -------------------------------------------------------------------------------------------- + * Append a portion of a string to the internal buffer. + */ + void AppendS(const char * str, SzType size) + { + m_Cur += Write(m_Cur, str, size); + } + protected: /* -------------------------------------------------------------------------------------------- @@ -507,9 +798,10 @@ private: // -------------------------------------------------------------------------------------------- Pointer m_Ptr; /* Pointer to the memory buffer. */ SzType m_Cap; /* The total size of the buffer. */ + SzType m_Cur; /* The buffer edit cursor. */ // -------------------------------------------------------------------------------------------- - MemRef m_Mem; + MemRef m_Mem; /* Reference to the associated memory manager. */ }; } // Namespace:: SqMod diff --git a/source/Library/Crypt.cpp b/source/Library/Crypt.cpp index 78942f41..8bdec5c1 100644 --- a/source/Library/Crypt.cpp +++ b/source/Library/Crypt.cpp @@ -209,12 +209,12 @@ void Register_Crypt(HSQUIRRELVM vm) RegisterWrapper< SHA256 >(hashns, _SC("SHA256")); RegisterWrapper< SHA3 >(hashns, _SC("SHA3")); - hashns.SquirrelFunc(_SC("GetCRC32"), &HashF< CRC32 >); - hashns.SquirrelFunc(_SC("GetKeccak"), &HashF< Keccak >); - hashns.SquirrelFunc(_SC("GetMD5"), &HashF< MD5 >); - hashns.SquirrelFunc(_SC("GetSHA1"), &HashF< SHA1 >); - hashns.SquirrelFunc(_SC("GetSHA256"), &HashF< SHA256 >); - hashns.SquirrelFunc(_SC("GetSHA3"), &HashF< SHA3 >); + hashns.SquirrelFunc(_SC("SqCRC32"), &HashF< CRC32 >); + hashns.SquirrelFunc(_SC("SqKeccak"), &HashF< Keccak >); + hashns.SquirrelFunc(_SC("SqMD5"), &HashF< MD5 >); + hashns.SquirrelFunc(_SC("SqSHA1"), &HashF< SHA1 >); + hashns.SquirrelFunc(_SC("SqSHA256"), &HashF< SHA256 >); + hashns.SquirrelFunc(_SC("SqSHA3"), &HashF< SHA3 >); RootTable(vm).Bind(_SC("SqHash"), hashns); diff --git a/source/Library/SysEnv.cpp b/source/Library/SysEnv.cpp new file mode 100644 index 00000000..95c15415 --- /dev/null +++ b/source/Library/SysEnv.cpp @@ -0,0 +1,1218 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/SysEnv.hpp" + +// ------------------------------------------------------------------------------------------------ +#include +#include +#include +#include + +// ------------------------------------------------------------------------------------------------ +#ifdef SQMOD_OS_WINDOWS + #include + #include +#else + #include + #include + #include +#endif + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +#ifdef SQMOD_OS_WINDOWS + // Maximum path size in characters + #define SQMOD_MAX_PATH (sizeof(TCHAR) * MAX_PATH) + // Character to be used when working with path + typedef TCHAR PChar; +#else + // Maximum path size in characters + #define SQMOD_MAX_PATH (PATH_MAX) + // Character to be used when working with path + typedef CharT PChar; +#endif // SQMOD_OS_WINDOWS + +// ------------------------------------------------------------------------------------------------ +void SysEnv::Get(Buffer & b, CCStr name, CCStr fallback) +{ + // Make sure the requested variable name is valid + if (name && *name != 0) + { + // Is there a buffer to work with? + if (!b) + { + // Acquire a moderately sized buffer + b = Buffer(128); + } +#ifdef SQMOD_OS_WINDOWS + // Retrieve the variable contents into the buffer that we have + DWORD len = GetEnvironmentVariableA(name, &b.Cursor(), b.Remaining()); + // If the returned length is 0 then the variable doesn't exist + if (!len) + { + // Write the fall-back value into the buffer instead + len = b.WriteS(b.Position(), fallback); + } + // Did we have enough space left in the buffer? + else if (len > b.Remaining()) + { + // Acquire a new buffer with a more appropriate capacity this time + b.Grow(len - b.Remaining() + 2); + // Attempt to retrieve the variable contents one more time + len = GetEnvironmentVariableA(name, &b.Cursor(), b.Remaining()); + } + // Move the edit cursor to the end of the appended data + b.Advance(len); +#else + // Retrieve the pointer to the variable contents + CSStr val = getenv(name); + // If the returned pointer is null then the variable doesn't exist + if (!val) + { + // Write the fall-back value into the buffer instead + Buffer.AppendS(fallback); + } + else + { + // Write the variable contents to the buffer + b.AppendS(val); + } +#endif + } + // Make sure that whatever string is in the buffer is null terminated + b.Cursor() = '\0'; +} + +// ------------------------------------------------------------------------------------------------ +bool SysEnv::Has(CCStr name) +{ +#ifdef SQMOD_OS_WINDOWS + return (GetEnvironmentVariableA(name, nullptr, 0) > 0); +#else + return (getenv(name) != 0); +#endif +} + +// ------------------------------------------------------------------------------------------------ +bool SysEnv::Has(const String & name) +{ +#ifdef SQMOD_OS_WINDOWS + return (GetEnvironmentVariableA(name.c_str(), nullptr, 0) > 0); +#else + return (getenv(name.c_str()) != 0); +#endif +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::Get(CCStr name, CCStr fallback) +{ + // Allocate a moderately sized buffer + Buffer b(128); + // Forward the call to the shared function + Get(b, name, fallback); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +bool SysEnv::Set(CCStr name, CCStr value) +{ +#ifdef SQMOD_OS_WINDOWS + // Set the specified environment variable and return the result + return (SetEnvironmentVariableA(name, value) != 0); +#else + // Allocated a moderately sized buffer + Buffer b(256); + // Generate the necessary set command + b.WriteF(0, "%s=%s", name, value); + // Set the resulted environment variable and return the result + return (putenv(b.Data()) == 0); +#endif +} + +// ------------------------------------------------------------------------------------------------ +bool SysEnv::Set(const String & name, const String & value) +{ +#ifdef SQMOD_OS_WINDOWS + // Set the specified environment variable and return the result + return (SetEnvironmentVariableA(name.c_str(), value.c_str()) != 0); +#else + // Obtain a temporary buffer capable of holding the set command + Buffer b(name.size() + value.size() + 2); + // Generate the necessary set command + b.WriteF(0, "%s=%s", name.c_str(), value.c_str()); + // Set the resulted environment variable and return the result + return (putenv(b.Data()) == 0); +#endif +} + +// ------------------------------------------------------------------------------------------------ +String SysEnv::OSName() +{ +#ifdef SQMOD_OS_WINDOWS + // Prepare the structure in which the OS information is retrieved + OSVERSIONINFO vi; + // Specify the size of the structure + vi.dwOSVersionInfoSize = sizeof(vi); + // Attempt to populate the previously created structure with information + if (GetVersionEx(&vi) == 0) + { + return "Unknown Windows"; + } + // Identify the platform from the obtained information + switch (vi.dwPlatformId) + { + case VER_PLATFORM_WIN32s: + return "Windows 3.x"; + case VER_PLATFORM_WIN32_WINDOWS: + return vi.dwMinorVersion == 0 ? "Windows 95" : "Windows 98"; + case VER_PLATFORM_WIN32_NT: + return "Windows NT"; + default: + return "Windows [Unknown]"; + } +#else + // Prepare the structure in which the OS information is retrieved + struct utsname uts; + // Attempt to populate the previously created structure with information + if (uname(&uts) < 0) + { + return String("Unknown Unix"); + } + // Return the requested information + return uts.sysname; +#endif +} + +// ------------------------------------------------------------------------------------------------ +String SysEnv::OSDisplayName() +{ +#ifdef SQMOD_OS_WINDOWS + // Prepare the structure in which the OS information is retrieved + OSVERSIONINFO vi; + // Specify the size of the structure + vi.dwOSVersionInfoSize = sizeof(vi); + // Attempt to populate the previously created structure with information + if (GetVersionEx(&vi) == 0) + { + return "Unknown Windows"; + } + // Identify the platform from the obtained information + switch(vi.dwMajorVersion) + { + case 6: + switch (vi.dwMinorVersion) + { + case 0: return "Windows Vista/Server 2008"; + case 1: return "Windows 7/Server 2008 R2"; + case 2: return "Windows 8/Server 2012"; + default: return "Windows 6.x [Unknown]"; + } + case 5: + switch (vi.dwMinorVersion) + { + case 0: return "Windows 2000"; + case 1: return "Windows XP"; + case 2: return "Windows Server 2003/Windows Server 2003 R2"; + default: return "Windows 5.x [Unknown]"; + } + case 4: + switch (vi.dwMinorVersion) + { + case 0: return "Windows 95/Windows NT 4.0"; + case 10: return "Windows 98"; + case 90: return "Windows ME"; + default: return "Windows 4.x [Unknown]"; + } + default: return "Windows [Unknown]"; + } +#else + // Use the same same output from OSName + return OSName(); +#endif +} + +// ------------------------------------------------------------------------------------------------ +String SysEnv::OSVersion() +{ +#ifdef SQMOD_OS_WINDOWS + // Prepare the structure in which the OS information is retrieved + OSVERSIONINFO vi; + // Specify the size of the structure + vi.dwOSVersionInfoSize = sizeof(vi); + // Attempt to populate the previously created structure with information + if (GetVersionEx(&vi) == 0) + { + String("Unknown"); + } + // Obtain a temporary buffer capable of holding the version string + Buffer b(128); + // The amount of data written to the buffer + Uint32 sz = 0; + // Generate the version string with the received information + if (vi.szCSDVersion[0]) + { + sz = b.WriteF(0, "%lu.%lu (Build %lu : %s)", + vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, vi.szCSDVersion); + } + else + { + sz = b.WriteF(0, "%lu.%lu (Build %lu)", vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber); + } + // Return a string with the buffer contents and leave the buffer clean after itself + return String(b.Get< String::value_type >(), sz); +#else + // Prepare the structure in which the OS information is retrieved + struct utsname uts; + // Attempt to populate the previously created structure with information + if (uname(&uts) < 0) + { + return String("Unknown"); + } + // Return the requested information + return uts.release; +#endif +} + +// ------------------------------------------------------------------------------------------------ +String SysEnv::OSArchitecture() +{ +#ifdef SQMOD_OS_WINDOWS + // Prepare the structure in which the system information is retrieved + SYSTEM_INFO si; + // Attempt to populate the previously created structure with information + GetSystemInfo(&si); + // Identify the architecture from the obtained information + switch (si.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_INTEL: return "IA32"; + case PROCESSOR_ARCHITECTURE_MIPS: return "MIPS"; + case PROCESSOR_ARCHITECTURE_ALPHA: return "ALPHA"; + case PROCESSOR_ARCHITECTURE_PPC: return "PPC"; + case PROCESSOR_ARCHITECTURE_IA64: return "IA64"; +#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "IA64/32"; +#endif +#ifdef PROCESSOR_ARCHITECTURE_AMD64 + case PROCESSOR_ARCHITECTURE_AMD64: return "AMD64"; +#endif + default: return "Unknown"; + } +#else + // Prepare the structure in which the OS information is retrieved + struct utsname uts; + // Attempt to populate the previously created structure with information + if (uname(&uts) < 0) + { + return String("Unknown"); + } + // Return the requested information + return uts.machine; +#endif +} + +// ------------------------------------------------------------------------------------------------ +String SysEnv::NodeName() +{ +#ifdef SQMOD_OS_WINDOWS + // Obtain a temporary buffer capable of holding the node name string + Buffer b(MAX_COMPUTERNAME_LENGTH + 1); + // Used to tell the size of our buffer and the size of data written to it + DWORD size = b.Size< TCHAR >(); + // Attempt to obtain the requested information + if (GetComputerNameA(b.Data(), &size) == 0) + { + return String(); + } + // Return a string with the buffer contents and leave the buffer clean after itself + return String(b.Get< String::value_type >(), size); +#else + // Prepare the structure in which the OS information is retrieved + struct utsname uts; + // Attempt to populate the previously created structure with information + if (uname(&uts) < 0) + { + return String("Unknown"); + } + // Return the requested information + return uts.nodename; +#endif +} + +// ------------------------------------------------------------------------------------------------ +Uint32 SysEnv::ProcessorCount() +{ +#ifdef SQMOD_OS_WINDOWS + // Prepare the structure in which the system information is retrieved + SYSTEM_INFO si; + // Attempt to populate the previously created structure with information + GetSystemInfo(&si); + // Return the requested information + return si.dwNumberOfProcessors; +#elif defined(_SC_NPROCESSORS_ONLN) + // Attempt to obtain the number of processors available on the system + const Int32 count = sysconf(_SC_NPROCESSORS_ONLN); + // Validate the result and return the appropriate value + return (count < 0) ? 1 : static_cast< Uint32 >(count); +#else + // Obviously at least one processor should be available + return 1; +#endif +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::TerminatePath(Buffer & b) +{ + // Is there any path to terminate? + if (!b) + { + return; + } + // Make sure that the path contains a trailing slash if necessary + else if (b.Cursor() == 0 && b.Before() != SQMOD_DIRSEP_CHAR) + { + b.Push(SQMOD_DIRSEP_CHAR); + } + // Make sure that whatever string is in the buffer, if any, is null terminated + b.Cursor() = '\0'; +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::ExpandVars(Buffer & b, CCStr pos, CCStr end) +{ + // Let's have a string to store the extracted variable name and value + String var; + // Extract the remaining directories from the specified path + while (pos != end) + { + // Should we start looking for a variable name? + if (*pos == '$') + { + // Clear previous name, if any + var.clear(); + // Where the name of the variable starts and where it ends + CCStr start = ++pos, stop = pos; + // Is this variable name enclosed within curly braces? + if (*start == '{') + { + // Find the closing brace + stop = strchr(start, '}'); + // Was there a closing brace? + if (!stop) + { + // Append the rest of the string to the buffer + b.AppendS(pos - 1, end - pos + 1); + // Stop parsing here + break; + } + // Is there anything between the brace? + else if ((stop - start) >= 1) + { + // Slice the variable name + var.assign(start + 1, stop - start - 1); + // Skip the ending brace + ++stop; + } + } + // Is the dollar character followed by a character allowed in variable names? + else if (isalnum(*start) != 0 || *start == '_') + { + // Find the first character that isn't allowed in variable names + while (stop != end && (isalnum(*stop) != 0 || *stop == '_')) + { + ++stop; + } + // Have we found anything? + if (start != stop) + { + // Slice the variable name + var.assign(start, stop - start); + } + } + else + { + // Just add the character to the buffer as is + b.Push('$'); + // Skip to the next character + continue; + } + // Update the position + pos = stop; + // Do we have a valid variable name and does it exist? + if (!var.empty() && Has(var)) + { + // Append the variable contents to our buffer + Get(b, var.c_str(), nullptr); + } + } + // Just add the character to the buffer as is + else + { + b.Push(*(pos++)); + } + } + // Make sure the string in the buffer is null terminated + b.Cursor() = '\0'; +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::ExpandPath(Buffer & b, CCStr pos, CCStr end) +{ + // Does the path even contain something to be expanded? + if (pos == end || *pos == '\0') + { + return; // Nothing to expand! + } + // If the path starts with the tilde character then the home directory was requested + else if (*pos == '~') + { + // To be expanded, the tilde character must be followed by a slash + if (*(++pos) == SQMOD_DIRSEP_CHAR) + { + // Let's expand this tilde to the home directory + HomeDir(b); + // Let's skip the slash as well + ++pos; + } + // Go back to the previous character and use it literally + else + { + --pos; + } + } + // The remaining string can be expanded normally + ExpandVars(b, pos, end); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::ExpandVars(Buffer & b, CCStr str) +{ + // Do we have anything to expand? + if (!str || *str == '\0') + { + // Make sure the string in the specified buffer, if any, is null terminated + if (b) + { + b.Cursor() = '\0'; + } + // Nothing to expand! + return; + } + // Calculate the size of the specified string + const Uint32 len = strlen(str); + // Forward the call to the internal function + ExpandVars(b, str, str + len); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::ExpandVars(Buffer & b, const String & str) +{ + // Do we have anything to expand? + if (str.empty()) + { + // Make sure the string in the specified buffer, if any, is null terminated + if (b) + { + b.Cursor() = '\0'; + } + // Nothing to expand! + return; + } + // Forward the call to the internal function + ExpandVars(b, str.c_str(), str.c_str() + str.size()); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::ExpandVars(CCStr str) +{ + // Do we have anything to expand? + if (!str || *str == '\0') + { + return Buffer(); // Nothing to expand! + } + // Calculate the size of the specified string + const Uint32 len = strlen(str); + // Allocate a moderately sized buffer + Buffer b(len + 128); + // Forward the call to the internal function + ExpandVars(b, str, str + len); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::ExpandVars(const String & str) +{ + // Do we have anything to expand? + if (str.empty()) + { + return Buffer(); // Nothing to expand! + } + // Allocate a moderately sized buffer + Buffer b(str.size() + 128); + // Forward the call to the internal function + ExpandVars(b, str.c_str(), str.c_str() + str.size()); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::ExpandPath(Buffer & b, CCStr path) +{ + // Do we have anything to expand? + if (!path || *path == '\0') + { + // Make sure the string in the specified buffer, if any, is null terminated + if (b) + { + b.Cursor() = '\0'; + } + // Nothing to expand! + return; + } + // Calculate the size of the specified string + const Uint32 len = strlen(path); + // Forward the call to the internal function + ExpandPath(b, path, path + len); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::ExpandPath(Buffer & b, const String & path) +{ + // Do we have anything to expand? + if (path.empty()) + { + // Make sure the string in the specified buffer, if any, is null terminated + if (b) + { + b.Cursor() = '\0'; + } + // Nothing to expand! + return; + } + // Forward the call to the internal function + ExpandPath(b, path.c_str(), path.c_str() + path.size()); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::ExpandPath(CCStr path) +{ + // Do we have anything to expand? + if (!path || *path == '\0') + { + return Buffer(); // Nothing to expand! + } + // Calculate the size of the specified string + const Uint32 len = strlen(path); + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the internal function + ExpandPath(b, path, path + len); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::ExpandPath(const String & path) +{ + // Do we have anything to expand? + if (path.empty()) + { + return Buffer(); // Nothing to expand! + } + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the internal function + ExpandPath(b, path.c_str(), path.c_str() + path.size()); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::WorkingDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Is there a buffer to work with? + if (!b) + { + // Allocate buffer capable of storing a full path + b = Buffer(SQMOD_MAX_PATH); + } + // Retrieve the current directory for the current process + DWORD len = GetCurrentDirectoryA(b.Remaining(), &b.Cursor()); + // Did we have enough space left in the buffer? + if (len > b.Remaining()) + { + // Acquire a new buffer with a more appropriate capacity this time + b.Grow(len - b.Remaining() + 2); + // Attempt to retrieve the working directory one more time + len = GetCurrentDirectoryA(b.Remaining(), &b.Cursor()); + // ^ On failure the null terminator is included in the length + } + // Move the edit cursor to the end of the appended data + b.Advance(len); +#else + // Do we have enough space to store a full path? + if (b.Remaining() < SQMOD_MAX_PATH) + { + b.Grow(SQMOD_MAX_PATH - b.Remaining() + 2); + } + // Attempt to retrieve the current working directory and validate result + if (getcwd(&b.Cursor(), b.Remaining())) + { + // Move the edit cursor to the end of the appended data + b.Advance(strlen(&b.Cursor())); + } +#endif // SQMOD_OS_WINDOWS + // Make sure that the path is properly terminated + TerminatePath(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::WorkingDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + WorkingDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::HomeDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Do we have enough space to store a full path? + if (b.Remaining() < SQMOD_MAX_PATH) + { + b.Grow(SQMOD_MAX_PATH - b.Remaining() + 2); + } + // Try the primary method of retrieving the home directory + if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PROFILE, nullptr, 0, &b.Cursor()))) + { + // Move the edit cursor to the end of the appended data + b.Advance(strlen(&b.Cursor())); + } + // Try the secondary method of retrieving the home directory + else if (Has("USERPROFILE")) + { + // Append the contents of the USERPROFILE environment variable + Get(b, "USERPROFILE", nullptr); + } + else if (Has("HOMEDRIVE") && Has("HOMEPATH")) + { + // Append the contents of the HOMEDRIVE environment variable + Get(b, "HOMEDRIVE", nullptr); + // Append the contents of the HOMEPATH environment variable + Get(b, "HOMEPATH", nullptr); + } +#else + // Try the primary method of retrieving the home directory + struct passwd * pwd = getpwuid(getuid()); + // Validate the success of the previous operation + if (pwd) + { + // Append the path to our buffer + b.AppendS(pwd->pw_dir); + } + else + { + // Try the secondary method of retrieving the home directory + pwd = getpwuid(geteuid()); + // Validate the success of the previous operation + if (pwd) + { + // Write the path to our buffer and store the size + b.AppendS(pwd->pw_dir); + } + // Fall back to the system environment variables + else if (Has("HOME");) + { + // Append the contents of the HOME environment variable + Get(b, "HOME", nullptr); + } + } +#endif // SQMOD_OS_WINDOWS + // Make sure that the path is properly terminated + TerminatePath(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::HomeDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + HomeDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::ConfigHomeDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Is there a buffer to work with? + if (!b) + { + // Allocate buffer capable of storing a full path + b = Buffer(SQMOD_MAX_PATH); + } + // Does the APPDATA environment variable exist? + if (Has("APPDATA")) + { + // Obtain the contents of the APPDATA environment variable + Get(b, "APPDATA", nullptr); + } + else + { + // Default to the home directory + HomeDir(b); + } +#else + // Obtain the home directory path (should contain a trailing slash) + HomeDir(b); + // Use the home directory and append the ".config" sub folder + b.AppendS(".config"); +#endif // SQMOD_OS_WINDOWS + // Make sure that the path is properly terminated + TerminatePath(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::ConfigHomeDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + ConfigHomeDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::DataHomeDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Is there a buffer to work with? + if (!b) + { + // Allocate buffer capable of storing a full path + b = Buffer(SQMOD_MAX_PATH); + } + // Does the LOCALAPPDATA environment variable exist? + if (Has("LOCALAPPDATA")) + { + // Obtain the contents of the LOCALAPPDATA environment variable + return Get(b, "LOCALAPPDATA", nullptr); + } + // Default to the home config directory + return ConfigHomeDir(b); +#else + // Obtain the home directory path (should contain a trailing slash) + HomeDir(b); + // Use the home directory and append the ".local/share" sub folder + b.AppendS(".config/share"); +#endif // SQMOD_OS_WINDOWS + // Make sure that the path is properly terminated + TerminatePath(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::DataHomeDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + DataHomeDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::TempHomeDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Use the regular temp directory + TempDir(b); +#else + // Obtain the home directory path (should contain a trailing slash) + HomeDir(b); + // Use the home directory and append the ".local/tmp" folder + b.AppendS(".local/tmp"); + // Make sure that the path is properly terminated + TerminatePath(b); +#endif // SQMOD_OS_WINDOWS +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::TempHomeDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + TempHomeDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::CacheHomeDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Use the regular temp directory + TempDir(b); +#else + // Obtain the home directory path (should contain a trailing slash) + HomeDir(b); + // Use the home directory and append the ".cache" folder + b.AppendS(".cache"); + // Make sure that the path is properly terminated + TerminatePath(b); +#endif // SQMOD_OS_WINDOWS +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::CacheHomeDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + CacheHomeDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::TempDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Is there a buffer to work with? + if (!b) + { + // Allocate buffer capable of storing a full path + b = Buffer(SQMOD_MAX_PATH); + } + // Retrieve the path of the directory designated for temporary files + DWORD len = GetTempPathA(b.Remaining(), &b.Cursor()); + // Did we failed to retrieve the path? + if (len == 0) + { + return; // Unable to retrieve the path! + } + // Did we have enough space left in the buffer? + else if (len > b.Remaining()) + { + // Acquire a new buffer with a more appropriate capacity this time + b.Grow(len - b.Remaining() + 2); + // Attempt to retrieve the temporary directory one more time + len = GetTempPathA(b.Remaining(), &b.Cursor()); + // ^ On failure the null terminator is included in the length + } + // Convert the acquired path to its long form + len = GetLongPathNameA(&b.Cursor(), &b.Cursor(), b.Remaining()); + // Did we failed to convert the path? + if (len == 0) + { + return; // Unable to convert the path! + } + // Did we have enough space left in the buffer? + else if (len > b.Remaining()) + { + // Acquire a new buffer with a more appropriate capacity this time + b.Grow(len - b.Remaining() + 2); + // Attempt to retrieve the temporary directory again because we reused the buffer + GetTempPathA(b.Remaining(), &b.Cursor()); + // Attempt to convert the acquired path to its long form one more time + len = GetLongPathNameA(&b.Cursor(), &b.Cursor(), b.Remaining()); + // ^ On failure the null terminator is included in the length + } + // Move the edit cursor to the end of the appended data + b.Advance(len); +#else + // Does the TMPDIR environment variable exist? + if (SysEnv::Has("TMPDIR")) + { + // Obtain the contents of the TMPDIR environment variable + Get(b, "TMPDIR", nullptr); + } + else + { + // Default to the "/tmp" directory + b.AppendS("/tmp/"); + } +#endif // SQMOD_OS_WINDOWS + // Make sure that the path is properly terminated + TerminatePath(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::TempDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + TempDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::ConfigDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Is there a buffer to work with? + if (!b) + { + // Allocate buffer capable of storing a full path + b = Buffer(SQMOD_MAX_PATH); + } + // Does the PROGRAMDATA environment variable exist? + if (Has("PROGRAMDATA")) + { + // Obtain the contents of the PROGRAMDATA environment variable + Get(b, "PROGRAMDATA", nullptr); + } + else + { + // Make sure that whatever string is in the buffer, if any, is null terminated + b.Cursor() = '\0'; + // Unable to retrieve the path! + return; + } +#else + // Default to "/etc" directory + b.WriteS("/etc/"); +#endif // SQMOD_OS_WINDOWS + // Make sure that the path is properly terminated + TerminatePath(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::ConfigDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + ConfigDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::SystemDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + // Is there a buffer to work with? + if (!b) + { + // Allocate buffer capable of storing a full path + b = Buffer(SQMOD_MAX_PATH); + } + // Retrieve the path of the system directory + DWORD len = GetSystemDirectoryA(&b.Cursor(), b.Remaining()); + // Did we failed to retrieve the path? + if (len == 0) + { + return; // Unable to retrieve the path! + } + // Did we have enough space left in the buffer? + else if (len > b.Remaining()) + { + // Acquire a new buffer with a more appropriate capacity this time + b.Grow(len - b.Remaining() + 2); + // Attempt to retrieve the path of the system directory one more time + len = GetSystemDirectoryA(&b.Cursor(), b.Remaining()); + // ^ On failure the null terminator is included in the length + } + // Move the edit cursor to the end of the appended data + b.Advance(len); +#else + // Use a dummy directory for now + b.WriteS("/sys/"); +#endif // SQMOD_OS_WINDOWS + // Make sure that the path is properly terminated + TerminatePath(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::SystemDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Forward the call to the regular function + SystemDir(b); + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +void SysEnv::NullDir(Buffer & b) +{ +#ifdef SQMOD_OS_WINDOWS + b.AppendS("NUL:"); +#else + b.AppendS("/dev/null/"); +#endif // SQMOD_OS_WINDOWS + // Make sure that whatever string is in the buffer, if any, is null terminated + b.Cursor() = '\0'; +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysEnv::NullDir() +{ + // Allocate buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Append the null path +#ifdef SQMOD_OS_WINDOWS + b.AppendS("NUL:"); +#else + b.AppendS("/dev/null/"); +#endif // SQMOD_OS_WINDOWS + // Make sure that whatever string is in the buffer, if any, is null terminated + b.Cursor() = '\0'; + // Return ownership of the buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +static Object BufferToObj(const Buffer & b) +{ + // Obtain the initial stack size + const StackGuard sg(DefaultVM::Get()); + // Push the string onto the stack + sq_pushstring(DefaultVM::Get(), b.Data(), b.Position()); + // Obtain the object from the stack and return it + return Var< Object >(DefaultVM::Get(), -1).value; +} + +// ------------------------------------------------------------------------------------------------ +static bool SqEnv_Has(CCStr name) +{ + return SysEnv::Has(name); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_Get(CCStr name) +{ + return BufferToObj(SysEnv::Get(name, nullptr)); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_GetOr(CCStr name, CCStr fallback) +{ + return BufferToObj(SysEnv::Get(name, fallback)); +} + +// ------------------------------------------------------------------------------------------------ +static void SqEnv_Set(CCStr name, CCStr value) +{ + SysEnv::Set(name, value); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_ExpandVars(CCStr str) +{ + return BufferToObj(SysEnv::ExpandVars(str)); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_ExpandPath(CCStr path) +{ + return BufferToObj(SysEnv::ExpandPath(path)); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_WorkingDir() +{ + return BufferToObj(SysEnv::WorkingDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_HomeDir() +{ + return BufferToObj(SysEnv::HomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_ConfigHomeDir() +{ + return BufferToObj(SysEnv::ConfigHomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_DataHomeDir() +{ + return BufferToObj(SysEnv::DataHomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_TempHomeDir() +{ + return BufferToObj(SysEnv::TempHomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_CacheHomeDir() +{ + return BufferToObj(SysEnv::CacheHomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_TempDir() +{ + return BufferToObj(SysEnv::TempDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_ConfigDir() +{ + return BufferToObj(SysEnv::ConfigDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_SystemDir() +{ + return BufferToObj(SysEnv::SystemDir()); +} + +// ------------------------------------------------------------------------------------------------ +static Object SqEnv_NullDir() +{ + return BufferToObj(SysEnv::NullDir()); +} + +// ================================================================================================ +void Register_SysEnv(HSQUIRRELVM vm) +{ + Table sens(vm); + + sens.Func(_SC("Has"), &SqEnv_Has); + sens.Func(_SC("Get"), &SqEnv_Get); + sens.Func(_SC("GetOr"), &SqEnv_GetOr); + sens.Func(_SC("Set"), &SqEnv_Set); + sens.Func(_SC("OSName"), &SysEnv::OSName); + sens.Func(_SC("OSDisplayName"), &SysEnv::OSDisplayName); + sens.Func(_SC("OSVersion"), &SysEnv::OSVersion); + sens.Func(_SC("OSArchitecture"), &SysEnv::OSArchitecture); + sens.Func(_SC("NodeName"), &SysEnv::NodeName); + sens.Func(_SC("ProcessorCount"), &SysEnv::ProcessorCount); + sens.Func(_SC("ExpandVars"), &SqEnv_ExpandVars); + sens.Func(_SC("ExpandPath"), &SqEnv_ExpandPath); + sens.Func(_SC("WorkingDir"), &SqEnv_WorkingDir); + sens.Func(_SC("HomeDir"), &SqEnv_HomeDir); + sens.Func(_SC("ConfigHomeDir"), &SqEnv_ConfigHomeDir); + sens.Func(_SC("DataHomeDir"), &SqEnv_DataHomeDir); + sens.Func(_SC("TempHomeDir"), &SqEnv_TempHomeDir); + sens.Func(_SC("CacheHomeDir"), &SqEnv_CacheHomeDir); + sens.Func(_SC("TempDir"), &SqEnv_TempDir); + sens.Func(_SC("ConfigDir"), &SqEnv_ConfigDir); + sens.Func(_SC("SystemDir"), &SqEnv_SystemDir); + sens.Func(_SC("NullDir"), &SqEnv_NullDir); + + RootTable(vm).Bind(_SC("SqSysEnv"), sens); +} + +} // Namespace:: SqMod diff --git a/source/Library/SysEnv.hpp b/source/Library/SysEnv.hpp new file mode 100644 index 00000000..37edaf87 --- /dev/null +++ b/source/Library/SysEnv.hpp @@ -0,0 +1,316 @@ +#ifndef _LIBRARY_SYSENV_HPP_ +#define _LIBRARY_SYSENV_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "Base/Shared.hpp" +#include "Base/Buffer.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * This class provides access to environment variables and some general system information. +*/ +struct SysEnv +{ + /* -------------------------------------------------------------------------------------------- + * Default constructor. (disabled) + */ + SysEnv() = delete; + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. (disabled) + */ + SysEnv(const SysEnv &) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. (disabled) + */ + SysEnv(SysEnv &&) = delete; + + /* -------------------------------------------------------------------------------------------- + * Destructor. (disabled) + */ + ~SysEnv() = delete; + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. (disabled) + */ + SysEnv & operator = (const SysEnv &) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. (disabled) + */ + SysEnv & operator = (SysEnv &&) = delete; + +public: + + /* -------------------------------------------------------------------------------------------- + * Returns true if an environment variable with the given name is defined. + */ + static bool Has(CCStr name); + + /* -------------------------------------------------------------------------------------------- + * Returns true if an environment variable with the given name is defined. + */ + static bool Has(const String & name); + + /* -------------------------------------------------------------------------------------------- + * Returns the value of the environment variable with the given name. + * If the environment variable is undefined, returns fallback value instead. + */ + static void Get(Buffer & b, CCStr name, CCStr fallback); + + /* -------------------------------------------------------------------------------------------- + * Returns the value of the environment variable with the given name. + */ + static Buffer Get(CCStr name) + { + return Get(name, nullptr); + } + + /* -------------------------------------------------------------------------------------------- + * Returns the value of the environment variable with the given name. + */ + static Buffer Get(const String & name) + { + return Get(name.c_str(), nullptr); + } + + /* -------------------------------------------------------------------------------------------- + * Returns the value of the environment variable with the given name. + * If the environment variable is undefined, returns fallback value instead. + */ + static Buffer Get(CCStr name, CCStr fallback); + + /* -------------------------------------------------------------------------------------------- + * Returns the value of the environment variable with the given name. + * If the environment variable is undefined, returns fallback value instead. + */ + static Buffer Get(CCStr name, const String & fallback) + { + return Get(name, fallback.c_str()); + } + + /* -------------------------------------------------------------------------------------------- + * Returns the value of the environment variable with the given name. + * If the environment variable is undefined, returns fallback value instead. + */ + static Buffer Get(const String & name, CCStr fallback) + { + return Get(name.c_str(), fallback); + } + + /* -------------------------------------------------------------------------------------------- + * Returns the value of the environment variable with the given name. + * If the environment variable is undefined, returns fallback value instead. + */ + static Buffer Get(const String & name, const String & fallback) + { + return Get(name.c_str(), fallback.c_str()); + } + + /* -------------------------------------------------------------------------------------------- + * Sets the environment variable with the given name to the given value. + */ + static bool Set(CCStr name, CCStr value); + + /* -------------------------------------------------------------------------------------------- + * Sets the environment variable with the given name to the given value. + */ + static bool Set(const String & name, const String & value); + + /* -------------------------------------------------------------------------------------------- + * Returns the operating system name. + */ + static String OSName(); + + /* -------------------------------------------------------------------------------------------- + * Returns the operating system name in a more "user-friendly" way. This only affects Windows. + */ + static String OSDisplayName(); + + /* -------------------------------------------------------------------------------------------- + * Returns the operating system version. + */ + static String OSVersion(); + + /* -------------------------------------------------------------------------------------------- + * Returns the operating system architecture. + */ + static String OSArchitecture(); + + /* -------------------------------------------------------------------------------------------- + * Returns the node (or host) name. + */ + static String NodeName(); + + /* -------------------------------------------------------------------------------------------- + * Returns the number of processors installed in the system. If the number of processors + * cannot be determined, returns 1. + */ + static Uint32 ProcessorCount(); + +protected: + + /* -------------------------------------------------------------------------------------------- + * Make sure that the path in the specified buffer contains a trailing slash. + */ + static void TerminatePath(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the string. + */ + static void ExpandVars(Buffer & b, CCStr pos, CCStr end); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the path. Uses the Unix variable style. + */ + static void ExpandPath(Buffer & b, CCStr pos, CCStr end); + +public: + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the string. + */ + static void ExpandVars(Buffer & b, CCStr str); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the string. + */ + static void ExpandVars(Buffer & b, const String & str); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the string. + */ + static Buffer ExpandVars(CCStr str); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the string. + */ + static Buffer ExpandVars(const String & str); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the path. Uses the Unix variable style. + */ + static void ExpandPath(Buffer & b, CCStr path); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the path. Uses the Unix variable style. + */ + static void ExpandPath(Buffer & b, const String & path); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the path. Uses the Unix variable style. + */ + static Buffer ExpandPath(CCStr path); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the path. Uses the Unix variable style. + */ + static Buffer ExpandPath(const String & path); + + /* -------------------------------------------------------------------------------------------- + * Obtain the current working directory within the specified buffer. + */ + static void WorkingDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the current working directory within a buffer and return it. + */ + static Buffer WorkingDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's home directory within the specified buffer. + */ + static void HomeDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's home directory within a buffer and return it. + */ + static Buffer HomeDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's config directory within the specified buffer. + */ + static void ConfigHomeDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's config directory within a buffer and return it. + */ + static Buffer ConfigHomeDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's data directory within the specified buffer. + */ + static void DataHomeDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's data directory within a buffer and return it. + */ + static Buffer DataHomeDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's temporary directory within the specified buffer. + */ + static void TempHomeDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's temporary directory within a buffer and return it. + */ + static Buffer TempHomeDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's cache directory within the specified buffer. + */ + static void CacheHomeDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the user's cache directory within a buffer and return it. + */ + static Buffer CacheHomeDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the temporary directory within the specified buffer. + */ + static void TempDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the temporary directory within a buffer and return it. + */ + static Buffer TempDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the systemwide config directory within the specified buffer. + */ + static void ConfigDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the systemwide config directory within a buffer and return it. + */ + static Buffer ConfigDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the system directory within the specified buffer. + */ + static void SystemDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the system directory within a buffer and return it. + */ + static Buffer SystemDir(); + + /* -------------------------------------------------------------------------------------------- + * Obtain the null directory within the specified buffer. + */ + static void NullDir(Buffer & b); + + /* -------------------------------------------------------------------------------------------- + * Obtain the null directory within a buffer and return it. + */ + static Buffer NullDir(); +}; + +} // Namespace:: SqMod + +#endif // _LIBRARY_SYSENV_HPP_ diff --git a/source/Library/SysPath.cpp b/source/Library/SysPath.cpp index e69de29b..03c477c4 100644 --- a/source/Library/SysPath.cpp +++ b/source/Library/SysPath.cpp @@ -0,0 +1,1739 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/SysPath.hpp" +#include "Library/SysEnv.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#ifdef SQMOD_OS_WINDOWS + #include +#else + #include +#endif + +// ------------------------------------------------------------------------------------------------ +#ifdef SQMOD_OS_WINDOWS + // Maximum path size in characters + #define SQMOD_MAX_PATH (sizeof(TCHAR) * MAX_PATH) + // Character to be used when working with path + typedef TCHAR PChar; +#else + // Maximum path size in characters + #define SQMOD_MAX_PATH (PATH_MAX) + // Character to be used when working with path + typedef CharT PChar; +#endif // SQMOD_OS_WINDOWS + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQInteger SysPath::Typename(HSQUIRRELVM vm) +{ + static SQChar name[] = _SC("SqSysPath"); + sq_pushstring(vm, name, sizeof(name)); + return 1; +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath() + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(false) +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(bool absolute) + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(absolute) +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(CSStr path) + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(false) +{ + Assign(path); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(CSStr path, Int32 style) + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(false) +{ + Assign(path, static_cast< Style >(style)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(CSStr path, Style style) + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(false) +{ + Assign(path, style); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(const Buffer & path, Int32 size) + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(false) +{ + Assign(path, Style::Guess, size); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(const Buffer & path, Style style, Int32 size) + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(false) +{ + Assign(path, style, size); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(const String & path) + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(false) +{ + Assign(path, Style::Guess); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(const String & path, Style style) + : m_Dirs() + , m_Name() + , m_Drive(0) + , m_Absolute(false) +{ + Assign(path, style); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(const SysPath & parent, CSStr name) + : m_Dirs(parent.m_Dirs) + , m_Name(name ? name : "") + , m_Drive(parent.m_Drive) + , m_Absolute(parent.m_Absolute) +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(const SysPath & parent, const String & name) + : m_Dirs(parent.m_Dirs) + , m_Name(name) + , m_Drive(parent.m_Drive) + , m_Absolute(parent.m_Absolute) +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(const SysPath & parent, const SysPath & relative) + : m_Dirs(parent.m_Dirs) + , m_Name(parent.m_Name) + , m_Drive(parent.m_Drive) + , m_Absolute(parent.m_Absolute) +{ + // Resolve the specified path + Resolve(relative); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(const SysPath & o) + : m_Dirs(o.m_Dirs) + , m_Name(o.m_Name) + , m_Drive(o.m_Drive) + , m_Absolute(o.m_Absolute) +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +SysPath::SysPath(SysPath && o) + : m_Dirs(std::move(o.m_Dirs)) + , m_Name(std::move(o.m_Name)) + , m_Drive(o.m_Drive) + , m_Absolute(o.m_Absolute) +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +SysPath::~SysPath() +{ + /* ... */ +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::operator = (const SysPath & o) +{ + // Prevent self assignment + if (this != &o) + { + m_Dirs = o.m_Dirs; + m_Name = o.m_Name; + m_Drive = o.m_Drive; + m_Absolute = o.m_Absolute; + } + + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::operator = (SysPath && o) +{ + // Prevent self assignment + if (this != &o) + { + m_Dirs = std::move(o.m_Dirs); + m_Name = std::move(o.m_Name); + m_Drive = o.m_Drive; + m_Absolute = o.m_Absolute; + } + + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::operator = (CSStr path) +{ + Assign(path); + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::operator = (const String & path) +{ + Assign(path); + return *this; +} + +// ------------------------------------------------------------------------------------------------ +bool SysPath::operator == (const SysPath & o) const +{ + return (m_Drive == o.m_Drive) && (m_Absolute == o.m_Absolute) + && (m_Name == o.m_Name) && (m_Dirs == o.m_Dirs); +} + +// ------------------------------------------------------------------------------------------------ +bool SysPath::operator != (const SysPath & o) const +{ + return !(*this == o); +} + +// ------------------------------------------------------------------------------------------------ +SysPath::operator bool () const +{ + return (m_Dirs.empty() && m_Name.empty() && (m_Drive == 0) && !m_Absolute); +} + +// ------------------------------------------------------------------------------------------------ +const String & SysPath::operator [] (Uint32 n) const +{ + // Is this within the bounds of the directory list? + if (n < m_Dirs.size()) + { + return m_Dirs[n]; + } + // Fall back to the file name + else + { + return m_Name; + } +} + +// ------------------------------------------------------------------------------------------------ +Int32 SysPath::Cmp(const SysPath & o) const +{ + if (*this == o) + return 0; + else if (ToBuffer().Position() > o.ToBuffer().Position()) + return 1; + else + return -1; +} + +// ------------------------------------------------------------------------------------------------ +Object SysPath::ToString() const +{ + // Transform the path components into a string + Buffer b(ToBuffer()); + // Obtain the initial stack size + const StackGuard sg(DefaultVM::Get()); + // Push the string onto the stack + sq_pushstring(DefaultVM::Get(), b.Data(), b.Position()); + // Obtain the object from the stack and return it + return Var< Object >(DefaultVM::Get(), -1).value; +} + +// ------------------------------------------------------------------------------------------------ +void SysPath::Swap(SysPath & path) +{ + m_Dirs.swap(path.m_Dirs); + m_Name.swap(path.m_Name); + std::swap(m_Drive, path.m_Drive); + std::swap(m_Absolute, path.m_Absolute); +} + +// ------------------------------------------------------------------------------------------------ +void SysPath::Clear() +{ + m_Dirs.clear(); + m_Name.clear(); + m_Drive = 0; + m_Absolute = false; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(CSStr path) +{ + // Is the specified path valid? + if (!path || *path == '\0') + { + // Just clear current path + Clear(); + } + else + { +#ifdef SQMOD_OS_WINDOWS + ParseWindows(path, path + strlen(path)); +#else + ParseUnix(path, path + strlen(path)); +#endif // SQMOD_OS_WINDOWS + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(CSStr path, Int32 style) +{ + return Assign(path, static_cast< Style >(style)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(CSStr path, Style style) +{ + // Is the specified path valid? + if (!path || *path == '\0') + { + // Just clear current path + Clear(); + } + else + { + // Identify which style was requested + switch (style) + { + case Style::Unix: + ParseUnix(path, path + strlen(path)); + break; + case Style::Windows: + ParseWindows(path, path + strlen(path)); + break; + case Style::Native: +#ifdef SQMOD_OS_WINDOWS + ParseWindows(path, path + strlen(path)); +#else + ParseUnix(path, path + strlen(path)); +#endif // SQMOD_OS_WINDOWS + break; + case Style::Guess: + ParseGuess(path, path + strlen(path)); + break; + case Style::Dynamic: + ParseDynamic(path, path + strlen(path)); + break; + default: + SqThrowF("Unknown system path style"); + } + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(const Buffer & path, Int32 size) +{ + // Is the specified path valid? + if (!path) + { + // Just clear current path + Clear(); + } + else if (size < 0) + { +#ifdef SQMOD_OS_WINDOWS + ParseWindows(path.Data(), &path.Cursor()); +#else + ParseUnix(path.Data(), &path.Cursor()); +#endif // SQMOD_OS_WINDOWS + } + else if (static_cast< Uint32 >(size) < path.Capacity()) + { +#ifdef SQMOD_OS_WINDOWS + ParseWindows(path.Data(), path.Data() + size); +#else + ParseUnix(path.Data(), path.Data() + size); +#endif // SQMOD_OS_WINDOWS + } + else + { + SqThrowF("The specified path size is out of range: %u >= %u", size, path.Capacity()); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(const Buffer & path, Style style, Int32 size) +{ + // Is the specified path valid? + if (!path) + { + // Just clear current path + Clear(); + } + else if (size < 0) + { + // Identify which style was requested + switch (style) + { + case Style::Unix: + ParseUnix(path.Data(), &path.Cursor()); + break; + case Style::Windows: + ParseWindows(path.Data(), &path.Cursor()); + break; + case Style::Native: +#ifdef SQMOD_OS_WINDOWS + ParseWindows(path.Data(), &path.Cursor()); +#else + ParseUnix(path.Data(), &path.Cursor()); +#endif // SQMOD_OS_WINDOWS + break; + case Style::Guess: + ParseGuess(path.Data(), &path.Cursor()); + break; + case Style::Dynamic: + ParseDynamic(path.Data(), &path.Cursor()); + break; + default: + SqThrowF("Unknown system path style"); + } + } + else if (static_cast< Uint32 >(size) < path.Capacity()) + { + // Identify which style was requested + switch (style) + { + case Style::Unix: + ParseUnix(path.Data(), path.Data() + size); + break; + case Style::Windows: + ParseWindows(path.Data(), path.Data() + size); + break; + case Style::Native: +#ifdef SQMOD_OS_WINDOWS + ParseWindows(path.Data(), path.Data() + size); +#else + ParseUnix(path.Data(), path.Data() + size); +#endif // SQMOD_OS_WINDOWS + break; + case Style::Guess: + ParseGuess(path.Data(), path.Data() + size); + break; + case Style::Dynamic: + ParseDynamic(path.Data(), path.Data() + size); + break; + default: + SqThrowF("Unknown system path style"); + } + } + else + { + SqThrowF("The specified path size is out of range: %u >= %u", size, path.Capacity()); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(const String & path) +{ + // Is the specified path valid? + if (path.empty()) + { + // Just clear current path + Clear(); + } + else + { +#ifdef SQMOD_OS_WINDOWS + ParseWindows(path.data(), path.data() + path.size()); +#else + ParseUnix(path.data(), path.data() + path.size()); +#endif // SQMOD_OS_WINDOWS + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(const String & path, Style style) +{ + // Is the specified path valid? + if (path.empty()) + { + // Just clear current path + Clear(); + } + else + { + // Identify which style was requested + switch (style) + { + case Style::Unix: + ParseUnix(path.data(), path.data() + path.size()); + break; + case Style::Windows: + ParseWindows(path.data(), path.data() + path.size()); + break; + case Style::Native: +#ifdef SQMOD_OS_WINDOWS + ParseWindows(path.data(), path.data() + path.size()); +#else + ParseUnix(path.data(), path.data() + path.size()); +#endif // SQMOD_OS_WINDOWS + break; + case Style::Guess: + ParseGuess(path.data(), path.data() + path.size()); + break; + case Style::Dynamic: + ParseDynamic(path.data(), path.data() + path.size()); + break; + default: + SqThrowF("Unknown system path style"); + } + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(const SysPath & parent, CSStr name) +{ + // Copy the parent values + *this = parent; + // Set the specified file name + SetFilename(name); + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(const SysPath & parent, const String & name) +{ + // Copy the parent values + *this = parent; + // Set the specified file name + SetFilename(name); + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(const SysPath & parent, const SysPath & relative) +{ + // Copy the parent values + *this = parent; + // Resolve the specified path + Resolve(relative); + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(const SysPath & path) +{ + // Just use regular assignment + *this = path; + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Assign(SysPath && path) +{ + // Just use regular assignment + *this = path; + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::AssignDir(CSStr path) +{ + // Assign the specified path + Assign(path); + // Force it to be a directory and allow chaining + return MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::AssignDir(CSStr path, Int32 style) +{ + // Assign the specified path + Assign(path, style); + // Force it to be a directory and allow chaining + return MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::AssignDir(CSStr path, Style style) +{ + // Assign the specified path + Assign(path, style); + // Force it to be a directory and allow chaining + return MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::AssignDir(const Buffer & path, Int32 size) +{ + // Assign the specified path + Assign(path, size); + // Force it to be a directory and allow chaining + return MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::AssignDir(const Buffer & path, Style style, Int32 size) +{ + // Assign the specified path + Assign(path, style, size); + // Force it to be a directory and allow chaining + return MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::AssignDir(const String & path) +{ + // Assign the specified path + Assign(path); + // Force it to be a directory and allow chaining + return MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::AssignDir(const String & path, Style style) +{ + // Assign the specified path + Assign(path, style); + // Force it to be a directory and allow chaining + return MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysPath::ToBuffer() const +{ +#ifdef SQMOD_OS_WINDOWS + return BuildWindows(); +#else + return BuildUnix(); +#endif // SQMOD_OS_WINDOWS +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysPath::ToBuffer(Style style) const +{ + if (style == Style::Unix) + { + return BuildUnix(); + } + else if (style == Style::Windows) + { + return BuildWindows(); + } +#ifdef SQMOD_OS_WINDOWS + return BuildWindows(); +#else + return BuildUnix(); +#endif // SQMOD_OS_WINDOWS +} + +// ------------------------------------------------------------------------------------------------ +Object SysPath::ToStr(Int32 style) const +{ + // Transform the path components into a string + Buffer b(ToBuffer(static_cast< Style >(style))); + // Obtain the initial stack size + const StackGuard sg(DefaultVM::Get()); + // Push the string onto the stack + sq_pushstring(DefaultVM::Get(), b.Data(), b.Position()); + // Obtain the object from the stack and return it + return Var< Object >(DefaultVM::Get(), -1).value; +} + +// ------------------------------------------------------------------------------------------------ +void SysPath::FromString(CSStr path) +{ + Assign(path); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::MakeDirectory() +{ + // Do we even have a name? + if (!m_Name.empty()) + { + // Make it a directory + m_Dirs.push_back(std::move(m_Name)); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::MakeFile() +{ + // Do we have some directories and no existing file? + if (!m_Dirs.empty() && m_Name.empty()) + { + // Use the last directory as a file + m_Name = std::move(m_Dirs.back()); + // Remove it from the directory list + m_Dirs.pop_back(); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::MakeParent() +{ + // Do we have a name? + if (m_Name.empty()) + { + // Do we have any existing directories? + if (m_Dirs.empty()) + { + // Make sure this path isn't absolute + if (!m_Absolute) + { + // Reference the parent directory + m_Dirs.emplace_back(".."); + } + } + else + { + // Are we already referencing a parent? + if (m_Dirs.back().compare("..") == 0) + { + // Then reference the parent of that parent + m_Dirs.emplace_back(".."); + } + // Just pop the last directory + else + { + m_Dirs.pop_back(); + } + } + } + // Just clear the name + else + { + m_Name.clear(); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::MakeAbsolute() +{ + // Is this path already absolute? + if (!m_Absolute) + { + MakeAbsolute(Working()); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::MakeAbsolute(const SysPath & base) +{ + // Is this path already absolute? + if (!m_Absolute) + { + MakeAbsolute(base); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::MakeAbsolute(SysPath && base) +{ + // Is this path already absolute? + if (!m_Absolute) + { + // Reserve the space upfront + base.m_Dirs.reserve(base.m_Dirs.size() + m_Dirs.size()); + // Move our directories at the back + for (auto & dir : m_Dirs) + { + base.m_Dirs.push_back(std::move(dir)); + } + // Take ownership of base directories + m_Dirs.swap(base.m_Dirs); + // Copy the drive letter + m_Drive = base.m_Drive; + // Make absolute only if base is + m_Absolute = base.m_Absolute; + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(const SysPath & path) +{ + // Make sure this is a directory + MakeDirectory(); + // Only attempt to append if not empty + if (!path.Empty()) + { + // Append the directories from the specified path + m_Dirs.insert(m_Dirs.end(), path.m_Dirs.begin(), path.m_Dirs.end()); + // Copy the file name if any + m_Name = path.m_Name; + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(SysPath && path) +{ + // Make sure this is a directory + MakeDirectory(); + // Only attempt to append if not empty + if (!path.Empty()) + { + // Request the necessary directory list size upfront + m_Dirs.reserve(m_Dirs.size() + path.m_Dirs.size()); + // Move all directories at the back of our list + for (auto & dir : path.m_Dirs) + { + m_Dirs.push_back(std::move(dir)); + } + // Move the file name if any + m_Name = std::move(path.m_Name); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(CSStr path) +{ + return Append(SysPath(path)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(CSStr path, Int32 style) +{ + return Append(SysPath(path, style)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(CSStr path, Style style) +{ + return Append(SysPath(path, style)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(const Buffer & path, Int32 size) +{ + return Append(SysPath(path, size)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(const Buffer & path, Style style, Int32 size) +{ + return Append(SysPath(path, style, size)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(const String & path) +{ + return Append(SysPath(path)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Append(const String & path, Style style) +{ + return Append(SysPath(path, style)); +} + +// ------------------------------------------------------------------------------------------------ +const String & SysPath::Directory(Uint32 n) const +{ + // Is this within the bounds of the directory list? + if (n < m_Dirs.size()) + { + return m_Dirs[n]; + } + // Fall back to the file name + else + { + return m_Name; + } +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Push(CSStr dir) +{ + // Is the specified directory valid? + if (dir && *dir != 0) + { + Push(String(dir)); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Push(const String & dir) +{ + // Is the specified directory valid? + if (!dir.empty() && dir.compare(".") != 0) + { + Push(String(dir)); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Push(String && dir) +{ + // Is the specified directory valid? + if (!dir.empty() && dir.compare(".") != 0) + { + // Does it refer to a parent directory? + if (dir.compare("..") == 0) + { + // Is out last directory already a reference to a parent? + if (!m_Dirs.empty() && m_Dirs.back().compare("..") != 0) + { + m_Dirs.pop_back(); + } + // Move it at the back of our list + else if (!m_Absolute) + { + m_Dirs.push_back(dir); + } + } + // Move it at the back of our list + else + { + m_Dirs.push_back(dir); + } + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::PopBack() +{ + // Do we even have any directories? + if (!m_Dirs.empty()) + { + // Erase the last one + m_Dirs.pop_back(); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::PopFront() +{ + // Do we even have any directories? + if (!m_Dirs.empty()) + { + // Erase the first one + m_Dirs.erase(m_Dirs.begin()); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::SetFilename(CSStr name) +{ + // Is the file name even valid? + if (name) + { + m_Name.assign(name); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::SetFilename(const String & name) +{ + // Is the file name even valid? + m_Name.assign(name); + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::SetFilename(String && name) +{ + // Is the file name even valid? + m_Name.assign(name); + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::SetBasename(CSStr name) +{ + // Is the file name even valid? + if (name) + { + // Extract the current extension + String ext = GetExtension(); + // Assign the new base name + m_Name.assign(name); + // Was there an extension before? + if (!ext.empty()) + { + // Add the extension separator + m_Name.push_back('.'); + // Add the original extension + m_Name.append(ext); + } + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::SetBasename(const String & name) +{ + // Extract the current extension + String ext = GetExtension(); + // Assign the new base name + m_Name.assign(name); + // Was there an extension before? + if (!ext.empty()) + { + // Add the extension separator + m_Name.push_back('.'); + // Add the original extension + m_Name.append(ext); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::SetBasename(String && name) +{ + // Extract the current extension + String ext = GetExtension(); + // Assign the new base name + m_Name.assign(name); + // Was there an extension before? + if (!ext.empty()) + { + // Add the extension separator + m_Name.push_back('.'); + // Add the original extension + m_Name.append(ext); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +String SysPath::GetBasename() const +{ + // Attempt to find the last dot in the file name + const String::size_type pos = m_Name.rfind('.'); + // Was there an extension separator? + if (pos != String::npos) + { + // Return everything before the separator + return m_Name.substr(0, pos); + } + // Return the whole name + return m_Name; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::SetExtension(CSStr ext) +{ + // Attempt to find the last dot in the file name + const String::size_type pos = m_Name.rfind('.'); + // Was there an extension separator? + if (pos != String::npos) + { + // Erase the current extension + m_Name.resize(pos); + } + // Is there an extension to append? + if (ext && *ext != 0) + { + m_Name.push_back('.'); + m_Name.append(ext); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::SetExtension(const String & ext) +{ + // Attempt to find the last dot in the file name + const String::size_type pos = m_Name.rfind('.'); + // Was there an extension separator? + if (pos != String::npos) + { + // Erase the current extension + m_Name.resize(pos); + } + // Is there an extension to append? + if (!ext.empty()) + { + m_Name.push_back('.'); + m_Name.append(ext); + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +String SysPath::GetExtension() const +{ + // Attempt to find the last dot in the file name + const String::size_type pos = m_Name.rfind('.'); + // Was there an extension separator? + if (pos != String::npos) + { + // Return everything after the separator + return m_Name.substr(pos + 1); + } + // Default to an empty string + return String(); +} + +// ------------------------------------------------------------------------------------------------ +CSStr SysPath::GetExtensionC() const +{ + // Attempt to find the last dot in the file name + const String::size_type pos = m_Name.rfind('.'); + // Was there an extension separator? + if (pos != String::npos) + { + // Because indexing starts from 0, the separator is skipped + return &m_Name[pos+1]; + } + // Default to an empty string + return ""; +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::Parent() const +{ + // Make a copy + SysPath p(*this); + // Force the copy to be parent + p.MakeParent(); + // Return ownership of copy + return std::move(p); +} + +// ------------------------------------------------------------------------------------------------ +SysPath & SysPath::Resolve(const SysPath & path) +{ + // Is the specified path absolute? + if (path.m_Absolute) + { + // Copy it's values + Assign(path); + } + else + { + // Append its directories + for (const auto & dir : path.m_Dirs) + { + Push(dir); + } + // Copy its file name + m_Name = path.m_Name; + } + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +void SysPath::ParseUnix(CSStr pos, CSStr end) +{ + // Clear previous path information + Clear(); + // Is there even a path to parse? + if (pos == end) + { + return; + } + // If the path starts with a forward slash then the path is absolute + else if (*pos == '/') + { + // This is now an absolute path + m_Absolute = true; + // Skip this directory separator + ++pos; + } + // If the path starts with the tilde character then the home directory was requested + else if (*pos == '~') + { + // To be expanded, the tilde character must be followed by a slash or no other character + if (pos == end || *(++pos) == '/') + { + // Obtain the home path + SysPath up(Home()); + // Take ownership, don't copy + m_Dirs.swap(up.m_Dirs); + // This is now an absolute path + m_Absolute = true; + } + // Go back to the previous character and use it literally + else + { + --pos; + } + } + // Make another iterator to slice directory names in one go + CSStr itr = pos; + // Extract the remaining directories from the specified path + while (itr != end) + { + // Have we encountered a directory separator? + if (*itr == '/') + { + // Slice the name from the path if valid + if (itr != pos) + { + m_Dirs.emplace_back(pos, itr); + } + // Move the iterator to the current position + pos = ++itr; + } + else + { + ++itr; + } + } + // Grab the last name if any + if (pos != end) + { + m_Name.assign(pos, end); + } +} + +// ------------------------------------------------------------------------------------------------ +void SysPath::ParseWindows(CSStr pos, CSStr end) +{ + // Clear previous path information + Clear(); + // Is there even a path to parse? + if (pos == end) + { + return; + } + // If the path starts with a forward slash then the path is absolute + else if (*pos == '\\') + { + // This is now an absolute path + m_Absolute = true; + // Skip this directory separator + ++pos; + } + // If the path starts with the tilde character then the home directory was requested + else if (*pos == '~') + { + // To be expanded, the tilde character must be followed by a slash or no other character + if (pos == end || *(++pos) == '\\') + { + // Obtain the home path + SysPath up(Home()); + // Take ownership, don't copy + m_Dirs.swap(up.m_Dirs); + // Also copy the drive letter + m_Drive = up.m_Drive; + // This is now an absolute path + m_Absolute = true; + } + // Go back to the previous character and use it literally + else + { + --pos; + } + } + // Is it possible to have a drive letter? + else if ((end - pos) > 2 && pos[1] == ':' && pos[2] == '\\' && isalpha(*pos) != 0) + { + // Grab the drive letter + m_Drive = *pos; + // Skip the drive path and colon + pos += 2; + } + // Is it possible to have just the drive letter? + else if ((end - pos) == 2 && pos[1] == ':' && isalpha(*pos) != 0) + { + // Grab the drive letter + m_Drive = *pos; + // Nothing left to parse + return; + } + // Make another iterator to slice directory names in one go + CSStr itr = pos; + // Extract the remaining directories from the specified path + while (itr != end) + { + // Have we encountered a directory separator? + if (*itr == '\\') + { + // Slice the name from the path if valid + if (itr != pos) + { + m_Dirs.emplace_back(pos, itr); + } + // Move the iterator to the current position + pos = ++itr; + } + else + { + ++itr; + } + } + // Grab the last name if any + if (pos != end) + { + m_Name.assign(pos, end); + } +} + +// ------------------------------------------------------------------------------------------------ +void SysPath::ParseDynamic(CSStr pos, CSStr end) +{ + // Clear previous path information + Clear(); + // Is there even a path to parse? + if (pos == end) + { + return; + } + // If the path starts with a slash then the path is absolute + else if (*pos == '\\' || *pos == '/') + { + // This is now an absolute path + m_Absolute = true; + // Skip this directory separator + ++pos; + } + // If the path starts with the tilde character then the home directory was requested + else if (*pos == '~') + { + // Skip the tilde character + ++pos; + // The tilde character must be followed by a slash or no other character to be expanded + if (pos == end || *pos == '/' || *pos == '\\') + { + // Obtain the home path + SysPath up(Home()); + // Take ownership, don't copy + m_Dirs.swap(up.m_Dirs); +#ifdef SQMOD_OS_WINDOWS + // Also copy the drive letter + m_Drive = up.m_Drive; +#endif // SQMOD_OS_WINDOWS + // This is now an absolute path + m_Absolute = true; + } + // Go back to the previous character and use it literally + else + { + --pos; + } + } +#ifdef SQMOD_OS_WINDOWS + // Is it possible to have a drive letter? + else if ((end - pos) > 2 && pos[1] == ':' && (pos[2] == '\\' || pos[2] == '/') && isalpha(*pos) != 0) + { + // Grab the drive letter + m_Drive = *pos; + // Skip the drive path and colon + pos += 2; + } + // Is it possible to have just the drive letter? + else if ((end - pos) == 2 && pos[1] == ':' && isalpha(*pos) != 0) + { + // Grab the drive letter + m_Drive = *pos; + // Nothing left to parse + return; + } +#endif // SQMOD_OS_WINDOWS + // Make another iterator to slice directory names in one go + CSStr itr = pos; + // Extract the remaining directories from the specified path + while (itr != end) + { + // Have we encountered a directory separator? + if (*itr == '/' || *itr == '\\') + { + // Slice the name from the path if valid + if (itr != pos) + { + m_Dirs.emplace_back(pos, itr); + } + // Move the iterator to the current position + pos = ++itr; + } + else + { + ++itr; + } + } + // Grab the last name if any + if (pos != end) + { + m_Name.assign(pos, end); + } +} + +// ------------------------------------------------------------------------------------------------ +void SysPath::ParseGuess(CSStr pos, CSStr end) +{ + // Scan for forward slash + const bool has_fwslash = (strchr(pos, '/') != NULL); + const bool has_bwslash = (strchr(pos, '\\') != NULL); + // Does it contain both forward and backward slashes? + if (has_fwslash && has_bwslash) + { + ParseDynamic(pos, end); + } + // Does it contain the forward slash? + else if (has_fwslash) + { + ParseUnix(pos, end); + } + // Does it contain the backward slash? + else if (has_bwslash) + { + ParseWindows(pos, end); + } + // Does it contain a drive letter? + else if ((end - pos) == 2 && pos[1] == ':' && isalpha(*pos) != 0) + { + ParseWindows(pos, end); + } + // Try to parse it as a dynamic path + else + { + ParseDynamic(pos, end); + } +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysPath::BuildUnix() const +{ + // Obtain a buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Is this an absolute path? + if (m_Absolute) + { + // Start with a slash + b.Push('/'); + } + // Concatenate all directories + for (const auto & dir : m_Dirs) + { + // Append the name + b.AppendS(dir.c_str(), dir.size()); + // Separate from next + b.Push('/'); + } + // Is there a file name to add? + if (!m_Name.empty()) + { + b.AppendS(m_Name.c_str(), m_Name.size()); + } + // Make sure the string is null terminated + b.Cursor() = '\0'; + // Return ownership of buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +Buffer SysPath::BuildWindows() const +{ + // Obtain a buffer capable of storing a full path + Buffer b(SQMOD_MAX_PATH); + // Does it have a drive letter? + if (isalpha(m_Drive) != 0) + { + // Add the drive letter + b.Push(m_Drive); + // Add the colon + b.Push(':'); + // Add the slash + b.Push('\\'); + } + // Is this an absolute path? + else if (m_Absolute) + { + // Start with a slash + b.Push('\\'); + } + // Concatenate all directories + for (const auto & dir : m_Dirs) + { + // Append the name + b.AppendS(dir.c_str(), dir.size()); + // Separate from next + b.Push('\\'); + } + // Is there a file name to add? + if (!m_Name.empty()) + { + b.AppendS(m_Name.c_str(), m_Name.size()); + } + // Make sure the string is null terminated + b.Cursor() = '\0'; + // Return ownership of buffer + return std::move(b); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::ForDirectory(CSStr path) +{ + return SysPath(path).MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::ForDirectory(CSStr path, Int32 style) +{ + return SysPath(path, style).MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::ForDirectory(CSStr path, Style style) +{ + return SysPath(path, style).MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::ForDirectory(const String & path) +{ + return SysPath(path).MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::ForDirectory(const String & path, Style style) +{ + return SysPath(path, style).MakeDirectory(); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::Expand(CSStr path) +{ + return SysPath(SysEnv::ExpandPath(path)); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::Home() +{ + return SysPath(SysEnv::HomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::ConfigHome() +{ + return SysPath(SysEnv::ConfigHomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::DataHome() +{ + return SysPath(SysEnv::DataHomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::TempHome() +{ + return SysPath(SysEnv::TempHomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::CacheHome() +{ + return SysPath(SysEnv::CacheHomeDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::Working() +{ + return SysPath(SysEnv::WorkingDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::Temp() +{ + return SysPath(SysEnv::TempDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::Config() +{ + return SysPath(SysEnv::ConfigDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::System() +{ + return SysPath(SysEnv::SystemDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::Null() +{ + return SysPath(SysEnv::NullDir()); +} + +// ------------------------------------------------------------------------------------------------ +SysPath SysPath::With(const SysPath & parent, CSStr name) +{ + return SysPath(parent, name); +} + +// ================================================================================================ +void Register_SysPath(HSQUIRRELVM vm) +{ + RootTable(vm).Bind("SqSysPath", Class< SysPath >(vm, "SqSysPath") + // Constructors + .Ctor() + .Ctor< CSStr >() + .Ctor< CSStr, Int32 >() + // Metamethods + .Func(_SC("_cmp"), &SysPath::Cmp) + .SquirrelFunc(_SC("_typename"), &SysPath::Typename) + .Func(_SC("_tostring"), &SysPath::ToString) + // Properties + .Prop(_SC("String"), &SysPath::ToString, &SysPath::FromString) + .Prop(_SC("Absolute"), &SysPath::IsAbsolute) + .Prop(_SC("Relative"), &SysPath::IsRelative) + .Prop(_SC("Directory"), &SysPath::IsDirectory) + .Prop(_SC("File"), &SysPath::IsFile) + .Prop(_SC("Empty"), &SysPath::Empty) + .Prop(_SC("Drive"), &SysPath::GetDrive, &SysPath::SetDrive) + .Prop(_SC("Depth"), &SysPath::Depth) + .Prop(_SC("Filename"), &SysPath::GetFilename, &SysPath::SqSetFilename) + .Prop(_SC("Basename"), &SysPath::GetBasename, &SysPath::SqSetBasename) + .Prop(_SC("Extension"), &SysPath::GetExtensionC, &SysPath::SqSetExtension) + .Prop(_SC("Parent"), &SysPath::Parent) + // Member Methods + .Func(_SC("Swap"), &SysPath::Swap) + .Func(_SC("Clear"), &SysPath::Clear) + .Func< SysPath & (SysPath::*)(const SysPath &) >(_SC("AssignPath"), &SysPath::Assign) + .Func< SysPath & (SysPath::*)(const SysPath &) >(_SC("AppendPath"), &SysPath::Append) + .Func(_SC("MakeDir"), &SysPath::MakeDirectory) + .Func(_SC("MakeDirectory"), &SysPath::MakeDirectory) + .Func(_SC("MakeFile"), &SysPath::MakeFile) + .Func(_SC("MakeParent"), &SysPath::MakeParent) + .Func(_SC("Dir"), &SysPath::Directory) + .Func(_SC("Directory"), &SysPath::Directory) + .Func< SysPath & (SysPath::*)(CSStr) >(_SC("Push"), &SysPath::Push) + .Func(_SC("PopBack"), &SysPath::PopBack) + .Func(_SC("PopFront"), &SysPath::PopFront) + .Func< SysPath & (SysPath::*)(CSStr) >(_SC("SetFilename"), &SysPath::SetFilename) + .Func(_SC("GetFilename"), &SysPath::GetFilename) + .Func< SysPath & (SysPath::*)(CSStr) >(_SC("SetBasename"), &SysPath::SetBasename) + .Func(_SC("GetBasename"), &SysPath::GetBasename) + .Func< SysPath & (SysPath::*)(CSStr) >(_SC("SetExtension"), &SysPath::SetExtension) + .Func(_SC("GetExtension"), &SysPath::GetExtension) + .Func(_SC("Resolve"), &SysPath::Resolve) + // Member Overloads + .Overload< SysPath & (SysPath::*)(CSStr) >(_SC("Assign"), &SysPath::Assign) + .Overload< SysPath & (SysPath::*)(CSStr, Int32) >(_SC("Assign"), &SysPath::Assign) + .Overload< SysPath & (SysPath::*)(CSStr) >(_SC("AssignDir"), &SysPath::AssignDir) + .Overload< SysPath & (SysPath::*)(CSStr, Int32) >(_SC("AssignDir"), &SysPath::AssignDir) + .Overload< SysPath & (SysPath::*)(CSStr) >(_SC("Append"), &SysPath::Append) + .Overload< SysPath & (SysPath::*)(CSStr, Int32) >(_SC("Append"), &SysPath::Append) + .Overload< SysPath & (SysPath::*)(void) >(_SC("MakeAbsolute"), &SysPath::MakeAbsolute) + .Overload< SysPath & (SysPath::*)(const SysPath &) >(_SC("MakeAbsolute"), &SysPath::MakeAbsolute) + // Static Functions + .StaticFunc(_SC("Separator"), &SysPath::Separator) + .StaticFunc(_SC("PathSeparator"), &SysPath::PathSeparator) + .StaticFunc< SysPath (*)(CSStr) >(_SC("Expand"), &SysPath::Expand) + .StaticFunc(_SC("Home"), &SysPath::Home) + .StaticFunc(_SC("ConfigHome"), &SysPath::ConfigHome) + .StaticFunc(_SC("DataHome"), &SysPath::DataHome) + .StaticFunc(_SC("TempHome"), &SysPath::TempHome) + .StaticFunc(_SC("CacheHome"), &SysPath::CacheHome) + .StaticFunc(_SC("Current"), &SysPath::Working) + .StaticFunc(_SC("Working"), &SysPath::Working) + .StaticFunc(_SC("Temp"), &SysPath::Temp) + .StaticFunc(_SC("Config"), &SysPath::Config) + .StaticFunc(_SC("System"), &SysPath::System) + .StaticFunc(_SC("Null"), &SysPath::Null) + .StaticFunc(_SC("With"), &SysPath::With) + // Static Overloads + .StaticOverload< SysPath (*)(CSStr) >(_SC("ForDir"), &SysPath::ForDirectory) + .StaticOverload< SysPath (*)(CSStr, Int32) >(_SC("ForDir"), &SysPath::ForDirectory) + .StaticOverload< SysPath (*)(CSStr) >(_SC("ForDirectory"), &SysPath::ForDirectory) + .StaticOverload< SysPath (*)(CSStr, Int32) >(_SC("ForDirectory"), &SysPath::ForDirectory) + ); + + ConstTable(vm).Enum(_SC("SqSysPathStyle"), Enumeration(vm) + .Const(_SC("Unix"), static_cast< Int32 >(SysPath::Style::Unix)) + .Const(_SC("Windows"), static_cast< Int32 >(SysPath::Style::Windows)) + .Const(_SC("Native"), static_cast< Int32 >(SysPath::Style::Native)) + .Const(_SC("Guess"), static_cast< Int32 >(SysPath::Style::Guess)) + .Const(_SC("Dynamic"), static_cast< Int32 >(SysPath::Style::Dynamic)) + ); +} + +} // Namespace:: SqMod diff --git a/source/Library/SysPath.hpp b/source/Library/SysPath.hpp index e69de29b..9b6fad5d 100644 --- a/source/Library/SysPath.hpp +++ b/source/Library/SysPath.hpp @@ -0,0 +1,729 @@ +#ifndef _LIBRARY_SYSPATH_HPP_ +#define _LIBRARY_SYSPATH_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "Base/Shared.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * This class represents filesystem paths in a platform-independent manner. +*/ +class SysPath +{ +public: + + // -------------------------------------------------------------------------------------------- + typedef std::vector< String > StrVec; // Directory list. + + /* -------------------------------------------------------------------------------------------- + * Styles of directories to expect when parsing or to export. + */ + enum struct Style + { + Unix = 0, + Windows, + Native, + Guess, + Dynamic + }; + + /* -------------------------------------------------------------------------------------------- + * Creates an empty relative path. + */ + SysPath(); + + /* -------------------------------------------------------------------------------------------- + * Creates an empty absolute or relative path. + */ + SysPath(bool absolute); + + /* -------------------------------------------------------------------------------------------- + * Creates a path in native format from a string. + */ + SysPath(CSStr path); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a string. + */ + SysPath(CSStr path, Int32 style); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a string. + */ + SysPath(CSStr path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Creates a path in native format from a string. + */ + SysPath(const Buffer & path, Int32 size = -1); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a string. + */ + SysPath(const Buffer & path, Style style, Int32 size = -1); + + /* -------------------------------------------------------------------------------------------- + * Creates a path in native format from a string. + */ + SysPath(const String & path); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a string. + */ + SysPath(const String & path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a parent path and a file name. The parent path is expected to reference + * a directory. + */ + SysPath(const SysPath & parent, CSStr name); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a parent path and a file name. The parent path is expected to reference + * a directory. + */ + SysPath(const SysPath & parent, const String & name); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a parent path and a relative path. The parent path is expected + * to reference a directory. The relative path is appended to the parent path. + */ + SysPath(const SysPath & parent, const SysPath & relative); + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + SysPath(const SysPath & o); + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + SysPath(SysPath && o); + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~SysPath(); + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator. + */ + SysPath & operator = (const SysPath & o); + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + SysPath & operator = (SysPath && o); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path in native format. + */ + SysPath & operator = (CSStr path); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path in native format. + */ + SysPath & operator = (const String & path); + + /* -------------------------------------------------------------------------------------------- + * Equality comparison. + */ + bool operator == (const SysPath & o) const; + + /* -------------------------------------------------------------------------------------------- + * Inequality comparison. + */ + bool operator != (const SysPath & o) const; + + /* -------------------------------------------------------------------------------------------- + * Implicit conversion to boolean operator. + */ + operator bool () const; + + /* -------------------------------------------------------------------------------------------- + * Returns the n'th directory in the directory list. If n == depth(), returns the file name. + */ + const String & operator [] (Uint32 n) const; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to compare two instances of this type. + */ + Int32 Cmp(const SysPath & o) const; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + Object ToString() const; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to retrieve the name from instances of this type. + */ + static SQInteger Typename(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * Swaps the path with another one. + */ + void Swap(SysPath & path); + + /* -------------------------------------------------------------------------------------------- + * Clears all components. + */ + void Clear(); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path in native format. + */ + SysPath & Assign(CSStr path); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path. + */ + SysPath & Assign(CSStr path, Int32 style); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path. + */ + SysPath & Assign(CSStr path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path in native format. + */ + SysPath & Assign(const Buffer & path, Int32 size = -1); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path. + */ + SysPath & Assign(const Buffer & path, Style style, Int32 size = -1); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path in native format. + */ + SysPath & Assign(const String & path); + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path. + */ + SysPath & Assign(const String & path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a parent path and a file name. The parent path is expected to reference + * a directory. + */ + SysPath & Assign(const SysPath & parent, CSStr name); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a parent path and a file name. The parent path is expected to reference + * a directory. + */ + SysPath & Assign(const SysPath & parent, const String & name); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a parent path and a relative path. The parent path is expected + * to reference a directory. The relative path is appended to the parent path. + */ + SysPath & Assign(const SysPath & parent, const SysPath & relative); + + /* -------------------------------------------------------------------------------------------- + * Copy the components from another path. + */ + SysPath & Assign(const SysPath & path); + + /* -------------------------------------------------------------------------------------------- + * Move the components of another path into this instance. + */ + SysPath & Assign(SysPath && path); + + /* -------------------------------------------------------------------------------------------- + * The resulting path always refers to a directory and the filename part is empty. + */ + SysPath & AssignDir(CSStr path); + + /* -------------------------------------------------------------------------------------------- + * The resulting path always refers to a directory and the filename part is empty. + */ + SysPath & AssignDir(CSStr path, Int32 style); + + /* -------------------------------------------------------------------------------------------- + * The resulting path always refers to a directory and the filename part is empty. + */ + SysPath & AssignDir(CSStr path, Style style); + + /* -------------------------------------------------------------------------------------------- + * The resulting path always refers to a directory and the filename part is empty. + */ + SysPath & AssignDir(const Buffer & path, Int32 size = -1); + + /* -------------------------------------------------------------------------------------------- + * The resulting path always refers to a directory and the filename part is empty. + */ + SysPath & AssignDir(const Buffer & path, Style style, Int32 size = -1); + + /* -------------------------------------------------------------------------------------------- + * The resulting path always refers to a directory and the filename part is empty. + */ + SysPath & AssignDir(const String & path); + + /* -------------------------------------------------------------------------------------------- + * The resulting path always refers to a directory and the filename part is empty. + */ + SysPath & AssignDir(const String & path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Returns a string containing the path in native format. + */ + Buffer ToBuffer() const; + + /* -------------------------------------------------------------------------------------------- + * Returns a string containing the path in the given format. + */ + Buffer ToBuffer(Style style) const; + + /* -------------------------------------------------------------------------------------------- + * Returns a string containing the path in the given format. + */ + Object ToStr(Int32 style) const; + + /* -------------------------------------------------------------------------------------------- + * Assigns a string containing a path. + */ + void FromString(CSStr path); + + /* -------------------------------------------------------------------------------------------- + * See whether the path is absolute. + */ + bool IsAbsolute() const + { + return m_Absolute; + } + + /* -------------------------------------------------------------------------------------------- + * See whether the path is relative. + */ + bool IsRelative() const + { + return !m_Absolute; + } + + /* -------------------------------------------------------------------------------------------- + * See whether the path references a directory. + */ + bool IsDirectory() const + { + return m_Name.empty(); + } + + /* -------------------------------------------------------------------------------------------- + * See whether the path references a file. + */ + bool IsFile() const + { + return !m_Name.empty(); + } + + /* -------------------------------------------------------------------------------------------- + * See whether the path Does not contain a drive, directories or file name. + */ + bool Empty() const + { + return (!m_Dirs.empty() || !m_Name.empty() || m_Drive != 0); + } + + /* -------------------------------------------------------------------------------------------- + * If the path contains a file name, the file name is appended to the directory list and cleared. + */ + SysPath & MakeDirectory(); + + /* -------------------------------------------------------------------------------------------- + * If the path contains no file name, the last directory becomes the file name. + */ + SysPath & MakeFile(); + + /* -------------------------------------------------------------------------------------------- + * Makes the path refer to its parent. + */ + SysPath & MakeParent(); + + /* -------------------------------------------------------------------------------------------- + * Makes the path absolute if it is relative. The current working directory is taken + * as base directory. + */ + SysPath & MakeAbsolute(); + + /* -------------------------------------------------------------------------------------------- + * Makes the path absolute if it is relative. The given path is taken as base. + */ + SysPath & MakeAbsolute(const SysPath & base); + + /* -------------------------------------------------------------------------------------------- + * Makes the path absolute if it is relative. The given path is taken as base. + */ + SysPath & MakeAbsolute(SysPath && base); + + /* -------------------------------------------------------------------------------------------- + * Appends the given path. + */ + SysPath & Append(const SysPath & path); + + /* -------------------------------------------------------------------------------------------- + * Appends the given path. + */ + SysPath & Append(SysPath && path); + + /* -------------------------------------------------------------------------------------------- + * Parse the given string and append the resulted path. + */ + SysPath & Append(CSStr path); + + /* -------------------------------------------------------------------------------------------- + * Parse the given string and append the resulted path. + */ + SysPath & Append(CSStr path, Int32 style); + + /* -------------------------------------------------------------------------------------------- + * Parse the given string and append the resulted path. + */ + SysPath & Append(CSStr path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Parse the given string and append the resulted path. + */ + SysPath & Append(const Buffer & path, Int32 size = -1); + + /* -------------------------------------------------------------------------------------------- + * Parse the given string and append the resulted path. + */ + SysPath & Append(const Buffer & path, Style style, Int32 size = -1); + + /* -------------------------------------------------------------------------------------------- + * Parse the given string and append the resulted path. + */ + SysPath & Append(const String & path); + + /* -------------------------------------------------------------------------------------------- + * Parse the given string and append the resulted path. + */ + SysPath & Append(const String & path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Returns the drive letter. + */ + CharT GetDrive() const + { + return m_Drive; + } + + /* -------------------------------------------------------------------------------------------- + * Modifies the drive letter. + */ + void SetDrive(CharT drive) + { + m_Drive = drive; + } + + /* -------------------------------------------------------------------------------------------- + * Returns the number of directories in the directory list. + */ + Uint32 Depth() const + { + return m_Dirs.size(); + } + + /* -------------------------------------------------------------------------------------------- + * Returns the n'th directory in the directory list. If n == depth(), returns the file name. + */ + const String & Directory(Uint32 n) const; + + /* -------------------------------------------------------------------------------------------- + * Adds a directory to the directory list. + */ + SysPath & Push(CSStr dir); + + /* -------------------------------------------------------------------------------------------- + * Adds a directory to the directory list. + */ + SysPath & Push(const String & dir); + + /* -------------------------------------------------------------------------------------------- + * Adds a directory to the directory list. + */ + SysPath & Push(String && dir); + + /* -------------------------------------------------------------------------------------------- + * Removes the last directory from the directory list. + */ + SysPath & PopBack(); + + /* -------------------------------------------------------------------------------------------- + * Removes the first directory from the directory list. + */ + SysPath & PopFront(); + + /* -------------------------------------------------------------------------------------------- + * Set the specified file name. + */ + SysPath & SetFilename(CSStr name); + + /* -------------------------------------------------------------------------------------------- + * Set the specified file name. + */ + SysPath & SetFilename(const String & name); + + /* -------------------------------------------------------------------------------------------- + * Set the specified file name. + */ + SysPath & SetFilename(String && name); + + /* -------------------------------------------------------------------------------------------- + * Retrieves the file name. + */ + const String & GetFilename() const + { + return m_Name; + } + + /* -------------------------------------------------------------------------------------------- + * Set the specified file name. + */ + void SqSetFilename(CSStr name) + { + SetFilename(name); + } + + /* -------------------------------------------------------------------------------------------- + * Sets the basename part of the file name and does not change the extension. + */ + SysPath & SetBasename(CSStr name); + + /* -------------------------------------------------------------------------------------------- + * Sets the basename part of the file name and does not change the extension. + */ + SysPath & SetBasename(const String & name); + + /* -------------------------------------------------------------------------------------------- + * Sets the basename part of the file name and does not change the extension. + */ + SysPath & SetBasename(String && name); + + /* -------------------------------------------------------------------------------------------- + * Returns the basename (the file name without extension) of the path. + */ + String GetBasename() const; + + /* -------------------------------------------------------------------------------------------- + * Sets the basename part of the file name and does not change the extension. + */ + void SqSetBasename(CSStr name) + { + SetBasename(name); + } + + /* -------------------------------------------------------------------------------------------- + * Sets the file name extension. + */ + SysPath & SetExtension(CSStr ext); + + /* -------------------------------------------------------------------------------------------- + * Sets the file name extension. + */ + SysPath & SetExtension(const String & ext); + + /* -------------------------------------------------------------------------------------------- + * Returns the file name extension. + */ + String GetExtension() const; + + /* -------------------------------------------------------------------------------------------- + * Sets the file name extension. + */ + void SqSetExtension(CSStr ext) + { + SetExtension(ext); + } + + /* -------------------------------------------------------------------------------------------- + * Returns a pointer to the internal name string where the extension starts. + */ + CSStr GetExtensionC() const; + + /* -------------------------------------------------------------------------------------------- + * Returns a path referring to the path's directory. + */ + SysPath Parent() const; + + /* -------------------------------------------------------------------------------------------- + * Resolves the given path against the current one. If the given path is absolute, it replaces + * the current one. Otherwise, the relative path is appended to the current path. + */ + SysPath & Resolve(const SysPath & path); + +protected: + + /* -------------------------------------------------------------------------------------------- + * Parse a path using the unix standards. + */ + void ParseUnix(CSStr pos, CSStr end); + + /* -------------------------------------------------------------------------------------------- + * Parse a path using the windows standards. + */ + void ParseWindows(CSStr pos, CSStr end); + + /* -------------------------------------------------------------------------------------------- + * Parse a path and expect combined windows and unix styles. + */ + void ParseDynamic(CSStr pos, CSStr end); + + /* -------------------------------------------------------------------------------------------- + * Parse a path and try to detect it's type automatically. + */ + void ParseGuess(CSStr pos, CSStr end); + + /* -------------------------------------------------------------------------------------------- + * Build a path string using the Unix conventions. + */ + Buffer BuildUnix() const; + + /* -------------------------------------------------------------------------------------------- + * Build a path string using the Windows conventions. + */ + Buffer BuildWindows() const; + +private: + + // -------------------------------------------------------------------------------------------- + StrVec m_Dirs; /* The list of directories that form the path. */ + String m_Name; /* The file name if one was specified. */ + CharT m_Drive; /* The drive letter if one was specified. */ + bool m_Absolute; /* Whether this path is an absolute path. */ + +public: + + /* -------------------------------------------------------------------------------------------- + * Returns the platform's path name separator, which separates the components (names) in a path. + */ + static CharT Separator() + { +#ifdef GMOD_OS_WINDOWS + return '\\'; +#else + return '/'; +#endif // GMOD_OS_WINDOWS + } + + /* -------------------------------------------------------------------------------------------- + * Returns the platform's path separator, which separates single paths in a list of paths. + */ + static CharT PathSeparator() + { +#ifdef GMOD_OS_WINDOWS + return ';'; +#else + return ':'; +#endif // GMOD_OS_WINDOWS + } + + /* -------------------------------------------------------------------------------------------- + * Creates a path referring to a directory. + */ + static SysPath ForDirectory(CSStr path); + + /* -------------------------------------------------------------------------------------------- + * Creates a path referring to a directory. + */ + static SysPath ForDirectory(CSStr path, Int32 style); + + /* -------------------------------------------------------------------------------------------- + * Creates a path referring to a directory. + */ + static SysPath ForDirectory(CSStr path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Creates a path referring to a directory. + */ + static SysPath ForDirectory(const String & path); + + /* -------------------------------------------------------------------------------------------- + * Creates a path referring to a directory. + */ + static SysPath ForDirectory(const String & path, Style style); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the path. On Unix, a tilde as first character + * in the path is replaced with the path to user's home directory. + */ + static SysPath Expand(CSStr path); + + /* -------------------------------------------------------------------------------------------- + * Expands all environment variables contained in the path. + */ + static SysPath Expand(const String & path) + { + return Expand(path.c_str()); + } + + /* -------------------------------------------------------------------------------------------- + * Returns the user's home directory. + */ + static SysPath Home(); + + /* -------------------------------------------------------------------------------------------- + * Returns the user's config directory. + */ + static SysPath ConfigHome(); + + /* -------------------------------------------------------------------------------------------- + * Returns the user's data directory. + */ + static SysPath DataHome(); + + /* -------------------------------------------------------------------------------------------- + * Returns the user's temp directory. + */ + static SysPath TempHome(); + + /* -------------------------------------------------------------------------------------------- + * Returns the user's temp directory. + */ + static SysPath CacheHome(); + + /* -------------------------------------------------------------------------------------------- + * Returns the current working directory. + */ + static SysPath Working(); + + /* -------------------------------------------------------------------------------------------- + * Returns the temporary directory. + */ + static SysPath Temp(); + + /* -------------------------------------------------------------------------------------------- + * Returns the systemwide config directory. + */ + static SysPath Config(); + + /* -------------------------------------------------------------------------------------------- + * Returns the system directory. + */ + static SysPath System(); + + /* -------------------------------------------------------------------------------------------- + * Returns the name of the null device. + */ + static SysPath Null(); + + /* -------------------------------------------------------------------------------------------- + * Creates a path from a parent path and a file name. The parent path is expected to reference + * a directory. + */ + static SysPath With(const SysPath & parent, CSStr name); + +}; + +} // Namespace:: SqMod + +#endif // _LIBRARY_SYSPATH_HPP_ diff --git a/source/Library/System.cpp b/source/Library/System.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/source/Library/System.hpp b/source/Library/System.hpp deleted file mode 100644 index e69de29b..00000000 diff --git a/source/Register.cpp b/source/Register.cpp index 74d48ae9..e82389cf 100644 --- a/source/Register.cpp +++ b/source/Register.cpp @@ -38,6 +38,8 @@ extern void Register_Crypt(HSQUIRRELVM vm); extern void Register_Numeric(HSQUIRRELVM vm); extern void Register_Random(HSQUIRRELVM vm); extern void Register_String(HSQUIRRELVM vm); +extern void Register_SysEnv(HSQUIRRELVM vm); +extern void Register_SysPath(HSQUIRRELVM vm); extern void Register_Time(HSQUIRRELVM vm); // ------------------------------------------------------------------------------------------------ @@ -80,6 +82,8 @@ bool RegisterAPI(HSQUIRRELVM vm) Register_Random(vm); Register_Numeric(vm); Register_String(vm); + Register_SysEnv(vm); + Register_SysPath(vm); Register_Time(vm); Register_Constants(vm);