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);