├── .gitignore ├── getiptables.exe ├── getiptables-win.exe ├── network ├── network_init.h └── network_init.cpp ├── utf8 ├── utf8_mixed_var.h ├── utf8_util.h ├── utf8_file_dir.h ├── utf8_util.cpp └── utf8_file_dir.cpp ├── templates ├── static_vector.h └── static_mixed_var.h ├── README.md ├── json ├── json_serializer.h └── json_serializer.cpp └── getiptables.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore debugging and intermediate files. 2 | *.obj 3 | getiptables/* 4 | -------------------------------------------------------------------------------- /getiptables.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubiclesoft/getiptables-windows/master/getiptables.exe -------------------------------------------------------------------------------- /getiptables-win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubiclesoft/getiptables-windows/master/getiptables-win.exe -------------------------------------------------------------------------------- /network/network_init.h: -------------------------------------------------------------------------------- 1 | // Cross-platform network initialization wrapper. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_NETWORK_INIT 5 | #define CUBICLESOFT_NETWORK_INIT 6 | 7 | namespace CubicleSoft 8 | { 9 | namespace Network 10 | { 11 | class Init 12 | { 13 | public: 14 | Init(); 15 | ~Init(); 16 | 17 | inline bool Started() { return MxStarted; } 18 | 19 | private: 20 | bool MxStarted; 21 | }; 22 | } 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /network/network_init.cpp: -------------------------------------------------------------------------------- 1 | // Cross-platform network initialization wrapper. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #define _CRT_SECURE_NO_WARNINGS 5 | 6 | #include "network_init.h" 7 | 8 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 9 | #include 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | namespace CubicleSoft 16 | { 17 | namespace Network 18 | { 19 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 20 | Init::Init() : MxStarted(false) 21 | { 22 | WSADATA WSAData; 23 | if (::WSAStartup(MAKEWORD(2, 2), &WSAData)) return; 24 | if (LOBYTE(WSAData.wVersion) != 2 || HIBYTE(WSAData.wVersion) != 2) return; 25 | 26 | MxStarted = true; 27 | } 28 | 29 | Init::~Init() 30 | { 31 | if (MxStarted) ::WSACleanup(); 32 | } 33 | #else 34 | Init::Init() : MxStarted(false) 35 | { 36 | // Ignore SIGPIPE signals from unexpected pipe disconnects such as a remote socket closing. 37 | void (*handler)(int); 38 | handler = signal(SIGPIPE, SIG_IGN); 39 | if (handler != SIG_DFL) signal(SIGPIPE, handler); 40 | 41 | MxStarted = true; 42 | } 43 | 44 | Init::~Init() 45 | { 46 | // Restore the original SIGPIPE handler. 47 | void (*handler)(int); 48 | handler = signal(SIGPIPE, SIG_DFL); 49 | if (handler != SIG_IGN) signal(SIGPIPE, handler); 50 | } 51 | #endif 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /utf8/utf8_mixed_var.h: -------------------------------------------------------------------------------- 1 | // Adds UTF8 support to the MixedVar template. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_UTF8_MIXED_VAR 5 | #define CUBICLESOFT_UTF8_MIXED_VAR 6 | 7 | #include "../templates/static_mixed_var.h" 8 | #include "utf8_util.h" 9 | 10 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 11 | #include 12 | #else 13 | #include 14 | 15 | #ifndef WCHAR 16 | #define WCHAR wchar_t 17 | #endif 18 | #endif 19 | 20 | namespace CubicleSoft 21 | { 22 | namespace UTF8 23 | { 24 | // Must be used like: UTF8MixedVar 25 | // Designed to be extended but not overridden. 26 | template 27 | class UTF8MixedVar : public StaticMixedVar 28 | { 29 | public: 30 | void SetUTF8(const WCHAR *src, size_t srcsize) 31 | { 32 | this->MxMode = MV_Str; 33 | this->MxStrPos = sizeof(this->MxStr); 34 | Util::ConvertToUTF8(src, srcsize, sizeof(WCHAR), (std::uint8_t *)this->MxStr, this->MxStrPos); 35 | if (this->MxStrPos == sizeof(this->MxStr) || (this->MxStrPos && this->MxStr[this->MxStrPos - 1] == '\0')) this->MxStrPos--; 36 | this->MxStr[this->MxStrPos] = '\0'; 37 | } 38 | 39 | inline void SetUTF8(const WCHAR *src) 40 | { 41 | SetUTF8(src, wcslen(src)); 42 | } 43 | 44 | void ConvertFromUTF8(WCHAR *DestData, size_t &DestDataSize) 45 | { 46 | Util::ConvertFromUTF8((std::uint8_t *)this->MxStr, this->MxStrPos + 1, DestData, DestDataSize, sizeof(WCHAR)); 47 | } 48 | 49 | void AppendUTF8(const WCHAR *src) 50 | { 51 | size_t srcsize = wcslen(src); 52 | if (srcsize > sizeof(this->MxStr) - this->MxStrPos) srcsize = sizeof(this->MxStr) - this->MxStrPos; 53 | Util::ConvertToUTF8(src, srcsize, sizeof(WCHAR), (std::uint8_t *)this->MxStr + this->MxStrPos, srcsize); 54 | this->MxStrPos += srcsize; 55 | if (this->MxStrPos == sizeof(this->MxStr) || (this->MxStrPos && this->MxStr[this->MxStrPos - 1] == '\0')) this->MxStrPos--; 56 | this->MxStr[this->MxStrPos] = '\0'; 57 | } 58 | }; 59 | } 60 | } 61 | 62 | #endif -------------------------------------------------------------------------------- /templates/static_vector.h: -------------------------------------------------------------------------------- 1 | // Statically allocated vector. No resize. 2 | // (C) 2013 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_STATIC_VECTOR 5 | #define CUBICLESOFT_STATIC_VECTOR 6 | 7 | namespace CubicleSoft 8 | { 9 | template 10 | class StaticVector 11 | { 12 | public: 13 | StaticVector(size_t Num) 14 | { 15 | ptr = new T[Num]; 16 | Size = Num; 17 | } 18 | 19 | ~StaticVector() 20 | { 21 | if (ptr != NULL) delete[] ptr; 22 | } 23 | 24 | // Copy constructor. 25 | StaticVector(const StaticVector &TempVector) 26 | { 27 | Size = TempVector.Size; 28 | ptr = new T[Size]; 29 | for (size_t x = 0; x < Size; x++) ptr[x] = TempVector.ptr[x]; 30 | } 31 | 32 | // Assignment operator. 33 | StaticVector &operator=(const StaticVector &TempVector) 34 | { 35 | if (this != &TempVector) 36 | { 37 | if (Size != TempVector.Size) 38 | { 39 | if (ptr != NULL) delete [] ptr; 40 | 41 | Size = TempVector.Size; 42 | if (!Size) ptr = NULL; 43 | else ptr = new T[Size]; 44 | } 45 | 46 | for (size_t x = 0; x < Size; x++) ptr[x] = TempVector.ptr[x]; 47 | } 48 | 49 | return *this; 50 | } 51 | 52 | inline size_t GetSize() const { return Size; } 53 | 54 | // Lets the caller access a specific data element. 55 | // If the position is out of bounds, then epic fail. 56 | inline T &operator[](size_t Pos) { return ptr[Pos]; } 57 | 58 | // Grants the caller access to the underlying raw data. 59 | inline T *RawData() const { return ptr; } 60 | 61 | // Raw comparison of another StaticVector with this StaticVector. 62 | bool operator==(const StaticVector &TempVector) const 63 | { 64 | if (Size != TempVector.Size) return false; 65 | 66 | size_t x; 67 | for (x = 0; x < Size && ptr[x] == TempVector.ptr[x]; x++); 68 | if (x < Size) return false; 69 | 70 | return true; 71 | } 72 | 73 | // Raw comparison of another StaticVector with this StaticVector. 74 | inline bool operator!=(const StaticVector &TempVector) const 75 | { 76 | return !((*this) == TempVector); 77 | } 78 | 79 | private: 80 | T *ptr; 81 | size_t Size; 82 | }; 83 | } 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /utf8/utf8_util.h: -------------------------------------------------------------------------------- 1 | // Cross-platform UTF-8 string conversion and lightweight parser utilities. 2 | // (C) 2016 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_UTF8_UTIL 5 | #define CUBICLESOFT_UTF8_UTIL 6 | 7 | #include 8 | #include 9 | 10 | namespace CubicleSoft 11 | { 12 | namespace UTF8 13 | { 14 | class Util 15 | { 16 | public: 17 | // Converts wide "characters" (Unicode) to and from UTF-8. 18 | // Strict transformation without Unicode normalization. Some stream support. 19 | // Designed for OS/compiler "wide char" to/from UTF-8 transformations. 20 | // For example, SrcWidth and DestWidth might be sizeof(TCHAR) on Windows for a (TCHAR *) string. 21 | 22 | // Generally avoid these for small buffers. Small, exact memory allocations = bad. 23 | // Check out Sync::TLS for a higher-performance allocator. 24 | static char *ConvertToUTF8(const void *SrcData, size_t SrcWidth, size_t *LastPos = NULL, void *AltMallocManager = NULL, void *(*AltMalloc)(void *, size_t) = NULL); 25 | static void *ConvertFromUTF8(const char *SrcData, size_t DestWidth, void *AltMallocManager = NULL, void *(*AltMalloc)(void *, size_t) = NULL); 26 | 27 | // More efficient for small buffers. 28 | static void ConvertToUTF8(const void *SrcData, size_t SrcDataSize, size_t SrcWidth, std::uint8_t *DestData, size_t &DestDataSize, size_t *LastPos = NULL); 29 | static void ConvertFromUTF8(const std::uint8_t *SrcData, size_t SrcDataSize, void *DestData, size_t &DestDataSize, size_t DestWidth); 30 | 31 | 32 | // Very basic parser functions for UTF-8 strings. 33 | // Literally anything beyond this *requires* a (multi-)MB library such as ICU or Boost.Locale. 34 | // Unicode strings are ideally treated as purely opaque within an application. 35 | // Realistically, some parsing will still take place. 36 | 37 | // Does not count combining code points (good) but not all grapheme clusters such as Hangul (not as good). 38 | static size_t strlen(const char *Str); 39 | 40 | static bool FindCodePoint(size_t &ResultPos, std::uint32_t CodePoint, const char *Str, bool AllowCombiningCP = false); 41 | 42 | // Returns false on invalid characters. Use ConvertToUTF8 to clean up strings. 43 | // NOTE: Pos must be initialized to 0 for the first call or the function will assume a continuation. 44 | static bool NextCodePoint(std::uint32_t &ResultCP, std::uint32_t &NextCP, const char *Str, size_t &Pos, size_t &Size, bool &HasCombiningCP); 45 | 46 | // Finds strict ASCII characters <= 0x7F (no combining code points). 47 | // NOTE: Pos must be initialized to 0 for the first call or the function will assume a continuation. 48 | static bool NextASCIICodePoint(char &ResultCP, std::uint32_t &NextCP, const char *Str, size_t &Pos, size_t &Size); 49 | 50 | static bool IsCombiningCodePoint(std::uint32_t CodePoint); 51 | 52 | private: 53 | static bool AppendUTF8CodePoint(std::uint32_t TempCP, std::uint8_t *DestData, size_t &DestDataSize, size_t MaxDestDataSize); 54 | static bool AppendUTFCodePoint(std::uint32_t TempCP, void *DestData, size_t &DestDataSize, size_t DestWidth, size_t MaxDestDataSize); 55 | }; 56 | } 57 | } 58 | 59 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Get IP Tables Command-Line Utility 2 | ================================== 3 | 4 | Dumps information about Windows TCP/IP and UDP/IP tables (both IPv4 and IPv6) as JSON. Released under a MIT or LGPL license. 5 | 6 | [![Donate](https://cubiclesoft.com/res/donate-shield.png)](https://cubiclesoft.com/donate/) [![Discord](https://img.shields.io/discord/777282089980526602?label=chat&logo=discord)](https://cubiclesoft.com/product-support/github/) 7 | 8 | Features 9 | -------- 10 | 11 | * Command-line action! 12 | * Dumps the results of the GetTcpTable2(), GetTcp6Table2(), GetUdpTable(), and GetUdp6Table() Windows APIs as JSON. Easily consumed by most programming and scripting languages. 13 | * Pre-built binaries using Visual Studio (statically linked C++ runtime, minimal file size of ~117K, direct Win32 API calls). 14 | * Windows subsystem variant. 15 | * Unicode support. 16 | * Has a liberal open source license. MIT or LGPL, your choice. 17 | * Sits on GitHub for all of that pull request and issue tracker goodness to easily submit changes and ideas respectively. 18 | 19 | Useful Information 20 | ------------------ 21 | 22 | Running the command with the `/?` option will display the options: 23 | 24 | ``` 25 | (C) 2021 CubicleSoft. All Rights Reserved. 26 | 27 | Syntax: getiptables.exe [options] 28 | 29 | Options: 30 | /v 31 | Verbose mode. 32 | 33 | /tcponly 34 | Only output TCP table information. 35 | Incompatible with 'udponly'. 36 | 37 | /udponly 38 | Only output UDP table information. 39 | Incompatible with 'tcponly'. 40 | 41 | /state=State 42 | Only output table information for the specified state. 43 | Map only be one of: 44 | CLOSED 45 | LISTEN 46 | SYN-SENT 47 | SYN-RECEIVED 48 | ESTABLISHED 49 | FIN-WAIT-1 50 | FIN-WAIT-2 51 | CLOSE-WAIT 52 | CLOSING 53 | LAST-ACK 54 | TIME-WAIT 55 | DELETE-TCB 56 | 57 | /localip=IPAddr 58 | Only output table information for the specified local IP address. 59 | 60 | /localport=PortNum 61 | Only output table information for the specified local port number. 62 | 63 | /remoteip=IPAddr 64 | Only output table information for the specified remote IP address. 65 | 66 | /remoteport=PortNum 67 | Only output table information for the specified remote port number. 68 | 69 | /sort 70 | Sort the output. 71 | 72 | /file=OutputFile 73 | File to write the JSON output to instead of stdout. 74 | ``` 75 | 76 | Example usage: 77 | 78 | ``` 79 | C:\>getiptables /tcponly /state=ESTABLISHED /localip=127.0.0.1 80 | {"success": true, "tcp4": {"success": true, "info": [{"state": "ESTABLISHED", "local_ip": "127.0.0.1", "local_port": 49767, "remote_ip": "127.0.0.1", "remote_port": 49768, "pid": 704, "offload_state": "InHost"}, 81 | 82 | {"state": "ESTABLISHED", "local_ip": "127.0.0.1", "local_port": 49768, "remote_ip": "127.0.0.1", "remote_port": 49767, "pid": 704, "offload_state": "InHost"}]}, 83 | 84 | "tcp6": {"success": true, "info": []}} 85 | ``` 86 | -------------------------------------------------------------------------------- /json/json_serializer.h: -------------------------------------------------------------------------------- 1 | // Cross-platform JSON serialization class. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_JSON_SERIALIZER 5 | #define CUBICLESOFT_JSON_SERIALIZER 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../templates/static_vector.h" 13 | 14 | namespace CubicleSoft 15 | { 16 | namespace JSON 17 | { 18 | class Serializer 19 | { 20 | public: 21 | enum ModeType 22 | { 23 | ModeRootFirst, 24 | ModeRootNext, 25 | ModeObjectFirst, 26 | ModeObjectNext, 27 | ModeArrayFirst, 28 | ModeArrayNext, 29 | ModeStr 30 | }; 31 | 32 | Serializer(const bool EscapeSlashes = false, const char *KeySplitter = ": ", const char *ValSplitter = ", ", size_t MaxDepth = 512); 33 | 34 | void Reset(); 35 | 36 | void SetBuffer(std::uint8_t *Buffer, size_t BufferSize); 37 | inline std::uint8_t *GetBuffer() const { return MxBuffer; } 38 | inline size_t GetCurrPos() const { return MxBufferPos; } 39 | inline size_t GetBufferSize() const { return MxBufferSize; } 40 | inline void ResetPos() { MxBufferPos = 0; } 41 | 42 | inline ModeType GetCurrMode() { return MxModes[MxModeDepth]; } 43 | 44 | inline void SetEscapeSlashes(const bool EscapeSlashes) { MxEscapeSlashes = EscapeSlashes; } 45 | inline void SetKeySplitter(const char *KeySplitter) { MxKeySplitter = KeySplitter; MxKeySplitterLen = strlen(KeySplitter); } 46 | inline void SetValSplitter(const char *ValSplitter) { MxValSplitter = ValSplitter; MxValSplitterLen = strlen(ValSplitter); } 47 | 48 | inline bool IsKeyRequired() { return (MxModes[MxModeDepth] == ModeObjectFirst || MxModes[MxModeDepth] == ModeObjectNext); } 49 | 50 | bool StartObject(const char *Key = NULL); 51 | bool EndObject(); 52 | 53 | bool StartArray(const char *Key = NULL); 54 | bool EndArray(); 55 | 56 | bool StartStr(const char *Key = NULL); 57 | bool EndStr(); 58 | 59 | bool AppendNull(const char *Key = NULL); 60 | bool AppendBool(const char *Key, const bool Val); 61 | bool AppendInt(const char *Key, const std::int64_t Val); 62 | bool AppendUInt(const char *Key, const std::uint64_t Val); 63 | bool AppendDouble(const char *Key, const double Val, const size_t Precision = 100); 64 | bool AppendStr(const char *Key, const char *Val); 65 | bool AppendStr(const char *Key, const char *Val, const size_t Size); 66 | 67 | inline bool AppendBool(const bool Val) { return AppendBool(NULL, Val); } 68 | inline bool AppendInt(const std::int64_t Val) { return AppendInt(NULL, Val); } 69 | inline bool AppendUInt(const std::uint64_t Val) { return AppendUInt(NULL, Val); } 70 | inline bool AppendDouble(const double Val, const size_t Precision = 16) { return AppendDouble(NULL, Val, Precision); } 71 | inline bool AppendStr(const char *Val) { return AppendStr(NULL, Val); } 72 | inline bool AppendStr(const char *Val, const size_t Size) { return AppendStr(NULL, Val, Size); } 73 | 74 | // Intended for appending arbitrary content (e.g. whitespace). 75 | bool Append(const char *Val, size_t Size); 76 | inline bool Append(const char *Val) { return Append(Val, strlen(Val)); }; 77 | 78 | bool Finish(); 79 | 80 | // Calculate the final size of a string including quotes. 81 | size_t CalculateStrSize(const char *Val, bool AddKeySplitter = false); 82 | size_t CalculateStrSize(const char *Val, size_t Size, bool AddKeySplitter = false); 83 | 84 | private: 85 | bool InternalAppendNextPrefix(const char *Key, size_t ExtraSpace); 86 | void InternalAppendStr(const char *Val); 87 | void InternalAppendStr(const char *Val, size_t Size); 88 | static bool IntToString(char *Result, size_t Size, std::uint64_t Num); 89 | static bool IntToString(char *Result, size_t Size, std::int64_t Num); 90 | 91 | StaticVector MxModes; 92 | size_t MxModeDepth; 93 | 94 | std::uint8_t *MxBuffer; 95 | size_t MxBufferPos, MxBufferSize; 96 | 97 | bool MxEscapeSlashes; 98 | const char *MxKeySplitter, *MxValSplitter; 99 | size_t MxKeySplitterLen, MxValSplitterLen; 100 | }; 101 | } 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /templates/static_mixed_var.h: -------------------------------------------------------------------------------- 1 | // A mixed, flexible variable data type (plain ol' data - POD) with all-public data access. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_STATIC_MIXED_VAR 5 | #define CUBICLESOFT_STATIC_MIXED_VAR 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace CubicleSoft 13 | { 14 | enum StaticMixedVarModes 15 | { 16 | MV_None, 17 | MV_Bool, 18 | MV_Int, 19 | MV_UInt, 20 | MV_Double, 21 | MV_Str 22 | }; 23 | 24 | // Must be used like: StaticMixedVar 25 | // Designed to be extended but not overridden. 26 | template 27 | class StaticMixedVar 28 | { 29 | public: 30 | StaticMixedVarModes MxMode; 31 | std::int64_t MxInt; 32 | double MxDouble; 33 | T MxStr; 34 | size_t MxStrPos; 35 | 36 | StaticMixedVar() : MxMode(MV_None), MxInt(0), MxDouble(0.0), MxStrPos(0) 37 | { 38 | } 39 | 40 | // Some functions for those who prefer member functions over directly accessing raw class data. 41 | inline bool IsNone() 42 | { 43 | return (MxMode == MV_None); 44 | } 45 | 46 | inline bool IsBool() 47 | { 48 | return (MxMode == MV_Bool); 49 | } 50 | 51 | inline bool IsInt() 52 | { 53 | return (MxMode == MV_Int); 54 | } 55 | 56 | inline bool IsUInt() 57 | { 58 | return (MxMode == MV_UInt); 59 | } 60 | 61 | inline bool IsDouble() 62 | { 63 | return (MxMode == MV_Double); 64 | } 65 | 66 | inline bool IsStr() 67 | { 68 | return (MxMode == MV_Str); 69 | } 70 | 71 | inline bool GetBool() 72 | { 73 | return (MxInt != 0); 74 | } 75 | 76 | inline std::int64_t GetInt() 77 | { 78 | return MxInt; 79 | } 80 | 81 | inline std::uint64_t GetUInt() 82 | { 83 | return (std::uint64_t)MxInt; 84 | } 85 | 86 | inline double GetDouble() 87 | { 88 | return MxDouble; 89 | } 90 | 91 | inline char *GetStr() 92 | { 93 | return MxStr; 94 | } 95 | 96 | inline size_t GetSize() 97 | { 98 | return MxStrPos; 99 | } 100 | 101 | inline size_t GetMaxSize() 102 | { 103 | return sizeof(MxStr); 104 | } 105 | 106 | inline void SetBool(const bool newbool) 107 | { 108 | MxMode = MV_Bool; 109 | MxInt = (int)newbool; 110 | } 111 | 112 | inline void SetInt(const std::int64_t newint) 113 | { 114 | MxMode = MV_Int; 115 | MxInt = newint; 116 | } 117 | 118 | inline void SetUInt(const std::uint64_t newint) 119 | { 120 | MxMode = MV_UInt; 121 | MxInt = (std::int64_t)newint; 122 | } 123 | 124 | inline void SetDouble(const double newdouble) 125 | { 126 | MxMode = MV_Double; 127 | MxDouble = newdouble; 128 | } 129 | 130 | void SetStr(const char *str) 131 | { 132 | MxMode = MV_Str; 133 | MxStrPos = 0; 134 | while (MxStrPos < sizeof(MxStr) - 1 && *str) 135 | { 136 | MxStr[MxStrPos++] = *str++; 137 | } 138 | MxStr[MxStrPos] = '\0'; 139 | } 140 | 141 | void SetData(const char *str, size_t size) 142 | { 143 | MxMode = MV_Str; 144 | MxStrPos = 0; 145 | while (MxStrPos < sizeof(MxStr) - 1 && size) 146 | { 147 | MxStr[MxStrPos++] = *str++; 148 | size--; 149 | } 150 | MxStr[MxStrPos] = '\0'; 151 | } 152 | 153 | // Prepend functions only prepend if there is enough space. 154 | void PrependStr(const char *str) 155 | { 156 | size_t y = strlen(str); 157 | if (MxStrPos + y < sizeof(MxStr) - 1) 158 | { 159 | memmove(MxStr + y, MxStr, MxStrPos + 1); 160 | memcpy(MxStr, str, y); 161 | MxStrPos += y; 162 | } 163 | } 164 | 165 | void PrependData(const char *str, size_t size) 166 | { 167 | if (MxStrPos + size < sizeof(MxStr) - 1) 168 | { 169 | memmove(MxStr + size, MxStr, MxStrPos + 1); 170 | memcpy(MxStr, str, size); 171 | MxStrPos += size; 172 | } 173 | } 174 | 175 | void PrependInt(const std::int64_t val, size_t radix = 10) 176 | { 177 | char tempbuffer[44]; 178 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) PrependStr(tempbuffer); 179 | } 180 | 181 | void PrependUInt(const std::uint64_t val, size_t radix = 10) 182 | { 183 | char tempbuffer[44]; 184 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) PrependStr(tempbuffer); 185 | } 186 | 187 | void PrependDouble(const double val, const size_t precision = 16) 188 | { 189 | char tempbuffer[100]; 190 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 191 | _snprintf_s(tempbuffer, sizeof(tempbuffer), _TRUNCATE, "%1.*g", precision, val); 192 | tempbuffer[sizeof(tempbuffer) - 1] = '\0'; 193 | #else 194 | snprintf(tempbuffer, sizeof(tempbuffer), "%1.*g", (int)precision, val); 195 | #endif 196 | 197 | PrependStr(tempbuffer); 198 | } 199 | 200 | void AppendStr(const char *str) 201 | { 202 | while (MxStrPos < sizeof(MxStr) - 1 && *str) 203 | { 204 | MxStr[MxStrPos++] = *str++; 205 | } 206 | MxStr[MxStrPos] = '\0'; 207 | } 208 | 209 | void AppendData(const char *str, size_t size) 210 | { 211 | while (MxStrPos < sizeof(MxStr) - 1 && size) 212 | { 213 | MxStr[MxStrPos++] = *str++; 214 | size--; 215 | } 216 | MxStr[MxStrPos] = '\0'; 217 | } 218 | 219 | void AppendInt(const std::int64_t val, size_t radix = 10) 220 | { 221 | char tempbuffer[44]; 222 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) AppendStr(tempbuffer); 223 | } 224 | 225 | void AppendUInt(const std::uint64_t val, size_t radix = 10) 226 | { 227 | char tempbuffer[44]; 228 | if (IntToString(tempbuffer, sizeof(tempbuffer), val, radix)) AppendStr(tempbuffer); 229 | } 230 | 231 | void AppendDouble(const double val, const size_t precision = 16) 232 | { 233 | char tempbuffer[100]; 234 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 235 | _snprintf_s(tempbuffer, sizeof(tempbuffer), _TRUNCATE, "%1.*g", precision, val); 236 | tempbuffer[sizeof(tempbuffer) - 1] = '\0'; 237 | #else 238 | snprintf(tempbuffer, sizeof(tempbuffer), "%1.*g", (int)precision, val); 239 | #endif 240 | 241 | AppendStr(tempbuffer); 242 | } 243 | 244 | inline void AppendChar(const char chr) 245 | { 246 | if (MxStrPos < sizeof(MxStr) - 1) 247 | { 248 | MxStr[MxStrPos++] = chr; 249 | MxStr[MxStrPos] = '\0'; 250 | } 251 | } 252 | 253 | inline void AppendMissingChar(const char chr) 254 | { 255 | if ((!MxStrPos || MxStr[MxStrPos - 1] != chr) && MxStrPos < sizeof(MxStr) - 1) 256 | { 257 | MxStr[MxStrPos++] = chr; 258 | MxStr[MxStrPos] = '\0'; 259 | } 260 | } 261 | 262 | inline bool RemoveTrailingChar(const char chr) 263 | { 264 | if (!MxStrPos || MxStr[MxStrPos - 1] != chr) return false; 265 | 266 | MxStr[--MxStrPos] = '\0'; 267 | 268 | return true; 269 | } 270 | 271 | inline void SetSize(const size_t size) 272 | { 273 | if (size < sizeof(MxStr)) 274 | { 275 | MxStrPos = size; 276 | MxStr[MxStrPos] = '\0'; 277 | } 278 | } 279 | 280 | // Swiped and slightly modified from Int::ToString(). 281 | static bool IntToString(char *Result, size_t Size, std::uint64_t Num, size_t Radix = 10) 282 | { 283 | if (Size < 2) return false; 284 | 285 | size_t x = Size, z; 286 | 287 | Result[--x] = '\0'; 288 | if (!Num) Result[--x] = '0'; 289 | else 290 | { 291 | while (Num && x) 292 | { 293 | z = Num % Radix; 294 | Result[--x] = (char)(z > 9 ? z - 10 + 'A' : z + '0'); 295 | Num /= Radix; 296 | } 297 | 298 | if (Num) return false; 299 | } 300 | 301 | memmove(Result, Result + x, Size - x); 302 | 303 | return true; 304 | } 305 | 306 | static bool IntToString(char *Result, size_t Size, std::int64_t Num, size_t Radix = 10) 307 | { 308 | if (Num >= 0) return IntToString(Result, Size, (std::uint64_t)Num, Radix); 309 | 310 | if (Size < 2) return false; 311 | Result[0] = '-'; 312 | 313 | return IntToString(Result + 1, Size - 1, (std::uint64_t)-Num, Radix); 314 | } 315 | }; 316 | } 317 | 318 | #endif 319 | -------------------------------------------------------------------------------- /utf8/utf8_file_dir.h: -------------------------------------------------------------------------------- 1 | // Cross-platform UTF-8, large file (> 2GB) and directory manipulation classes. 2 | // (C) 2016 CubicleSoft. All Rights Reserved. 3 | 4 | #ifndef CUBICLESOFT_UTF8_FILE 5 | #define CUBICLESOFT_UTF8_FILE 6 | 7 | #ifndef _LARGEFILE64_SOURCE 8 | #define _LARGEFILE64_SOURCE 9 | #endif 10 | 11 | #include "utf8_util.h" 12 | 13 | #include 14 | #include 15 | 16 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 17 | #include 18 | #include 19 | #include 20 | #else 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifdef __APPLE__ 33 | #define lseek64 lseek 34 | #define open64 open 35 | #define off64_t off_t 36 | #endif 37 | #endif 38 | 39 | #ifndef O_RDONLY 40 | #define O_RDONLY 00 41 | #endif 42 | #ifndef O_WRONLY 43 | #define O_WRONLY 01 44 | #endif 45 | #ifndef O_RDWR 46 | #define O_RDWR 02 47 | #endif 48 | #ifndef O_ACCMODE 49 | #define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) 50 | #endif 51 | 52 | #ifndef O_CREAT 53 | #define O_CREAT 0100 54 | #endif 55 | #ifndef O_EXCL 56 | #define O_EXCL 0200 57 | #endif 58 | #ifndef O_TRUNC 59 | #define O_TRUNC 01000 60 | #endif 61 | #ifndef O_APPEND 62 | #define O_APPEND 02000 63 | #endif 64 | 65 | // O_UNSAFE allows for devices to be opened for raw reading/writing. 66 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 67 | #ifndef O_UNSAFE 68 | #define O_UNSAFE 04000 69 | #endif 70 | #endif 71 | 72 | #ifndef _S_IFLNK 73 | #ifdef S_IFLNK 74 | #define _S_IFLNK S_IFLNK 75 | #else 76 | #define _S_IFLNK 0120000 77 | #endif 78 | #endif 79 | 80 | #ifndef S_ISLNK 81 | #define S_ISLNK(mode) (((mode) & S_IFMT) == _S_IFLNK) 82 | #endif 83 | 84 | 85 | namespace CubicleSoft 86 | { 87 | namespace UTF8 88 | { 89 | class File 90 | { 91 | public: 92 | enum SeekType 93 | { 94 | SeekStart, 95 | SeekEnd, 96 | SeekForward, 97 | SeekBackward 98 | }; 99 | 100 | enum ShareType 101 | { 102 | ShareBoth, 103 | ShareRead, 104 | ShareWrite, 105 | ShareNone 106 | }; 107 | 108 | struct FilenameInfo 109 | { 110 | size_t StartVolume, StartPath, StartFilename, StartExtension, StartLastExtension, Length; 111 | }; 112 | 113 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 114 | typedef struct __stat64 FileStat; 115 | #else 116 | typedef struct stat FileStat; 117 | #endif 118 | 119 | File(); 120 | virtual ~File(); 121 | 122 | virtual bool IsOpen() const; 123 | bool Open(const char *Filename, int Flags, ShareType ShareFlag = ShareBoth, int Mode = 0666); 124 | virtual bool Seek(SeekType whence, std::uint64_t Pos); 125 | virtual bool Read(std::uint8_t *Data, size_t DataSize, size_t &DataRead); 126 | 127 | // Result is not UTF-8 and also not intended for binary data. '\0' replaced with ' ' (space). 128 | // Result will have been terminated by EOF or an ASCII newline '\r', '\n', '\r\n' but trimmed. 129 | // SizeHint helps optimize memory allocation performance. 130 | // Check out Sync::TLS for a high-performance allocator. 131 | virtual char *LineInput(size_t SizeHint = 1024, void *AltMallocManager = NULL, void *(*AltRealloc)(void *, void *, size_t) = NULL); 132 | 133 | virtual bool Write(const char *Data, size_t &DataWritten); 134 | virtual bool Write(const std::uint8_t *Data, size_t DataSize, size_t &DataWritten); 135 | virtual bool Flush(); 136 | inline std::uint64_t GetCurrPos() const { return MxCurrPos; } 137 | inline std::uint64_t GetMaxPos() const { return MxMaxPos; } 138 | virtual bool UpdateMaxPos(); 139 | virtual bool Close(); 140 | 141 | // Some static functions specifically for files. 142 | static bool GetPlatformFilename(char *Result, size_t ResultSize, const char *Filename, bool AllowUnsafe = false); 143 | static bool IsValidFilenameFormat(const char *Filename); 144 | static void GetPlatformFilenameInfo(FilenameInfo &Result, const char *Filename); 145 | static bool GetAbsoluteFilename(char *Result, size_t ResultSize, const char *BaseDir, const char *Filename, bool TrailingSlash = false); 146 | static bool Exists(const char *Filename); 147 | static bool Realpath(char *Result, size_t ResultSize, const char *Filename); 148 | static bool Chmod(const char *Filename, int Mode); 149 | static bool Chown(const char *Filename, const char *Owner); 150 | static bool Chgrp(const char *Filename, const char *Group); 151 | static bool Copy(const char *SrcFilename, const char *DestFilename); 152 | static bool Move(const char *SrcFilename, const char *DestFilename); 153 | static bool Delete(const char *Filename); 154 | static bool Stat(FileStat &Result, const char *Filename, bool Link = false); 155 | static bool Symlink(const char *Src, const char *Dest); 156 | static bool Readlink(char *Result, size_t ResultSize, const char *Filename); 157 | 158 | // For quickly loading relatively small files. 159 | // Check out Sync::TLS for a high-performance allocator. 160 | static bool LoadEntireFile(const char *Filename, char *&Result, size_t &BytesRead, void *AltMallocManager = NULL, void *(*AltMalloc)(void *, size_t) = NULL, void (*AltFree)(void *, void *) = NULL); 161 | 162 | // Timestamps are in Unix microsecond format. 163 | static bool SetFileTimes(const char *Filename, std::uint64_t *Creation, std::uint64_t *LastAccess, std::uint64_t *LastUpdate); 164 | 165 | private: 166 | // Deny copy construction and assignment. Use a (smart) pointer instead. 167 | File(const File &); 168 | File &operator=(const File &); 169 | 170 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 171 | public: 172 | static bool GetWindowsPlatformFilename(LPWSTR Result, size_t ResultSize, const char *Filename, bool AllowUnsafe = false); 173 | static bool GetWindowsFilenameInfo(BY_HANDLE_FILE_INFORMATION *Result, LPWSTR Filename); 174 | private: 175 | static FILETIME ConvertUnixMicrosecondTimeToFILETIME(std::uint64_t TempTime); 176 | static __time64_t ConvertFILETIMEToUnixTime(FILETIME TempTime); 177 | typedef BOOL (APIENTRY *CreateSymbolicLinkWFunc)(LPWSTR, LPWSTR, DWORD); 178 | static bool SetThreadProcessPrivilege(LPCWSTR PrivilegeName, bool Enable); 179 | 180 | HANDLE MxFile; 181 | #else 182 | int MxFile; 183 | bool MxLocked; 184 | #endif 185 | 186 | protected: 187 | bool MxRead, MxWrite; 188 | std::uint64_t MxCurrPos, MxMaxPos; 189 | }; 190 | 191 | class Dir 192 | { 193 | public: 194 | Dir(); 195 | ~Dir(); 196 | 197 | bool Open(const char *Dirname); 198 | bool Read(char *Filename, size_t Size); 199 | bool Close(); 200 | 201 | // Some static functions specifically for directories. Some functions are possibly not thread-safe. 202 | static bool Getcwd(char *Result, size_t ResultSize); 203 | 204 | // Unlike most directory/file calls, UTF8::Dir::Mkdir() is more difficult to use because Dirname must reference an 205 | // absolute path but UTF8::File::Realpath() can't be used on the complete calculation since one or more directories 206 | // likely don't exist (yet). Use UTF8::File::Realpath() to transform the known existing part of the path and then 207 | // carefully concatenate the new path item(s) and call UTF8::Dir::Mkdir(). 208 | static bool Mkdir(const char *Dirname, int Mode = 0777, bool Recursive = false); 209 | 210 | static bool Rmdir(const char *Dirname, bool Recursive = false); 211 | 212 | private: 213 | // Deny copy construction and assignment. Use a (smart) pointer instead. 214 | Dir(const Dir &); 215 | Dir &operator=(const Dir &); 216 | 217 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 218 | static void Rmdir_RecurseInternal(WCHAR *Dirname, size_t Size); 219 | 220 | HANDLE MxDir; 221 | WIN32_FIND_DATAW MxFindData; 222 | #else 223 | static void Rmdir_RecurseInternal(char *Dirname, size_t Size); 224 | 225 | DIR *MxDir; 226 | #endif 227 | }; 228 | } 229 | } 230 | 231 | #endif 232 | -------------------------------------------------------------------------------- /json/json_serializer.cpp: -------------------------------------------------------------------------------- 1 | // Cross-platform JSON serialization class. 2 | // (C) 2021 CubicleSoft. All Rights Reserved. 3 | 4 | #define _CRT_SECURE_NO_WARNINGS 5 | 6 | #include "json_serializer.h" 7 | #include 8 | 9 | namespace CubicleSoft 10 | { 11 | namespace JSON 12 | { 13 | Serializer::Serializer(const bool EscapeSlashes, const char *KeySplitter, const char *ValSplitter, size_t MaxDepth) 14 | : MxModes(MaxDepth), MxModeDepth(0), MxBuffer(NULL), MxBufferPos(0), MxBufferSize(0), MxEscapeSlashes(EscapeSlashes), MxKeySplitter(KeySplitter), MxValSplitter(ValSplitter) 15 | { 16 | MxModes[0] = ModeRootFirst; 17 | MxKeySplitterLen = strlen(KeySplitter); 18 | MxValSplitterLen = strlen(ValSplitter); 19 | } 20 | 21 | void Serializer::Reset() 22 | { 23 | MxModes[0] = ModeRootFirst; 24 | MxModeDepth = 0; 25 | MxBufferPos = 0; 26 | } 27 | 28 | void Serializer::SetBuffer(std::uint8_t *Buffer, size_t BufferSize) 29 | { 30 | MxBuffer = Buffer; 31 | MxBufferPos = 0; 32 | MxBufferSize = BufferSize - 1; 33 | } 34 | 35 | bool Serializer::StartObject(const char *Key) 36 | { 37 | if (MxModeDepth + 1 >= MxModes.GetSize() || !InternalAppendNextPrefix(Key, 1)) return false; 38 | 39 | MxBuffer[MxBufferPos++] = '{'; 40 | MxBuffer[MxBufferPos] = '\0'; 41 | 42 | MxModeDepth++; 43 | MxModes[MxModeDepth] = ModeObjectFirst; 44 | 45 | return true; 46 | } 47 | 48 | bool Serializer::EndObject() 49 | { 50 | if (MxModes[MxModeDepth] != ModeObjectFirst && MxModes[MxModeDepth] != ModeObjectNext) return false; 51 | 52 | if (MxBufferPos + 1 > MxBufferSize) return false; 53 | 54 | MxBuffer[MxBufferPos++] = '}'; 55 | MxBuffer[MxBufferPos] = '\0'; 56 | 57 | MxModeDepth--; 58 | 59 | return true; 60 | } 61 | 62 | bool Serializer::StartArray(const char *Key) 63 | { 64 | if (MxModeDepth + 1 >= MxModes.GetSize() || !InternalAppendNextPrefix(Key, 1)) return false; 65 | 66 | MxBuffer[MxBufferPos++] = '['; 67 | MxBuffer[MxBufferPos] = '\0'; 68 | 69 | MxModeDepth++; 70 | MxModes[MxModeDepth] = ModeArrayFirst; 71 | 72 | return true; 73 | } 74 | 75 | bool Serializer::EndArray() 76 | { 77 | if (MxModes[MxModeDepth] != ModeArrayFirst && MxModes[MxModeDepth] != ModeArrayNext) return false; 78 | 79 | if (MxBufferPos + 1 > MxBufferSize) return false; 80 | 81 | MxBuffer[MxBufferPos++] = ']'; 82 | MxBuffer[MxBufferPos] = '\0'; 83 | 84 | MxModeDepth--; 85 | 86 | return true; 87 | } 88 | 89 | bool Serializer::StartStr(const char *Key) 90 | { 91 | if (MxModeDepth + 1 >= MxModes.GetSize() || !InternalAppendNextPrefix(Key, 1)) return false; 92 | 93 | MxBuffer[MxBufferPos++] = '"'; 94 | MxBuffer[MxBufferPos] = '\0'; 95 | 96 | MxModeDepth++; 97 | MxModes[MxModeDepth] = ModeStr; 98 | 99 | return true; 100 | } 101 | 102 | bool Serializer::EndStr() 103 | { 104 | if (MxModes[MxModeDepth] != ModeStr) return false; 105 | 106 | if (MxBufferPos + 1 > MxBufferSize) return false; 107 | 108 | MxBuffer[MxBufferPos++] = '"'; 109 | MxBuffer[MxBufferPos] = '\0'; 110 | 111 | MxModeDepth--; 112 | 113 | return true; 114 | } 115 | 116 | bool Serializer::AppendNull(const char *Key) 117 | { 118 | if (!InternalAppendNextPrefix(Key, 4)) return false; 119 | 120 | strcpy((char *)MxBuffer + MxBufferPos, "null"); 121 | MxBufferPos += 4; 122 | 123 | return true; 124 | } 125 | 126 | bool Serializer::AppendBool(const char *Key, const bool Val) 127 | { 128 | if (!InternalAppendNextPrefix(Key, 5)) return false; 129 | 130 | if (Val) 131 | { 132 | strcpy((char *)MxBuffer + MxBufferPos, "true"); 133 | MxBufferPos += 4; 134 | } 135 | else 136 | { 137 | strcpy((char *)MxBuffer + MxBufferPos, "false"); 138 | MxBufferPos += 5; 139 | } 140 | 141 | return true; 142 | } 143 | 144 | bool Serializer::AppendInt(const char *Key, const std::int64_t Val) 145 | { 146 | char TempBuffer[44]; 147 | if (!IntToString(TempBuffer, sizeof(TempBuffer), Val)) return false; 148 | 149 | size_t x = strlen(TempBuffer); 150 | if (!InternalAppendNextPrefix(Key, x)) return false; 151 | 152 | strcpy((char *)MxBuffer + MxBufferPos, TempBuffer); 153 | MxBufferPos += x; 154 | 155 | return true; 156 | } 157 | 158 | bool Serializer::AppendUInt(const char *Key, const std::uint64_t Val) 159 | { 160 | char TempBuffer[44]; 161 | if (!IntToString(TempBuffer, sizeof(TempBuffer), Val)) return false; 162 | 163 | size_t x = strlen(TempBuffer); 164 | if (!InternalAppendNextPrefix(Key, x)) return false; 165 | 166 | strcpy((char *)MxBuffer + MxBufferPos, TempBuffer); 167 | MxBufferPos += x; 168 | 169 | return true; 170 | } 171 | 172 | bool Serializer::AppendDouble(const char *Key, const double Val, const size_t Precision) 173 | { 174 | char TempBuffer[100]; 175 | #if (defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) && defined(_MSC_VER) && _MSC_VER < 1900 176 | _snprintf_s(TempBuffer, sizeof(TempBuffer), _TRUNCATE, "%1.*g", Precision, Val); 177 | TempBuffer[sizeof(TempBuffer) - 1] = '\0'; 178 | #else 179 | snprintf(TempBuffer, sizeof(TempBuffer), "%1.*g", (int)Precision, Val); 180 | #endif 181 | 182 | size_t x = strlen(TempBuffer); 183 | if (!InternalAppendNextPrefix(Key, x)) return false; 184 | 185 | strcpy((char *)MxBuffer + MxBufferPos, TempBuffer); 186 | MxBufferPos += x; 187 | 188 | return true; 189 | } 190 | 191 | bool Serializer::AppendStr(const char *Key, const char *Val) 192 | { 193 | if (Val == NULL) return false; 194 | 195 | size_t x = CalculateStrSize(Val, false); 196 | if ((MxModes[MxModeDepth] != ModeStr && !InternalAppendNextPrefix(Key, x)) || (MxModes[MxModeDepth] == ModeStr && MxBufferPos + x > MxBufferSize)) return false; 197 | 198 | InternalAppendStr(Val); 199 | 200 | return true; 201 | } 202 | 203 | bool Serializer::AppendStr(const char *Key, const char *Val, const size_t Size) 204 | { 205 | if (Val == NULL) return false; 206 | 207 | size_t x = CalculateStrSize(Val, Size, false); 208 | if ((MxModes[MxModeDepth] != ModeStr && !InternalAppendNextPrefix(Key, x)) || (MxModes[MxModeDepth] == ModeStr && MxBufferPos + x > MxBufferSize)) return false; 209 | 210 | InternalAppendStr(Val, Size); 211 | 212 | return true; 213 | } 214 | 215 | bool Serializer::Append(const char *Val, size_t Size) 216 | { 217 | if (MxBufferPos + Size > MxBufferSize || Val == NULL) return false; 218 | 219 | while (Size) 220 | { 221 | MxBuffer[MxBufferPos++] = *Val++; 222 | Size--; 223 | } 224 | 225 | MxBuffer[MxBufferPos] = '\0'; 226 | 227 | return true; 228 | } 229 | 230 | bool Serializer::Finish() 231 | { 232 | while (MxModeDepth) 233 | { 234 | if (MxModes[MxModeDepth] == ModeObjectFirst || MxModes[MxModeDepth] == ModeObjectNext) 235 | { 236 | if (!EndObject()) return false; 237 | } 238 | if (MxModes[MxModeDepth] == ModeArrayFirst || MxModes[MxModeDepth] == ModeArrayNext) 239 | { 240 | if (!EndArray()) return false; 241 | } 242 | else if (MxModes[MxModeDepth] == ModeStr) 243 | { 244 | if (!EndStr()) return false; 245 | } 246 | } 247 | 248 | return true; 249 | } 250 | 251 | size_t Serializer::CalculateStrSize(const char *Val, bool AddKeySplitter) 252 | { 253 | size_t Result = (MxModes[MxModeDepth] != ModeStr ? 2 : 0); 254 | 255 | for (; *Val; Val++) 256 | { 257 | switch (*Val) 258 | { 259 | case '"': 260 | case '\\': 261 | case '\b': 262 | case '\f': 263 | case '\n': 264 | case '\r': 265 | case '\t': 266 | { 267 | Result++; 268 | 269 | break; 270 | } 271 | 272 | case '/': 273 | { 274 | if (MxEscapeSlashes) Result++; 275 | 276 | break; 277 | } 278 | } 279 | 280 | Result++; 281 | } 282 | 283 | if (AddKeySplitter) Result += MxKeySplitterLen; 284 | 285 | return Result; 286 | } 287 | 288 | size_t Serializer::CalculateStrSize(const char *Val, size_t Size, bool AddKeySplitter) 289 | { 290 | size_t Result = (MxModes[MxModeDepth] != ModeStr ? 2 : 0); 291 | 292 | while (Size) 293 | { 294 | switch (*Val) 295 | { 296 | case '"': 297 | case '\\': 298 | case '\b': 299 | case '\f': 300 | case '\n': 301 | case '\r': 302 | case '\t': 303 | { 304 | Result++; 305 | 306 | break; 307 | } 308 | 309 | case '/': 310 | { 311 | if (MxEscapeSlashes) Result++; 312 | 313 | break; 314 | } 315 | } 316 | 317 | Result++; 318 | Size--; 319 | } 320 | 321 | if (AddKeySplitter) Result += MxKeySplitterLen; 322 | 323 | return Result; 324 | } 325 | 326 | bool Serializer::InternalAppendNextPrefix(const char *Key, size_t ExtraSpace) 327 | { 328 | if (MxModes[MxModeDepth] == ModeRootNext || MxModes[MxModeDepth] == ModeStr || ((MxModes[MxModeDepth] == ModeObjectFirst || MxModes[MxModeDepth] == ModeObjectNext) && Key == NULL) || ((MxModes[MxModeDepth] == ModeRootFirst || MxModes[MxModeDepth] == ModeArrayFirst || MxModes[MxModeDepth] == ModeArrayNext) && Key != NULL)) return false; 329 | 330 | size_t x = (MxModes[MxModeDepth] == ModeObjectNext || MxModes[MxModeDepth] == ModeArrayNext ? MxValSplitterLen : 0); 331 | if (Key != NULL) x += CalculateStrSize(Key, true); 332 | 333 | if (MxBufferPos + x + ExtraSpace > MxBufferSize) return false; 334 | 335 | if (MxModes[MxModeDepth] == ModeObjectNext || MxModes[MxModeDepth] == ModeArrayNext) Append(MxValSplitter, MxValSplitterLen); 336 | else if (MxModes[MxModeDepth] == ModeObjectFirst) MxModes[MxModeDepth] = ModeObjectNext; 337 | else if (MxModes[MxModeDepth] == ModeArrayFirst) MxModes[MxModeDepth] = ModeArrayNext; 338 | else if (MxModes[MxModeDepth] == ModeRootFirst) MxModes[MxModeDepth] = ModeRootNext; 339 | 340 | if (Key != NULL) 341 | { 342 | InternalAppendStr(Key); 343 | Append(MxKeySplitter, MxKeySplitterLen); 344 | } 345 | 346 | return true; 347 | } 348 | 349 | // All calculations prior to calling these internal functions must be applied correctly to avoid an overflow. 350 | void Serializer::InternalAppendStr(const char *Val) 351 | { 352 | if (MxModes[MxModeDepth] != ModeStr) MxBuffer[MxBufferPos++] = '"'; 353 | 354 | while (*Val) 355 | { 356 | switch (*Val) 357 | { 358 | case '"': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = '"'; Val++; break; 359 | case '\\': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = '\\'; Val++; break; 360 | case '/': if (MxEscapeSlashes) { MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = '/'; } Val++; break; 361 | case '\b': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 'b'; Val++; break; 362 | case '\f': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 'f'; Val++; break; 363 | case '\n': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 'n'; Val++; break; 364 | case '\r': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 'r'; Val++; break; 365 | case '\t': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 't'; Val++; break; 366 | default: MxBuffer[MxBufferPos++] = *Val++; break; 367 | } 368 | } 369 | 370 | if (MxModes[MxModeDepth] != ModeStr) MxBuffer[MxBufferPos++] = '"'; 371 | MxBuffer[MxBufferPos] = '\0'; 372 | } 373 | 374 | void Serializer::InternalAppendStr(const char *Val, size_t Size) 375 | { 376 | if (MxModes[MxModeDepth] != ModeStr) MxBuffer[MxBufferPos++] = '"'; 377 | 378 | while (Size) 379 | { 380 | switch (*Val) 381 | { 382 | case '"': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = '"'; Val++; break; 383 | case '\\': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = '\\'; Val++; break; 384 | case '/': if (MxEscapeSlashes) { MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = '/'; } Val++; break; 385 | case '\b': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 'b'; Val++; break; 386 | case '\f': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 'f'; Val++; break; 387 | case '\n': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 'n'; Val++; break; 388 | case '\r': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 'r'; Val++; break; 389 | case '\t': MxBuffer[MxBufferPos++] = '\\'; MxBuffer[MxBufferPos++] = 't'; Val++; break; 390 | default: MxBuffer[MxBufferPos++] = *Val++; break; 391 | } 392 | 393 | Size--; 394 | } 395 | 396 | if (MxModes[MxModeDepth] != ModeStr) MxBuffer[MxBufferPos++] = '"'; 397 | MxBuffer[MxBufferPos] = '\0'; 398 | } 399 | 400 | // Swiped and slightly modified from Int::ToString(). 401 | bool Serializer::IntToString(char *Result, size_t Size, std::uint64_t Num) 402 | { 403 | if (Size < 2) return false; 404 | 405 | size_t x = Size; 406 | 407 | Result[--x] = '\0'; 408 | if (!Num) Result[--x] = '0'; 409 | else 410 | { 411 | while (Num && x) 412 | { 413 | Result[--x] = (char)(Num % 10) + '0'; 414 | Num /= 10; 415 | } 416 | 417 | if (Num) return false; 418 | } 419 | 420 | memmove(Result, Result + x, Size - x); 421 | 422 | return true; 423 | } 424 | 425 | bool Serializer::IntToString(char *Result, size_t Size, std::int64_t Num) 426 | { 427 | if (Num >= 0) return IntToString(Result, Size, (std::uint64_t)Num); 428 | 429 | if (Size < 2) return false; 430 | Result[0] = '-'; 431 | 432 | return IntToString(Result + 1, Size - 1, (std::uint64_t)-Num); 433 | } 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /utf8/utf8_util.cpp: -------------------------------------------------------------------------------- 1 | // Cross-platform UTF-8 string conversion and lightweight parser. 2 | // (C) 2013 CubicleSoft. All Rights Reserved. 3 | 4 | #include "utf8_util.h" 5 | 6 | namespace CubicleSoft 7 | { 8 | namespace UTF8 9 | { 10 | char *Util::ConvertToUTF8(const void *SrcData, size_t SrcWidth, size_t *LastPos, void *AltMallocManager, void *(*AltMalloc)(void *, size_t)) 11 | { 12 | size_t x = 0, y; 13 | std::uint8_t *DestData; 14 | 15 | if (SrcWidth == 1) 16 | { 17 | const std::uint8_t *SrcData2 = (const std::uint8_t *)SrcData; 18 | 19 | do 20 | { 21 | x++; 22 | } while (*SrcData2++); 23 | } 24 | else if (SrcWidth == 2) 25 | { 26 | const uint16_t *SrcData2 = (const uint16_t *)SrcData; 27 | 28 | do 29 | { 30 | x++; 31 | } while (*SrcData2++); 32 | } 33 | else if (SrcWidth == 4) 34 | { 35 | const std::uint32_t *SrcData2 = (const std::uint32_t *)SrcData; 36 | 37 | do 38 | { 39 | x++; 40 | } while (*SrcData2++); 41 | } 42 | else return NULL; 43 | 44 | ConvertToUTF8(SrcData, x, SrcWidth, NULL, y, NULL); 45 | DestData = (AltMalloc != NULL ? (std::uint8_t *)AltMalloc(AltMallocManager, y) : new std::uint8_t[y]); 46 | ConvertToUTF8(SrcData, x, SrcWidth, DestData, y, LastPos); 47 | 48 | return (char *)DestData; 49 | } 50 | 51 | void *Util::ConvertFromUTF8(const char *SrcData, size_t DestWidth, void *AltMallocManager, void *(*AltMalloc)(void *, size_t)) 52 | { 53 | size_t x, y; 54 | void *DestData; 55 | 56 | x = strlen(SrcData); 57 | ConvertFromUTF8((const std::uint8_t *)SrcData, x, NULL, y, DestWidth); 58 | if (DestWidth == 1) DestData = (AltMalloc != NULL ? (std::uint8_t *)AltMalloc(AltMallocManager, y) : new std::uint8_t[y]); 59 | else if (DestWidth == 2) DestData = (AltMalloc != NULL ? (std::uint16_t *)AltMalloc(AltMallocManager, y * sizeof(uint16_t)) : new uint16_t[y]); 60 | else if (DestWidth == 4) DestData = (AltMalloc != NULL ? (std::uint32_t *)AltMalloc(AltMallocManager, y * sizeof(uint32_t)) : new std::uint32_t[y]); 61 | else return NULL; 62 | ConvertFromUTF8((const std::uint8_t *)SrcData, x, DestData, y, DestWidth); 63 | 64 | return DestData; 65 | } 66 | 67 | bool Util::AppendUTF8CodePoint(std::uint32_t TempCP, std::uint8_t *DestData, size_t &DestDataSize, size_t MaxDestDataSize) 68 | { 69 | // 0xD800-0xDFFF are for UTF-16 surrogate pairs. Invalid characters. 70 | // 0xFDD0-0xFDEF are non-characters. 71 | // 0x*FFFE and 0x*FFFF are reserved. 72 | if ((TempCP >= 0xD800 && TempCP <= 0xDFFF) || (TempCP >= 0xFDD0 && TempCP <= 0xFDEF) || (TempCP & 0xFFFE) == 0xFFFE) return false; 73 | 74 | // First character can't be a combining code point. 75 | if (!DestDataSize && ((TempCP >= 0x0300 && TempCP <= 0x036F) || (TempCP >= 0x1DC0 && TempCP <= 0x1DFF) || (TempCP >= 0x20D0 && TempCP <= 0x20FF) || (TempCP >= 0xFE20 && TempCP <= 0xFE2F))) return false; 76 | 77 | if (TempCP <= 0x7F) 78 | { 79 | if (DestData == NULL) DestDataSize++; 80 | else if (DestDataSize >= MaxDestDataSize) return false; 81 | else DestData[DestDataSize++] = (std::uint8_t)TempCP; 82 | 83 | return true; 84 | } 85 | else if (TempCP <= 0x07FF) 86 | { 87 | if (DestData == NULL) DestDataSize += 2; 88 | else if (DestDataSize + 1 >= MaxDestDataSize) return false; 89 | else 90 | { 91 | DestData[DestDataSize++] = (std::uint8_t)(0xC0 | (TempCP >> 6)); 92 | DestData[DestDataSize++] = (std::uint8_t)(0x80 | (TempCP & 0x3F)); 93 | } 94 | 95 | return true; 96 | } 97 | else if (TempCP <= 0xFFFF) 98 | { 99 | if (DestData == NULL) DestDataSize += 3; 100 | else if (DestDataSize + 2 >= MaxDestDataSize) return false; 101 | else 102 | { 103 | DestData[DestDataSize++] = (std::uint8_t)(0xE0 | (TempCP >> 12)); 104 | DestData[DestDataSize++] = (std::uint8_t)(0x80 | ((TempCP >> 6) & 0x3F)); 105 | DestData[DestDataSize++] = (std::uint8_t)(0x80 | (TempCP & 0x3F)); 106 | } 107 | 108 | return true; 109 | } 110 | else if (TempCP <= 0x10FFFF) 111 | { 112 | if (DestData == NULL) DestDataSize += 4; 113 | else if (DestDataSize + 3 >= MaxDestDataSize) return false; 114 | else 115 | { 116 | DestData[DestDataSize++] = (std::uint8_t)(0xF0 | (TempCP >> 18)); 117 | DestData[DestDataSize++] = (std::uint8_t)(0x80 | ((TempCP >> 12) & 0x3F)); 118 | DestData[DestDataSize++] = (std::uint8_t)(0x80 | ((TempCP >> 6) & 0x3F)); 119 | DestData[DestDataSize++] = (std::uint8_t)(0x80 | (TempCP & 0x3F)); 120 | } 121 | 122 | return true; 123 | } 124 | 125 | return false; 126 | } 127 | 128 | void Util::ConvertToUTF8(const void *SrcData, size_t SrcDataSize, size_t SrcWidth, std::uint8_t *DestData, size_t &DestDataSize, size_t *LastPos) 129 | { 130 | size_t x, y = DestDataSize; 131 | std::uint32_t TempCP; 132 | 133 | DestDataSize = 0; 134 | 135 | if (SrcWidth == 1) 136 | { 137 | const std::uint8_t *SrcData2 = (const std::uint8_t *)SrcData; 138 | 139 | x = 0; 140 | while (x < SrcDataSize) 141 | { 142 | TempCP = SrcData2[x]; 143 | 144 | if (TempCP <= 0x7F) 145 | { 146 | x++; 147 | if (AppendUTF8CodePoint(TempCP, DestData, DestDataSize, y) && LastPos != NULL) *LastPos = x; 148 | } 149 | else if (x + 1 < SrcDataSize && (TempCP & 0xE0) == 0xC0 && (SrcData2[x + 1] & 0xC0) == 0x80) 150 | { 151 | TempCP = (((TempCP & 0x1F) << 6) | ((std::uint32_t)SrcData2[x + 1] & 0x3F)); 152 | x += 2; 153 | if (AppendUTF8CodePoint(TempCP, DestData, DestDataSize, y) && LastPos != NULL) *LastPos = x; 154 | } 155 | else if (x + 2 < SrcDataSize && (TempCP & 0xF0) == 0xE0 && (SrcData2[x + 1] & 0xC0) == 0x80 && (SrcData2[x + 2] & 0xC0) == 0x80) 156 | { 157 | TempCP = (((TempCP & 0x0F) << 12) | (((std::uint32_t)SrcData2[x + 1] & 0x3F) << 6) | ((std::uint32_t)SrcData2[x + 2] & 0x3F)); 158 | AppendUTF8CodePoint(TempCP, DestData, DestDataSize, y); 159 | x += 3; 160 | } 161 | else if (x + 3 < SrcDataSize && (TempCP & 0xF8) == 0xF0 && (SrcData2[x + 1] & 0xC0) == 0x80 && (SrcData2[x + 2] & 0xC0) == 0x80 && (SrcData2[x + 3] & 0xC0) == 0x80) 162 | { 163 | TempCP = (((TempCP & 0x07) << 18) | (((std::uint32_t)SrcData2[x + 1] & 0x3F) << 12) | (((std::uint32_t)SrcData2[x + 2] & 0x3F) << 6) | ((std::uint32_t)SrcData2[x + 3] & 0x3F)); 164 | if (AppendUTF8CodePoint(TempCP, DestData, DestDataSize, y) && LastPos != NULL) *LastPos = x; 165 | x += 4; 166 | } 167 | else x++; 168 | } 169 | } 170 | else if (SrcWidth == 2) 171 | { 172 | const uint16_t *SrcData2 = (const uint16_t *)SrcData; 173 | 174 | x = 0; 175 | for (x = 0; x < SrcDataSize; x++) 176 | { 177 | TempCP = SrcData2[x]; 178 | if (TempCP < 0xD800 || TempCP > 0xDBFF) 179 | { 180 | if (AppendUTF8CodePoint(TempCP, DestData, DestDataSize, y) && LastPos != NULL) *LastPos = x; 181 | } 182 | else 183 | { 184 | x++; 185 | 186 | if (x < SrcDataSize && SrcData2[x] >= 0xDC00 && SrcData2[x] <= 0xDFFF) 187 | { 188 | TempCP = (((TempCP - 0xD800) << 10) | ((std::uint32_t)SrcData2[x] - 0xDC00)) + 0x010000; 189 | if (AppendUTF8CodePoint(TempCP, DestData, DestDataSize, y) && LastPos != NULL) *LastPos = x; 190 | } 191 | } 192 | } 193 | } 194 | else if (SrcWidth == 4) 195 | { 196 | const std::uint32_t *SrcData2 = (const std::uint32_t *)SrcData; 197 | 198 | for (x = 0; x < SrcDataSize; x++) 199 | { 200 | TempCP = SrcData2[x]; 201 | 202 | if (AppendUTF8CodePoint(TempCP, DestData, DestDataSize, y) && LastPos != NULL) *LastPos = x; 203 | } 204 | } 205 | } 206 | 207 | bool Util::AppendUTFCodePoint(std::uint32_t TempCP, void *DestData, size_t &DestDataSize, size_t DestWidth, size_t MaxDestDataSize) 208 | { 209 | if (DestWidth == 1) return AppendUTF8CodePoint(TempCP, (std::uint8_t *)DestData, DestDataSize, MaxDestDataSize); 210 | 211 | // 0xD800-0xDFFF are for UTF-16 surrogate pairs. Invalid characters. 212 | // 0xFDD0-0xFDEF are non-characters. 213 | // 0x*FFFE and 0x*FFFF are reserved. 214 | // The largest possible character is 0x10FFFF. 215 | if ((TempCP >= 0xD800 && TempCP <= 0xDFFF) || (TempCP >= 0xFDD0 && TempCP <= 0xFDEF) || (TempCP & 0xFFFE) == 0xFFFE || TempCP > 0x10FFFF) return false; 216 | 217 | // First character can't be a combining code point. 218 | if (!DestDataSize && ((TempCP >= 0x0300 && TempCP <= 0x036F) || (TempCP >= 0x1DC0 && TempCP <= 0x1DFF) || (TempCP >= 0x20D0 && TempCP <= 0x20FF) || (TempCP >= 0xFE20 && TempCP <= 0xFE2F))) return false; 219 | 220 | if (DestWidth == 2) 221 | { 222 | uint16_t *DestData2 = (uint16_t *)DestData; 223 | 224 | if (TempCP > 0xFFFF) 225 | { 226 | if (DestData2 == NULL) DestDataSize += 2; 227 | else if (DestDataSize + 1 >= MaxDestDataSize) return false; 228 | else 229 | { 230 | TempCP -= 0x010000; 231 | DestData2[DestDataSize++] = (uint16_t)(((TempCP >> 10) & 0x03FF) + 0xD800); 232 | DestData2[DestDataSize++] = (uint16_t)((TempCP & 0x03FF) + 0xDC00); 233 | } 234 | } 235 | else if (DestData2 == NULL) DestDataSize++; 236 | else if (DestDataSize >= MaxDestDataSize) return false; 237 | else DestData2[DestDataSize++] = (uint16_t)TempCP; 238 | } 239 | else if (DestWidth == 4) 240 | { 241 | std::uint32_t *DestData2 = (std::uint32_t *)DestData; 242 | 243 | if (DestData2 == NULL) DestDataSize++; 244 | else if (DestDataSize >= MaxDestDataSize) return false; 245 | else DestData2[DestDataSize++] = TempCP; 246 | } 247 | 248 | return true; 249 | } 250 | 251 | void Util::ConvertFromUTF8(const std::uint8_t *SrcData, size_t SrcDataSize, void *DestData, size_t &DestDataSize, size_t DestWidth) 252 | { 253 | size_t x, y; 254 | std::uint32_t TempCP; 255 | 256 | y = DestDataSize; 257 | DestDataSize = 0; 258 | 259 | x = 0; 260 | while (x < SrcDataSize && y) 261 | { 262 | TempCP = SrcData[x]; 263 | 264 | if (TempCP <= 0x7F) 265 | { 266 | if (!AppendUTFCodePoint(TempCP, DestData, DestDataSize, DestWidth, y)) return; 267 | x++; 268 | } 269 | else if (x + 1 < SrcDataSize && (TempCP & 0xE0) == 0xC0 && (SrcData[x + 1] & 0xC0) == 0x80) 270 | { 271 | TempCP = (((TempCP & 0x1F) << 6) | ((std::uint32_t)SrcData[x + 1] & 0x3F)); 272 | if (!AppendUTFCodePoint(TempCP, DestData, DestDataSize, DestWidth, y)) return; 273 | x += 2; 274 | } 275 | else if (x + 2 < SrcDataSize && (TempCP & 0xF0) == 0xE0 && (SrcData[x + 1] & 0xC0) == 0x80 && (SrcData[x + 2] & 0xC0) == 0x80) 276 | { 277 | TempCP = (((TempCP & 0x0F) << 12) | (((std::uint32_t)SrcData[x + 1] & 0x3F) << 6) | ((std::uint32_t)SrcData[x + 2] & 0x3F)); 278 | if (!AppendUTFCodePoint(TempCP, DestData, DestDataSize, DestWidth, y)) return; 279 | x += 3; 280 | } 281 | else if (x + 3 < SrcDataSize && (TempCP & 0xF8) == 0xF0 && (SrcData[x + 1] & 0xC0) == 0x80 && (SrcData[x + 2] & 0xC0) == 0x80 && (SrcData[x + 3] & 0xC0) == 0x80) 282 | { 283 | TempCP = (((TempCP & 0x07) << 18) | (((std::uint32_t)SrcData[x + 1] & 0x3F) << 12) | (((std::uint32_t)SrcData[x + 2] & 0x3F) << 6) | ((std::uint32_t)SrcData[x + 3] & 0x3F)); 284 | if (!AppendUTFCodePoint(TempCP, DestData, DestDataSize, DestWidth, y)) return; 285 | x += 4; 286 | } 287 | else x++; 288 | } 289 | } 290 | 291 | size_t Util::strlen(const char *Str) 292 | { 293 | size_t x, y, Result; 294 | std::uint32_t ResultCP, NextCP; 295 | bool HasCombiningCP; 296 | 297 | x = 0; 298 | Result = 0; 299 | HasCombiningCP = false; 300 | while (NextCodePoint(ResultCP, NextCP, Str, x, y, HasCombiningCP)) 301 | { 302 | if (!HasCombiningCP) Result++; 303 | } 304 | if (HasCombiningCP) Result++; 305 | 306 | return Result; 307 | } 308 | 309 | bool Util::FindCodePoint(size_t &ResultPos, std::uint32_t CodePoint, const char *Str, bool AllowCombiningCP) 310 | { 311 | size_t x, y; 312 | std::uint32_t ResultCP, NextCP; 313 | bool HasCombiningCP; 314 | 315 | if (!AllowCombiningCP && IsCombiningCodePoint(CodePoint)) return false; 316 | 317 | x = 0; 318 | while (NextCodePoint(ResultCP, NextCP, Str, x, y, HasCombiningCP)) 319 | { 320 | if (ResultCP == CodePoint && (AllowCombiningCP || !HasCombiningCP)) 321 | { 322 | ResultPos = x; 323 | 324 | return true; 325 | } 326 | } 327 | 328 | return false; 329 | } 330 | 331 | bool Util::NextCodePoint(std::uint32_t &ResultCP, std::uint32_t &NextCP, const char *Str, size_t &Pos, size_t &Size, bool &HasCombiningCP) 332 | { 333 | const std::uint8_t *Str2 = (const std::uint8_t *)Str; 334 | 335 | if (Pos) 336 | { 337 | ResultCP = NextCP; 338 | if (ResultCP == 0) return false; 339 | } 340 | else 341 | { 342 | Size = strlen(Str); 343 | HasCombiningCP = false; 344 | 345 | ResultCP = Str2[0]; 346 | if (ResultCP == 0) return false; 347 | 348 | if (ResultCP <= 0x7F) Pos++; 349 | else if (Pos + 1 < Size && (ResultCP & 0xE0) == 0xC0 && (Str2[Pos + 1] & 0xC0) == 0x80) Pos += 2; 350 | else if (Pos + 2 < Size && (ResultCP & 0xF0) == 0xE0 && (Str2[Pos + 1] & 0xC0) == 0x80 && (Str2[Pos + 2] & 0xC0) == 0x80) Pos += 3; 351 | else if (Pos + 3 < Size && (ResultCP & 0xF8) == 0xF0 && (Str2[Pos + 1] & 0xC0) == 0x80 && (Str2[Pos + 2] & 0xC0) == 0x80 && (Str2[Pos + 3] & 0xC0) == 0x80) Pos += 4; 352 | else return false; 353 | 354 | // First character can't be a combining code point. 355 | if ((ResultCP >= 0x0300 && ResultCP <= 0x036F) || (ResultCP >= 0x1DC0 && ResultCP <= 0x1DFF) || (ResultCP >= 0x20D0 && ResultCP <= 0x20FF) || (ResultCP >= 0xFE20 && ResultCP <= 0xFE2F)) return false; 356 | } 357 | 358 | NextCP = Str2[Pos]; 359 | 360 | if (NextCP != 0) 361 | { 362 | if (NextCP <= 0x7F) Pos++; 363 | else if (Pos + 1 < Size && (NextCP & 0xE0) == 0xC0 && (Str2[Pos + 1] & 0xC0) == 0x80) Pos += 2; 364 | else if (Pos + 2 < Size && (NextCP & 0xF0) == 0xE0 && (Str2[Pos + 1] & 0xC0) == 0x80 && (Str2[Pos + 2] & 0xC0) == 0x80) Pos += 3; 365 | else if (Pos + 3 < Size && (NextCP & 0xF8) == 0xF0 && (Str2[Pos + 1] & 0xC0) == 0x80 && (Str2[Pos + 2] & 0xC0) == 0x80 && (Str2[Pos + 3] & 0xC0) == 0x80) Pos += 4; 366 | else NextCP = 0; 367 | } 368 | 369 | HasCombiningCP = ((NextCP >= 0x0300 && NextCP <= 0x036F) || (NextCP >= 0x1DC0 && NextCP <= 0x1DFF) || (NextCP >= 0x20D0 && NextCP <= 0x20FF) || (NextCP >= 0xFE20 && NextCP <= 0xFE2F)); 370 | 371 | return true; 372 | } 373 | 374 | bool Util::NextASCIICodePoint(char &ResultCP, std::uint32_t &NextCP, const char *Str, size_t &Pos, size_t &Size) 375 | { 376 | bool HasCombiningCP; 377 | std::uint32_t TempCP; 378 | 379 | while (NextCodePoint(TempCP, NextCP, Str, Pos, Size, HasCombiningCP)) 380 | { 381 | if (!HasCombiningCP && TempCP <= 0x7F) 382 | { 383 | ResultCP = (char)TempCP; 384 | 385 | return true; 386 | } 387 | } 388 | 389 | return false; 390 | } 391 | 392 | bool Util::IsCombiningCodePoint(std::uint32_t CodePoint) 393 | { 394 | return ((CodePoint >= 0x0300 && CodePoint <= 0x036F) || (CodePoint >= 0x1DC0 && CodePoint <= 0x1DFF) || (CodePoint >= 0x20D0 && CodePoint <= 0x20FF) || (CodePoint >= 0xFE20 && CodePoint <= 0xFE2F)); 395 | } 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /getiptables.cpp: -------------------------------------------------------------------------------- 1 | // A simple program to dump TCP/UDP table information as consumable JSON. 2 | // 3 | // (C) 2021 CubicleSoft. All Rights Reserved. 4 | 5 | #define UNICODE 6 | #define _UNICODE 7 | #define _CRT_SECURE_NO_WARNINGS 8 | 9 | #ifdef _MBCS 10 | #undef _MBCS 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "utf8/utf8_util.h" 23 | #include "utf8/utf8_file_dir.h" 24 | #include "utf8/utf8_mixed_var.h" 25 | #include "json/json_serializer.h" 26 | #include "network/network_init.h" 27 | 28 | // Initialize networking. 29 | CubicleSoft::Network::Init GxNetworkInit; 30 | 31 | #ifdef SUBSYSTEM_WINDOWS 32 | // If the caller is a console application and is waiting for this application to complete, then attach to the console. 33 | void InitVerboseMode(void) 34 | { 35 | if (::AttachConsole(ATTACH_PARENT_PROCESS)) 36 | { 37 | if (::GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) 38 | { 39 | freopen("CONOUT$", "w", stdout); 40 | setvbuf(stdout, NULL, _IONBF, 0); 41 | } 42 | 43 | if (::GetStdHandle(STD_ERROR_HANDLE) != INVALID_HANDLE_VALUE) 44 | { 45 | freopen("CONOUT$", "w", stderr); 46 | setvbuf(stderr, NULL, _IONBF, 0); 47 | } 48 | } 49 | } 50 | #endif 51 | 52 | void DumpSyntax(TCHAR *currfile) 53 | { 54 | #ifdef SUBSYSTEM_WINDOWS 55 | InitVerboseMode(); 56 | #endif 57 | 58 | _tprintf(_T("(C) 2021 CubicleSoft. All Rights Reserved.\n\n")); 59 | 60 | _tprintf(_T("Syntax: %s [options]\n\n"), currfile); 61 | 62 | _tprintf(_T("Options:\n")); 63 | 64 | _tprintf(_T("\t/v\n\ 65 | \tVerbose mode.\n\ 66 | \n\ 67 | \t/tcponly\n\ 68 | \tOnly output TCP table information.\n\ 69 | \tIncompatible with 'udponly'.\n\ 70 | \n\ 71 | \t/udponly\n\ 72 | \tOnly output UDP table information.\n\ 73 | \tIncompatible with 'tcponly'.\n\ 74 | \n\ 75 | \t/state=State\n\ 76 | \tOnly output table information for the specified state.\n\ 77 | \tMap only be one of:\n\ 78 | \t\tCLOSED\n\ 79 | \t\tLISTEN\n\ 80 | \t\tSYN-SENT\n\ 81 | \t\tSYN-RECEIVED\n\ 82 | \t\tESTABLISHED\n\ 83 | \t\tFIN-WAIT-1\n\ 84 | \t\tFIN-WAIT-2\n\ 85 | \t\tCLOSE-WAIT\n\ 86 | \t\tCLOSING\n\ 87 | \t\tLAST-ACK\n\ 88 | \t\tTIME-WAIT\n\ 89 | \t\tDELETE-TCB\n\ 90 | \n\ 91 | \t/localip=IPAddr\n\ 92 | \tOnly output table information for the specified local IP address.\n\ 93 | \n\ 94 | \t/localport=PortNum\n\ 95 | \tOnly output table information for the specified local port number.\n\ 96 | \n\ 97 | \t/remoteip=IPAddr\n\ 98 | \tOnly output table information for the specified remote IP address.\n\ 99 | \n\ 100 | \t/remoteport=PortNum\n\ 101 | \tOnly output table information for the specified remote port number.\n\ 102 | \n\ 103 | \t/sort\n\ 104 | \tSort the output.\n\ 105 | \n\ 106 | \t/file=OutputFile\n\ 107 | \tFile to write the JSON output to instead of stdout.\n\n")); 108 | 109 | #ifdef SUBSYSTEM_WINDOWS 110 | _tprintf(_T("\t/attach\n")); 111 | _tprintf(_T("\tAttempt to attach to a parent console if it exists.\n\n")); 112 | #endif 113 | } 114 | 115 | 116 | void DumpOutput(CubicleSoft::UTF8::File &OutputFile, CubicleSoft::JSON::Serializer &OutputJSON) 117 | { 118 | size_t y; 119 | 120 | if (OutputFile.IsOpen()) OutputFile.Write((std::uint8_t *)OutputJSON.GetBuffer(), OutputJSON.GetCurrPos(), y); 121 | else printf("%s", OutputJSON.GetBuffer()); 122 | 123 | OutputJSON.ResetPos(); 124 | } 125 | 126 | void DumpWinError(CubicleSoft::JSON::Serializer &OutputJSON, DWORD winerror) 127 | { 128 | LPTSTR errmsg = NULL; 129 | CubicleSoft::UTF8::UTF8MixedVar TempVar; 130 | 131 | ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, winerror, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, NULL); 132 | 133 | if (errmsg == NULL) OutputJSON.AppendStr("winerror", "Unknown Windows error message."); 134 | else 135 | { 136 | TempVar.SetUTF8(errmsg); 137 | OutputJSON.AppendStr("winerror", TempVar.GetStr()); 138 | 139 | ::LocalFree(errmsg); 140 | } 141 | } 142 | 143 | void DumpErrorMsg(CubicleSoft::UTF8::File &OutputFile, CubicleSoft::JSON::Serializer &OutputJSON, const char *errorstr, const char *errorcode, DWORD winerror) 144 | { 145 | OutputJSON.AppendBool("success", false); 146 | OutputJSON.AppendStr("error", errorstr); 147 | OutputJSON.AppendStr("errorcode", errorcode); 148 | DumpWinError(OutputJSON, winerror); 149 | OutputJSON.AppendUInt("winerrorcode", winerror); 150 | 151 | DumpOutput(OutputFile, OutputJSON); 152 | } 153 | 154 | bool IP4AddrMatches(struct addrinfo *ipptr, u_long addr) 155 | { 156 | while (ipptr != NULL) 157 | { 158 | if (ipptr->ai_family == AF_INET && ((struct sockaddr_in *)ipptr->ai_addr)->sin_addr.S_un.S_addr == addr) return true; 159 | 160 | ipptr = ipptr->ai_next; 161 | } 162 | 163 | return false; 164 | } 165 | 166 | bool IP6AddrMatches(struct addrinfo *ipptr, struct in6_addr addr) 167 | { 168 | while (ipptr != NULL) 169 | { 170 | if (ipptr->ai_family == AF_INET6 && !memcmp(&((struct sockaddr_in6 *)ipptr->ai_addr)->sin6_addr, &addr, sizeof(struct in6_addr))) return true; 171 | 172 | ipptr = ipptr->ai_next; 173 | } 174 | 175 | return false; 176 | } 177 | 178 | void AppendTCPState(CubicleSoft::JSON::Serializer &OutputJSON, DWORD state) 179 | { 180 | switch (state) 181 | { 182 | case MIB_TCP_STATE_CLOSED: OutputJSON.AppendStr("state", "CLOSED"); break; 183 | case MIB_TCP_STATE_LISTEN: OutputJSON.AppendStr("state", "LISTEN"); break; 184 | case MIB_TCP_STATE_SYN_SENT: OutputJSON.AppendStr("state", "SYN-SENT"); break; 185 | case MIB_TCP_STATE_SYN_RCVD: OutputJSON.AppendStr("state", "SYN-RECEIVED"); break; 186 | case MIB_TCP_STATE_ESTAB: OutputJSON.AppendStr("state", "ESTABLISHED"); break; 187 | case MIB_TCP_STATE_FIN_WAIT1: OutputJSON.AppendStr("state", "FIN-WAIT-1"); break; 188 | case MIB_TCP_STATE_FIN_WAIT2: OutputJSON.AppendStr("state", "FIN-WAIT-2"); break; 189 | case MIB_TCP_STATE_CLOSE_WAIT: OutputJSON.AppendStr("state", "CLOSE-WAIT"); break; 190 | case MIB_TCP_STATE_CLOSING: OutputJSON.AppendStr("state", "CLOSING"); break; 191 | case MIB_TCP_STATE_LAST_ACK: OutputJSON.AppendStr("state", "LAST-ACK"); break; 192 | case MIB_TCP_STATE_TIME_WAIT: OutputJSON.AppendStr("state", "TIME-WAIT"); break; 193 | case MIB_TCP_STATE_DELETE_TCB: OutputJSON.AppendStr("state", "DELETE-TCB"); break; 194 | default: 195 | { 196 | OutputJSON.AppendStr("state", "UNKNOWN"); 197 | OutputJSON.AppendUInt("state_val", state); 198 | 199 | break; 200 | } 201 | } 202 | } 203 | 204 | void AppendTCPOffloadState(CubicleSoft::JSON::Serializer &OutputJSON, DWORD state) 205 | { 206 | switch (state) 207 | { 208 | case TcpConnectionOffloadStateInHost: OutputJSON.AppendStr("offload_state", "InHost"); break; 209 | case TcpConnectionOffloadStateOffloading: OutputJSON.AppendStr("offload_state", "Offloading"); break; 210 | case TcpConnectionOffloadStateOffloaded: OutputJSON.AppendStr("offload_state", "Offloaded"); break; 211 | case TcpConnectionOffloadStateUploading: OutputJSON.AppendStr("offload_state", "Uploading"); break; 212 | default: 213 | { 214 | OutputJSON.AppendStr("state", "UNKNOWN"); 215 | OutputJSON.AppendUInt("state_val", state); 216 | 217 | break; 218 | } 219 | } 220 | } 221 | 222 | enum IPTablesMode 223 | { 224 | TCPUDPMode, 225 | TCPMode, 226 | UDPMode 227 | }; 228 | 229 | int _tmain(int argc, TCHAR **argv) 230 | { 231 | bool verbose = false; 232 | IPTablesMode mode = TCPUDPMode; 233 | bool states[MIB_TCP_STATE_DELETE_TCB + 1] = { false }; 234 | bool usestates = false; 235 | struct addrinfo hints, *localip = NULL, *remoteip = NULL; 236 | DWORD localport = (DWORD)-1, remoteport = (DWORD)-1; 237 | BOOL sort = FALSE; 238 | LPTSTR filename = NULL; 239 | int result = 0; 240 | const char *errorstr = NULL, *errorcode = NULL; 241 | DWORD winerror; 242 | 243 | CubicleSoft::UTF8::UTF8MixedVar TempVar; 244 | 245 | // Process command-line options. 246 | int x; 247 | for (x = 1; x < argc; x++) 248 | { 249 | if (!_tcsicmp(argv[x], _T("/v"))) verbose = true; 250 | else if (!_tcsicmp(argv[x], _T("/?")) || !_tcsicmp(argv[x], _T("/h"))) 251 | { 252 | DumpSyntax(argv[0]); 253 | 254 | return 1; 255 | } 256 | else if (!_tcsicmp(argv[x], _T("/tcponly"))) mode = TCPMode; 257 | else if (!_tcsicmp(argv[x], _T("/udponly"))) mode = UDPMode; 258 | else if (!_tcsncicmp(argv[x], _T("/state="), 7)) 259 | { 260 | int statenum = -1; 261 | 262 | if (!_tcsicmp(argv[x] + 7, _T("CLOSED"))) statenum = MIB_TCP_STATE_CLOSED; 263 | else if (!_tcsicmp(argv[x] + 7, _T("LISTEN"))) statenum = MIB_TCP_STATE_LISTEN; 264 | else if (!_tcsicmp(argv[x] + 7, _T("SYN-SENT"))) statenum = MIB_TCP_STATE_SYN_SENT; 265 | else if (!_tcsicmp(argv[x] + 7, _T("SYN-RECEIVED"))) statenum = MIB_TCP_STATE_SYN_RCVD; 266 | else if (!_tcsicmp(argv[x] + 7, _T("ESTABLISHED"))) statenum = MIB_TCP_STATE_ESTAB; 267 | else if (!_tcsicmp(argv[x] + 7, _T("FIN-WAIT-1"))) statenum = MIB_TCP_STATE_FIN_WAIT1; 268 | else if (!_tcsicmp(argv[x] + 7, _T("FIN-WAIT-2"))) statenum = MIB_TCP_STATE_FIN_WAIT2; 269 | else if (!_tcsicmp(argv[x] + 7, _T("CLOSE-WAIT"))) statenum = MIB_TCP_STATE_CLOSE_WAIT; 270 | else if (!_tcsicmp(argv[x] + 7, _T("CLOSING"))) statenum = MIB_TCP_STATE_CLOSING; 271 | else if (!_tcsicmp(argv[x] + 7, _T("LAST-ACK"))) statenum = MIB_TCP_STATE_LAST_ACK; 272 | else if (!_tcsicmp(argv[x] + 7, _T("TIME-WAIT"))) statenum = MIB_TCP_STATE_TIME_WAIT; 273 | else if (!_tcsicmp(argv[x] + 7, _T("DELETE-TCB"))) statenum = MIB_TCP_STATE_DELETE_TCB; 274 | 275 | if (statenum > -1) 276 | { 277 | states[statenum] = true; 278 | usestates = true; 279 | } 280 | } 281 | else if (!_tcsncicmp(argv[x], _T("/localip="), 9)) 282 | { 283 | TempVar.SetUTF8(argv[x] + 9); 284 | 285 | ::ZeroMemory(&hints, sizeof(hints)); 286 | hints.ai_flags = AI_NUMERICHOST; 287 | hints.ai_family = AF_UNSPEC; 288 | 289 | if (::getaddrinfo(TempVar.MxStr, NULL, &hints, &localip) != 0) 290 | { 291 | errorstr = "Unable to convert/parse local IP address."; 292 | errorcode = "invalid_local_ip"; 293 | winerror = ::WSAGetLastError(); 294 | } 295 | } 296 | else if (!_tcsncicmp(argv[x], _T("/localport="), 11)) localport = (DWORD)_tstoi(argv[x] + 11); 297 | else if (!_tcsncicmp(argv[x], _T("/remoteip="), 10)) 298 | { 299 | TempVar.SetUTF8(argv[x] + 10); 300 | 301 | ::ZeroMemory(&hints, sizeof(hints)); 302 | hints.ai_flags = AI_NUMERICHOST; 303 | hints.ai_family = AF_UNSPEC; 304 | 305 | if (::getaddrinfo(TempVar.MxStr, NULL, &hints, &remoteip) != 0) 306 | { 307 | errorstr = "Unable to convert/parse remote IP address."; 308 | errorcode = "invalid_remote_ip"; 309 | winerror = ::WSAGetLastError(); 310 | } 311 | } 312 | else if (!_tcsncicmp(argv[x], _T("/remoteport="), 12)) remoteport = (DWORD)_tstoi(argv[x] + 12); 313 | else if (!_tcsicmp(argv[x], _T("/sort"))) sort = TRUE; 314 | else if (!_tcsncicmp(argv[x], _T("/file="), 6)) filename = argv[x] + 6; 315 | else if (!_tcsicmp(argv[x], _T("/attach"))) 316 | { 317 | #ifdef SUBSYSTEM_WINDOWS 318 | // For the Windows subsystem only, attempt to attach to a parent console if it exists. 319 | InitVerboseMode(); 320 | #endif 321 | } 322 | else 323 | { 324 | // Probably reached the command to execute portion of the arguments. 325 | break; 326 | } 327 | } 328 | 329 | if (verbose) 330 | { 331 | #ifdef SUBSYSTEM_WINDOWS 332 | InitVerboseMode(); 333 | #endif 334 | 335 | _tprintf(_T("Arguments:\n")); 336 | for (int x2 = 0; x2 < argc; x2++) 337 | { 338 | _tprintf(_T("\targv[%d] = %s\n"), x2, argv[x2]); 339 | } 340 | _tprintf(_T("\n")); 341 | } 342 | 343 | // Handle output to a file. 344 | CubicleSoft::UTF8::File OutputFile; 345 | size_t y; 346 | if (filename != NULL) 347 | { 348 | TempVar.SetUTF8(filename); 349 | if (!OutputFile.Open(TempVar.GetStr(), O_CREAT | O_WRONLY | O_TRUNC)) 350 | { 351 | #ifdef SUBSYSTEM_WINDOWS 352 | InitVerboseMode(); 353 | #endif 354 | 355 | _tprintf(_T("Unable to open '%s' for writing.\n"), filename); 356 | 357 | return 1; 358 | } 359 | } 360 | 361 | char outputbuffer[4096]; 362 | CubicleSoft::JSON::Serializer OutputJSON; 363 | 364 | OutputJSON.SetBuffer((std::uint8_t *)outputbuffer, sizeof(outputbuffer)); 365 | OutputJSON.StartObject(); 366 | 367 | if (errorstr != NULL) 368 | { 369 | DumpErrorMsg(OutputFile, OutputJSON, errorstr, errorcode, winerror); 370 | 371 | result = 1; 372 | } 373 | else 374 | { 375 | OutputJSON.AppendBool("success", true); 376 | 377 | ULONG tablesize = 65536, tablesize2; 378 | char *tablebuffer = new char[tablesize]; 379 | sockaddr_in ip4addr = { AF_INET, 0 }; 380 | sockaddr_in6 ip6addr = { AF_INET6, 0 }; 381 | u_short localport2, remoteport2; 382 | DWORD tempsize; 383 | 384 | if (mode == TCPUDPMode || mode == TCPMode) 385 | { 386 | // TCP v4. 387 | OutputJSON.StartObject("tcp4"); 388 | 389 | PMIB_TCPTABLE2 tcptable2ptr = (PMIB_TCPTABLE2)tablebuffer; 390 | 391 | tablesize2 = tablesize; 392 | while ((winerror = ::GetTcpTable2(tcptable2ptr, &tablesize2, sort)) == ERROR_INSUFFICIENT_BUFFER) 393 | { 394 | delete[] tablebuffer; 395 | 396 | tablesize = tablesize2; 397 | tablebuffer = new char[tablesize]; 398 | tcptable2ptr = (PMIB_TCPTABLE2)tablebuffer; 399 | } 400 | 401 | if (winerror != NO_ERROR) 402 | { 403 | DumpErrorMsg(OutputFile, OutputJSON, "The call to GetTcpTable2 failed.", "get_tcp_table_failed", winerror); 404 | 405 | result = 1; 406 | } 407 | else 408 | { 409 | OutputJSON.AppendBool("success", true); 410 | 411 | OutputJSON.StartArray("info"); 412 | 413 | for (x = 0; x < (int)tcptable2ptr->dwNumEntries; x++) 414 | { 415 | localport2 = ::ntohs((u_short)tcptable2ptr->table[x].dwLocalPort); 416 | remoteport2 = ::ntohs((u_short)tcptable2ptr->table[x].dwRemotePort); 417 | 418 | // Check state, IP addresses, and port numbers for matches. 419 | if (usestates && !states[tcptable2ptr->table[x].dwState]) continue; 420 | if (localip != NULL && !IP4AddrMatches(localip, (u_long)tcptable2ptr->table[x].dwLocalAddr)) continue; 421 | if (localport != (DWORD)-1 && localport != (DWORD)localport2) continue; 422 | if (remoteip != NULL && !IP4AddrMatches(remoteip, (u_long)tcptable2ptr->table[x].dwRemoteAddr)) continue; 423 | if (remoteport != (DWORD)-1 && remoteport != (DWORD)remoteport2) continue; 424 | 425 | OutputJSON.SetValSplitter(",\n\n"); 426 | OutputJSON.StartObject(); 427 | OutputJSON.SetValSplitter(", "); 428 | 429 | AppendTCPState(OutputJSON, tcptable2ptr->table[x].dwState); 430 | 431 | ip4addr.sin_addr.S_un.S_addr = (u_long)tcptable2ptr->table[x].dwLocalAddr; 432 | tempsize = TempVar.GetMaxSize(); 433 | if (::WSAAddressToStringA((LPSOCKADDR)&ip4addr, sizeof(ip4addr), NULL, TempVar.GetStr(), &tempsize) == 0) OutputJSON.AppendStr("local_ip", TempVar.GetStr()); 434 | else OutputJSON.AppendNull("local_ip"); 435 | 436 | OutputJSON.AppendUInt("local_port", localport2); 437 | 438 | ip4addr.sin_addr.S_un.S_addr = (u_long)tcptable2ptr->table[x].dwRemoteAddr; 439 | tempsize = TempVar.GetMaxSize(); 440 | if (::WSAAddressToStringA((LPSOCKADDR)&ip4addr, sizeof(ip4addr), NULL, TempVar.GetStr(), &tempsize) == 0) OutputJSON.AppendStr("remote_ip", TempVar.GetStr()); 441 | else OutputJSON.AppendNull("remote_ip"); 442 | 443 | OutputJSON.AppendUInt("remote_port", remoteport2); 444 | 445 | OutputJSON.AppendUInt("pid", tcptable2ptr->table[x].dwOwningPid); 446 | 447 | AppendTCPOffloadState(OutputJSON, tcptable2ptr->table[x].dwOffloadState); 448 | 449 | DumpOutput(OutputFile, OutputJSON); 450 | 451 | OutputJSON.EndObject(); 452 | } 453 | 454 | OutputJSON.EndArray(); 455 | } 456 | 457 | OutputJSON.EndObject(); 458 | 459 | // TCP v6. 460 | OutputJSON.SetValSplitter(",\n\n"); 461 | OutputJSON.StartObject("tcp6"); 462 | OutputJSON.SetValSplitter(", "); 463 | 464 | PMIB_TCP6TABLE2 tcp6table2ptr = (PMIB_TCP6TABLE2)tablebuffer; 465 | 466 | tablesize2 = tablesize; 467 | while ((winerror = ::GetTcp6Table2(tcp6table2ptr, &tablesize2, sort)) == ERROR_INSUFFICIENT_BUFFER) 468 | { 469 | delete[] tablebuffer; 470 | 471 | tablesize = tablesize2; 472 | tablebuffer = new char[tablesize]; 473 | tcp6table2ptr = (PMIB_TCP6TABLE2)tablebuffer; 474 | } 475 | 476 | if (winerror != NO_ERROR) 477 | { 478 | DumpErrorMsg(OutputFile, OutputJSON, "The call to GetTcp6Table2 failed.", "get_tcp_table_failed", winerror); 479 | 480 | result = 1; 481 | } 482 | else 483 | { 484 | OutputJSON.AppendBool("success", true); 485 | 486 | OutputJSON.StartArray("info"); 487 | 488 | for (x = 0; x < (int)tcp6table2ptr->dwNumEntries; x++) 489 | { 490 | localport2 = ::ntohs((u_short)tcp6table2ptr->table[x].dwLocalPort); 491 | remoteport2 = ::ntohs((u_short)tcp6table2ptr->table[x].dwRemotePort); 492 | 493 | // Check state, IP addresses, and port numbers for matches. 494 | if (usestates && !states[tcp6table2ptr->table[x].State]) continue; 495 | if (localip != NULL && !IP6AddrMatches(localip, tcp6table2ptr->table[x].LocalAddr)) continue; 496 | if (localport != (DWORD)-1 && localport != (DWORD)localport2) continue; 497 | if (remoteip != NULL && !IP6AddrMatches(remoteip, tcp6table2ptr->table[x].RemoteAddr)) continue; 498 | if (remoteport != (DWORD)-1 && remoteport != (DWORD)remoteport2) continue; 499 | 500 | OutputJSON.SetValSplitter(",\n\n"); 501 | OutputJSON.StartObject(); 502 | OutputJSON.SetValSplitter(", "); 503 | 504 | AppendTCPState(OutputJSON, tcp6table2ptr->table[x].State); 505 | 506 | ip6addr.sin6_addr = (in6_addr)tcp6table2ptr->table[x].LocalAddr; 507 | tempsize = TempVar.GetMaxSize(); 508 | if (::WSAAddressToStringA((LPSOCKADDR)&ip6addr, sizeof(ip6addr), NULL, TempVar.GetStr(), &tempsize) == 0) OutputJSON.AppendStr("local_ip", TempVar.GetStr()); 509 | else OutputJSON.AppendNull("local_ip"); 510 | 511 | OutputJSON.AppendUInt("local_port", localport2); 512 | 513 | OutputJSON.AppendUInt("local_scope_id", tcp6table2ptr->table[x].dwLocalScopeId); 514 | 515 | ip6addr.sin6_addr = (in6_addr)tcp6table2ptr->table[x].RemoteAddr; 516 | tempsize = TempVar.GetMaxSize(); 517 | if (::WSAAddressToStringA((LPSOCKADDR)&ip6addr, sizeof(ip6addr), NULL, TempVar.GetStr(), &tempsize) == 0) OutputJSON.AppendStr("remote_ip", TempVar.GetStr()); 518 | else OutputJSON.AppendNull("remote_ip"); 519 | 520 | OutputJSON.AppendUInt("remote_port", remoteport2); 521 | 522 | OutputJSON.AppendUInt("remote_scope_id", tcp6table2ptr->table[x].dwRemoteScopeId); 523 | 524 | OutputJSON.AppendUInt("pid", tcp6table2ptr->table[x].dwOwningPid); 525 | 526 | AppendTCPOffloadState(OutputJSON, tcp6table2ptr->table[x].dwOffloadState); 527 | 528 | DumpOutput(OutputFile, OutputJSON); 529 | 530 | OutputJSON.EndObject(); 531 | } 532 | 533 | OutputJSON.EndArray(); 534 | } 535 | 536 | OutputJSON.EndObject(); 537 | } 538 | 539 | if (mode == TCPUDPMode || mode == UDPMode) 540 | { 541 | // UDP v4. 542 | OutputJSON.SetValSplitter(",\n\n"); 543 | OutputJSON.StartObject("udp4"); 544 | OutputJSON.SetValSplitter(", "); 545 | 546 | PMIB_UDPTABLE udptableptr = (PMIB_UDPTABLE)tablebuffer; 547 | 548 | tablesize2 = tablesize; 549 | while ((winerror = ::GetUdpTable(udptableptr, &tablesize2, sort)) == ERROR_INSUFFICIENT_BUFFER) 550 | { 551 | delete[] tablebuffer; 552 | 553 | tablesize = tablesize2; 554 | tablebuffer = new char[tablesize]; 555 | udptableptr = (PMIB_UDPTABLE)tablebuffer; 556 | } 557 | 558 | if (winerror != NO_ERROR) 559 | { 560 | DumpErrorMsg(OutputFile, OutputJSON, "The call to GetUdpTable failed.", "get_udp_table_failed", winerror); 561 | 562 | result = 1; 563 | } 564 | else 565 | { 566 | OutputJSON.AppendBool("success", true); 567 | 568 | OutputJSON.StartArray("info"); 569 | 570 | for (x = 0; x < (int)udptableptr->dwNumEntries; x++) 571 | { 572 | localport2 = ::ntohs((u_short)udptableptr->table[x].dwLocalPort); 573 | 574 | // Check state, IP address, and port number for matches. 575 | if (usestates && !states[MIB_TCP_STATE_LISTEN]) continue; 576 | if (localip != NULL && !IP4AddrMatches(localip, (u_long)udptableptr->table[x].dwLocalAddr)) continue; 577 | if (localport != (DWORD)-1 && localport != (DWORD)localport2) continue; 578 | 579 | OutputJSON.SetValSplitter(",\n\n"); 580 | OutputJSON.StartObject(); 581 | OutputJSON.SetValSplitter(", "); 582 | 583 | OutputJSON.AppendStr("state", "LISTEN"); 584 | 585 | ip4addr.sin_addr.S_un.S_addr = (u_long)udptableptr->table[x].dwLocalAddr; 586 | tempsize = TempVar.GetMaxSize(); 587 | if (::WSAAddressToStringA((LPSOCKADDR)&ip4addr, sizeof(ip4addr), NULL, TempVar.GetStr(), &tempsize) == 0) OutputJSON.AppendStr("local_ip", TempVar.GetStr()); 588 | else OutputJSON.AppendNull("local_ip"); 589 | 590 | OutputJSON.AppendUInt("local_port", localport2); 591 | 592 | DumpOutput(OutputFile, OutputJSON); 593 | 594 | OutputJSON.EndObject(); 595 | } 596 | 597 | OutputJSON.EndArray(); 598 | } 599 | 600 | OutputJSON.EndObject(); 601 | 602 | // UDP v6. 603 | OutputJSON.SetValSplitter(",\n\n"); 604 | OutputJSON.StartObject("udp6"); 605 | OutputJSON.SetValSplitter(", "); 606 | 607 | PMIB_UDP6TABLE udp6tableptr = (PMIB_UDP6TABLE)tablebuffer; 608 | 609 | tablesize2 = tablesize; 610 | while ((winerror = ::GetUdp6Table(udp6tableptr, &tablesize2, sort)) == ERROR_INSUFFICIENT_BUFFER) 611 | { 612 | delete[] tablebuffer; 613 | 614 | tablesize = tablesize2; 615 | tablebuffer = new char[tablesize]; 616 | udp6tableptr = (PMIB_UDP6TABLE)tablebuffer; 617 | } 618 | 619 | if (winerror != NO_ERROR) 620 | { 621 | DumpErrorMsg(OutputFile, OutputJSON, "The call to GetUdp6Table failed.", "get_udp_table_failed", winerror); 622 | 623 | result = 1; 624 | } 625 | else 626 | { 627 | OutputJSON.AppendBool("success", true); 628 | 629 | OutputJSON.StartArray("info"); 630 | 631 | for (x = 0; x < (int)udp6tableptr->dwNumEntries; x++) 632 | { 633 | localport2 = ::ntohs((u_short)udp6tableptr->table[x].dwLocalPort); 634 | 635 | // Check state, IP address, and port number for matches. 636 | if (usestates && !states[MIB_TCP_STATE_LISTEN]) continue; 637 | if (localip != NULL && !IP6AddrMatches(localip, udp6tableptr->table[x].dwLocalAddr)) continue; 638 | if (localport != (DWORD)-1 && localport != (DWORD)localport2) continue; 639 | 640 | OutputJSON.SetValSplitter(",\n\n"); 641 | OutputJSON.StartObject(); 642 | OutputJSON.SetValSplitter(", "); 643 | 644 | OutputJSON.AppendStr("state", "LISTEN"); 645 | 646 | ip6addr.sin6_addr = (in6_addr)udp6tableptr->table[x].dwLocalAddr; 647 | tempsize = TempVar.GetMaxSize(); 648 | if (::WSAAddressToStringA((LPSOCKADDR)&ip6addr, sizeof(ip6addr), NULL, TempVar.GetStr(), &tempsize) == 0) OutputJSON.AppendStr("local_ip", TempVar.GetStr()); 649 | else OutputJSON.AppendNull("local_ip"); 650 | 651 | OutputJSON.AppendUInt("local_port", localport2); 652 | 653 | OutputJSON.AppendUInt("local_scope_id", udp6tableptr->table[x].dwLocalScopeId); 654 | 655 | DumpOutput(OutputFile, OutputJSON); 656 | 657 | OutputJSON.EndObject(); 658 | } 659 | 660 | OutputJSON.EndArray(); 661 | } 662 | 663 | OutputJSON.EndObject(); 664 | } 665 | 666 | delete[] tablebuffer; 667 | } 668 | 669 | OutputJSON.EndObject(); 670 | OutputJSON.Finish(); 671 | 672 | DumpOutput(OutputFile, OutputJSON); 673 | 674 | if (!OutputFile.IsOpen()) printf("\n"); 675 | else OutputFile.Write("\n", y); 676 | 677 | OutputFile.Close(); 678 | 679 | if (localip != NULL) ::freeaddrinfo(localip); 680 | if (remoteip != NULL) ::freeaddrinfo(remoteip); 681 | 682 | // Let the OS clean up after this program. It is lazy, but whatever. 683 | if (verbose) _tprintf(_T("Return code = %i\n"), result); 684 | 685 | return result; 686 | } 687 | 688 | #ifdef SUBSYSTEM_WINDOWS 689 | #ifndef UNICODE 690 | // Swiped from: https://stackoverflow.com/questions/291424/canonical-way-to-parse-the-command-line-into-arguments-in-plain-c-windows-api 691 | LPSTR* CommandLineToArgvA(LPSTR lpCmdLine, INT *pNumArgs) 692 | { 693 | int retval; 694 | retval = ::MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, lpCmdLine, -1, NULL, 0); 695 | if (!SUCCEEDED(retval)) return NULL; 696 | 697 | LPWSTR lpWideCharStr = (LPWSTR)malloc(retval * sizeof(WCHAR)); 698 | if (lpWideCharStr == NULL) return NULL; 699 | 700 | retval = ::MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, lpCmdLine, -1, lpWideCharStr, retval); 701 | if (!SUCCEEDED(retval)) 702 | { 703 | free(lpWideCharStr); 704 | 705 | return NULL; 706 | } 707 | 708 | int numArgs; 709 | LPWSTR* args; 710 | args = ::CommandLineToArgvW(lpWideCharStr, &numArgs); 711 | free(lpWideCharStr); 712 | if (args == NULL) return NULL; 713 | 714 | int storage = numArgs * sizeof(LPSTR); 715 | for (int i = 0; i < numArgs; i++) 716 | { 717 | BOOL lpUsedDefaultChar = FALSE; 718 | retval = ::WideCharToMultiByte(CP_ACP, 0, args[i], -1, NULL, 0, NULL, &lpUsedDefaultChar); 719 | if (!SUCCEEDED(retval)) 720 | { 721 | ::LocalFree(args); 722 | 723 | return NULL; 724 | } 725 | 726 | storage += retval; 727 | } 728 | 729 | LPSTR* result = (LPSTR *)::LocalAlloc(LMEM_FIXED, storage); 730 | if (result == NULL) 731 | { 732 | ::LocalFree(args); 733 | 734 | return NULL; 735 | } 736 | 737 | int bufLen = storage - numArgs * sizeof(LPSTR); 738 | LPSTR buffer = ((LPSTR)result) + numArgs * sizeof(LPSTR); 739 | for (int i = 0; i < numArgs; ++ i) 740 | { 741 | BOOL lpUsedDefaultChar = FALSE; 742 | retval = ::WideCharToMultiByte(CP_ACP, 0, args[i], -1, buffer, bufLen, NULL, &lpUsedDefaultChar); 743 | if (!SUCCEEDED(retval)) 744 | { 745 | ::LocalFree(result); 746 | ::LocalFree(args); 747 | 748 | return NULL; 749 | } 750 | 751 | result[i] = buffer; 752 | buffer += retval; 753 | bufLen -= retval; 754 | } 755 | 756 | ::LocalFree(args); 757 | 758 | *pNumArgs = numArgs; 759 | return result; 760 | } 761 | #endif 762 | 763 | int CALLBACK WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR lpCmdLine, int /* nCmdShow */) 764 | { 765 | int argc; 766 | TCHAR **argv; 767 | int result; 768 | 769 | #ifdef UNICODE 770 | argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 771 | #else 772 | argv = CommandLineToArgvA(lpCmdLine, &argc); 773 | #endif 774 | 775 | if (argv == NULL) return 0; 776 | 777 | result = _tmain(argc, argv); 778 | 779 | ::LocalFree(argv); 780 | 781 | return result; 782 | } 783 | #endif 784 | -------------------------------------------------------------------------------- /utf8/utf8_file_dir.cpp: -------------------------------------------------------------------------------- 1 | // Cross-platform UTF-8, large file (> 2GB) and directory manipulation classes. 2 | // (C) 2016 CubicleSoft. All Rights Reserved. 3 | 4 | #define _CRT_SECURE_NO_WARNINGS 5 | 6 | #include "utf8_file_dir.h" 7 | 8 | namespace CubicleSoft 9 | { 10 | namespace UTF8 11 | { 12 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) 13 | // Apparently this structure is only included in the Windows DDK. 14 | typedef struct _REPARSE_DATA_BUFFER { 15 | ULONG ReparseTag; 16 | USHORT ReparseDataLength; 17 | USHORT Reserved; 18 | union { 19 | struct { 20 | USHORT SubstituteNameOffset; 21 | USHORT SubstituteNameLength; 22 | USHORT PrintNameOffset; 23 | USHORT PrintNameLength; 24 | ULONG Flags; 25 | WCHAR PathBuffer[1]; 26 | } SymbolicLinkReparseBuffer; 27 | struct { 28 | USHORT SubstituteNameOffset; 29 | USHORT SubstituteNameLength; 30 | USHORT PrintNameOffset; 31 | USHORT PrintNameLength; 32 | WCHAR PathBuffer[1]; 33 | } MountPointReparseBuffer; 34 | struct { 35 | UCHAR DataBuffer[1]; 36 | } GenericReparseBuffer; 37 | }; 38 | } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; 39 | 40 | #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY 41 | #define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 42 | #endif 43 | 44 | File::File() : MxFile(NULL), MxRead(false), MxWrite(false), MxCurrPos(0), MxMaxPos(0) 45 | { 46 | } 47 | 48 | File::~File() 49 | { 50 | Close(); 51 | } 52 | 53 | bool File::IsOpen() const 54 | { 55 | return (MxFile != NULL); 56 | } 57 | 58 | bool File::Open(const char *Filename, int Flags, ShareType ShareFlag, int) 59 | { 60 | Close(); 61 | 62 | DWORD WinAccess, WinShareMode, WinCreation; 63 | 64 | int Access = Flags & O_ACCMODE; 65 | 66 | if (Access == O_RDONLY) 67 | { 68 | WinAccess = GENERIC_READ; 69 | MxRead = true; 70 | MxWrite = false; 71 | } 72 | else if (Access == O_WRONLY) 73 | { 74 | WinAccess = GENERIC_WRITE; 75 | MxRead = false; 76 | MxWrite = true; 77 | } 78 | else if (Access == O_RDWR) 79 | { 80 | WinAccess = GENERIC_READ | GENERIC_WRITE; 81 | MxRead = true; 82 | MxWrite = true; 83 | } 84 | else 85 | { 86 | return false; 87 | } 88 | 89 | if (ShareFlag == ShareNone) WinShareMode = 0; 90 | else if (ShareFlag == ShareRead) WinShareMode = FILE_SHARE_READ; 91 | else if (ShareFlag == ShareWrite) WinShareMode = FILE_SHARE_WRITE; 92 | else if (ShareFlag == ShareBoth) WinShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; 93 | else return false; 94 | 95 | if (Flags & O_CREAT) WinCreation = (Flags & O_EXCL ? CREATE_NEW : OPEN_ALWAYS); 96 | else WinCreation = OPEN_EXISTING; 97 | 98 | // Verify and retrieve a UTF-16 version of the filename. 99 | WCHAR Filename2[8192]; 100 | if (!GetWindowsPlatformFilename(Filename2, 8192, Filename, (Flags & O_UNSAFE) != 0)) return false; 101 | 102 | // Open the file. 103 | SECURITY_ATTRIBUTES SecAttr; 104 | SecAttr.nLength = sizeof(SecAttr); 105 | SecAttr.lpSecurityDescriptor = NULL; 106 | SecAttr.bInheritHandle = TRUE; 107 | 108 | MxFile = ::CreateFileW(Filename2, WinAccess, WinShareMode | FILE_SHARE_DELETE, &SecAttr, WinCreation, FILE_ATTRIBUTE_NORMAL, NULL); 109 | if (MxFile == INVALID_HANDLE_VALUE) MxFile = NULL; 110 | 111 | // Deal with post-open initialization. 112 | if (MxFile != NULL) 113 | { 114 | if (Flags & O_TRUNC) 115 | { 116 | ::SetEndOfFile(MxFile); 117 | MxCurrPos = 0; 118 | MxMaxPos = 0; 119 | } 120 | else if (Flags & O_APPEND) 121 | { 122 | UpdateMaxPos(); 123 | Seek(File::SeekEnd, 0); 124 | } 125 | else 126 | { 127 | MxCurrPos = 0; 128 | UpdateMaxPos(); 129 | } 130 | } 131 | 132 | return (MxFile != NULL); 133 | } 134 | 135 | bool File::Seek(SeekType whence, std::uint64_t Pos) 136 | { 137 | if (MxFile == NULL) return false; 138 | 139 | std::uint64_t TempPos; 140 | 141 | switch (whence) 142 | { 143 | case File::SeekStart: TempPos = (Pos <= MxMaxPos ? Pos : MxMaxPos); break; 144 | case File::SeekEnd: TempPos = (Pos <= MxMaxPos ? MxMaxPos - Pos : 0); break; 145 | case File::SeekForward: TempPos = (MxCurrPos + Pos <= MxMaxPos ? MxCurrPos + Pos : MxMaxPos); break; 146 | case File::SeekBackward: TempPos = (MxCurrPos >= Pos ? MxCurrPos - Pos : 0); break; 147 | default: TempPos = MxCurrPos; break; 148 | } 149 | 150 | LONG LowWord, HighWord; 151 | LowWord = (LONG)(std::uint32_t)TempPos; 152 | HighWord = (LONG)(std::uint32_t)(TempPos >> 32); 153 | DWORD Result = ::SetFilePointer(MxFile, LowWord, &HighWord, FILE_BEGIN); 154 | if (Result == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR) return false; 155 | 156 | MxCurrPos = TempPos; 157 | 158 | return true; 159 | } 160 | 161 | bool File::Read(std::uint8_t *Data, size_t DataSize, size_t &DataRead) 162 | { 163 | if (MxFile == NULL || !MxRead) return false; 164 | 165 | if ((std::uint64_t)DataSize > MxMaxPos - MxCurrPos) 166 | { 167 | UpdateMaxPos(); 168 | if ((std::uint64_t)DataSize > MxMaxPos - MxCurrPos) DataSize = (size_t)(MxMaxPos - MxCurrPos); 169 | } 170 | 171 | DataRead = 0; 172 | if (DataSize == 0) return true; 173 | DWORD BytesRead = 0; 174 | if (!::ReadFile(MxFile, Data, (DWORD)DataSize, &BytesRead, NULL)) return false; 175 | DataRead = (size_t)BytesRead; 176 | MxCurrPos += (std::uint64_t)BytesRead; 177 | 178 | return true; 179 | } 180 | 181 | bool File::Write(const std::uint8_t *Data, size_t DataSize, size_t &DataWritten) 182 | { 183 | if (MxFile == NULL || !MxWrite) return false; 184 | 185 | DataWritten = 0; 186 | if (DataSize == 0) return true; 187 | DWORD BytesWritten = 0; 188 | if (!::WriteFile(MxFile, Data, (DWORD)DataSize, &BytesWritten, NULL)) return false; 189 | DataWritten = (size_t)BytesWritten; 190 | MxCurrPos += (std::uint64_t)BytesWritten; 191 | if (MxCurrPos > MxMaxPos) MxMaxPos = MxCurrPos; 192 | 193 | return true; 194 | } 195 | 196 | bool File::Flush() 197 | { 198 | if (MxFile == NULL || !MxWrite) return false; 199 | 200 | if (!::FlushFileBuffers(MxFile)) return false; 201 | 202 | return true; 203 | } 204 | 205 | bool File::UpdateMaxPos() 206 | { 207 | if (MxFile == NULL) return false; 208 | 209 | DWORD LowWord, HighWord; 210 | LowWord = ::GetFileSize(MxFile, &HighWord); 211 | if (LowWord == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR) MxMaxPos = 0; 212 | else MxMaxPos = (((std::uint64_t)HighWord) << 32) | ((std::uint64_t)LowWord); 213 | 214 | return true; 215 | } 216 | 217 | bool File::Close() 218 | { 219 | if (MxFile != NULL) 220 | { 221 | ::CloseHandle(MxFile); 222 | MxFile = NULL; 223 | } 224 | 225 | return true; 226 | } 227 | 228 | bool File::GetPlatformFilename(char *Result, size_t ResultSize, const char *Filename, bool AllowUnsafe) 229 | { 230 | // Check for invalid characters and strings (restricted device names) in the path/filename. Replace '/' with '\'. 231 | char TempCP; 232 | size_t x, y = strlen(Filename), OrigResultSize = ResultSize, y2; 233 | if (y + 1 > ResultSize) return false; 234 | Util::ConvertToUTF8(Filename, y + 1, sizeof(char), (std::uint8_t *)Result, ResultSize); 235 | if (!strlen(Result) || strcmp(Result, Filename)) return false; 236 | 237 | // Ignore combining code points since kernels are generally not fully UTF-8 aware. 238 | for (x = 0; x < y; x++) 239 | { 240 | if (Result[x] == '/') Result[x] = '\\'; 241 | } 242 | 243 | // Generally disallow direct device access for security reasons. 244 | if (!strncmp(Result, "\\??\\", 4)) Result[1] = '\\'; 245 | if (!_strnicmp(Result, "\\\\?\\Device\\", 11) || !strncmp(Result, "\\\\.\\", 4)) return AllowUnsafe; 246 | 247 | bool UNCPath = false, UnicodePath = false, DriveLetter = false; 248 | if (!_strnicmp(Result, "\\\\?\\UNC\\", 8)) 249 | { 250 | x = 8; 251 | UNCPath = true; 252 | UnicodePath = true; 253 | } 254 | else if (!strncmp(Result, "\\\\?\\", 4)) 255 | { 256 | x = 4; 257 | UnicodePath = true; 258 | } 259 | else 260 | { 261 | x = 0; 262 | } 263 | 264 | if (Result[x] >= 'a' && Result[x] <= 'z' && Result[x + 1] == ':') Result[x] = Result[x] - 'a' + 'A'; 265 | if (Result[x] >= 'A' && Result[x] <= 'Z' && Result[x + 1] == ':') 266 | { 267 | x += 2; 268 | DriveLetter = true; 269 | } 270 | else if (!strncmp(Result, "Volume{", 7)) DriveLetter = true; 271 | 272 | size_t LastPos = 0; 273 | for (; x < y; x++) 274 | { 275 | TempCP = Result[x]; 276 | 277 | if (TempCP < 0x20 || TempCP == 0x7F || TempCP == ':' || TempCP == '*' || TempCP == '?' || TempCP == '\"' || TempCP == '<' || TempCP == '>' || TempCP == '|') return false; 278 | else if (TempCP == '.' || TempCP == '\\') 279 | { 280 | y2 = x - LastPos; 281 | if (y2 == 3 && (!_strnicmp(Result + LastPos, "NUL", 3) || !_strnicmp(Result + LastPos, "CON", 3) || !_strnicmp(Result + LastPos, "AUX", 3) || !_strnicmp(Result + LastPos, "PRN", 3))) return false; 282 | else if (y2 == 6 && !_strnicmp(Result + LastPos, "CLOCK$", 6)) return false; 283 | else if (y2 == 4 && (!_strnicmp(Result + LastPos, "LPT", 3) || !_strnicmp(Result + LastPos, "COM", 3)) && Result[x - 1] >= '0' && Result[x - 1] <= '9') return false; 284 | 285 | if (TempCP == '\\') LastPos = x + 1; 286 | } 287 | } 288 | 289 | y2 = y - LastPos; 290 | if (y2 == 3 && (!_strnicmp(Result + LastPos, "NUL", 3) || !_strnicmp(Result + LastPos, "CON", 3) || !_strnicmp(Result + LastPos, "AUX", 3) || !_strnicmp(Result + LastPos, "PRN", 3))) return false; 291 | else if (y2 == 6 && !_strnicmp(Result + LastPos, "CLOCK$", 6)) return false; 292 | else if (y2 == 4 && (!_strnicmp(Result + LastPos, "LPT", 3) || !_strnicmp(Result + LastPos, "COM", 3)) && Result[y - 1] >= '0' && Result[y - 1] <= '9') return false; 293 | 294 | // Normalize absolute filenames so they have a '\\?\' prefix. 295 | if (DriveLetter) 296 | { 297 | if (!UnicodePath) 298 | { 299 | // Drive letter found without Unicode prefix. 300 | if (y + 4 > OrigResultSize) return false; 301 | memmove(Result + 4, Result, y + 1); 302 | memcpy(Result, "\\\\?\\", 4); 303 | } 304 | 305 | // Attempt to resolve \\?\Volume{GUID}\ paths. 306 | char *Result3 = strchr(Result + 4, '\\'); 307 | if (!strncmp(Result, "\\\\?\\Volume{", 11) && Result3 != NULL) 308 | { 309 | Result[1] = '?'; 310 | 311 | HKEY RegKey; 312 | if (::RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\MountedDevices", 0, KEY_READ, &RegKey) == ERROR_SUCCESS) 313 | { 314 | BYTE Buffer[1024]; 315 | DWORD BufferSize = 1024; 316 | 317 | if (::RegQueryValueExA(RegKey, Result, NULL, NULL, Buffer, &BufferSize) == ERROR_SUCCESS) 318 | { 319 | // Found a match for \??\Volume{GUID}. 320 | char ValueName[100]; 321 | BYTE Buffer2[1024]; 322 | DWORD KeyIndex = 0, ValueNameSize = 100, BufferSize2 = 1024; 323 | 324 | while (::RegEnumValueA(RegKey, KeyIndex++, ValueName, &ValueNameSize, NULL, NULL, Buffer2, &BufferSize2) == ERROR_SUCCESS) 325 | { 326 | if (BufferSize == BufferSize2 && !strncmp(ValueName, "\\DosDevice\\", 11) && !memcmp(Buffer, Buffer2, BufferSize)) 327 | { 328 | // Found a match. Resolve the volume to the drive letter. 329 | Result[4] = ValueName[11]; 330 | Result[5] = ':'; 331 | memmove(Result + 6, Result3, y + 1 - (Result3 - Result)); 332 | 333 | break; 334 | } 335 | 336 | ValueNameSize = 100; 337 | BufferSize2 = 1024; 338 | } 339 | } 340 | 341 | ::RegCloseKey(RegKey); 342 | } 343 | 344 | Result[1] = '\\'; 345 | } 346 | } 347 | else if (!UNCPath) 348 | { 349 | if (UnicodePath) 350 | { 351 | // Unicode network path found without UNC prefix. 352 | if (y + 4 > OrigResultSize) return false; 353 | memmove(Result + 8, Result + 4, y + 1 - 4); 354 | memcpy(Result + 4, "UNC\\", 4); 355 | } 356 | else if (Result[0] == '\\' && Result[1] == '\\') 357 | { 358 | // Network path found without Unicode and UNC prefixes. 359 | if (y + 6 > OrigResultSize) return false; 360 | memmove(Result + 7, Result + 1, y + 1 - 1); 361 | memcpy(Result + 1, "\\?\\UNC", 6); 362 | } 363 | } 364 | 365 | return true; 366 | } 367 | 368 | // Expects Filename to be a string returned from GetPlatformFilename(). 369 | void File::GetPlatformFilenameInfo(FilenameInfo &Result, const char *Filename) 370 | { 371 | size_t x, y = strlen(Filename); 372 | 373 | Result.Length = y; 374 | 375 | if (!_strnicmp(Filename, "\\\\?\\UNC\\", 8)) Result.StartVolume = 8; 376 | else if (!strncmp(Filename, "\\\\?\\", 4)) Result.StartVolume = 4; 377 | else Result.StartVolume = 0; 378 | 379 | if (!Result.StartVolume) Result.StartPath = 0; 380 | else 381 | { 382 | for (x = Result.StartVolume; x < y && Filename[x] != '\\'; x++); 383 | Result.StartPath = x; 384 | } 385 | 386 | for (x = y; x > Result.StartPath && Filename[x - 1] != '\\'; x--); 387 | Result.StartFilename = x; 388 | 389 | for (; x < y && Filename[x] != '.'; x++); 390 | Result.StartLastExtension = Result.StartExtension = x; 391 | 392 | for (; x < y; x++) 393 | { 394 | if (Filename[x] == '.') Result.StartLastExtension = x; 395 | } 396 | } 397 | 398 | bool File::GetAbsoluteFilename(char *Result, size_t ResultSize, const char *BaseDir, const char *Filename, bool TrailingSlash) 399 | { 400 | char Filename2[8192], Filename3[8192]; 401 | 402 | if (!GetPlatformFilename(Filename3, 8192, Filename)) return false; 403 | 404 | // Replace '\.\' with '\'. 405 | size_t x, x2, y = strlen(Filename3); 406 | x2 = 0; 407 | for (x = 0; x < y; x++) 408 | { 409 | while (Filename3[x] == '\\' && Filename3[x + 1] == '.' && Filename3[x + 2] == '\\') x += 2; 410 | 411 | Filename3[x2++] = Filename3[x]; 412 | 413 | if (Filename3[x] == '\\' && Filename3[x + 1] == '.' && Filename3[x + 2] == '\0') break; 414 | } 415 | if (x2 >= 3 && Filename3[x2 - 3] == '\\' && Filename3[x2 - 2] == '.' && Filename3[x2 - 1] == '.') 416 | { 417 | if (x2 >= 8191) return false; 418 | Filename3[x2++] = '\\'; 419 | } 420 | Filename3[x2] = '\0'; 421 | 422 | // Get some parsed information about the input data. 423 | FilenameInfo FileInfo2, FileInfo3; 424 | GetPlatformFilenameInfo(FileInfo3, Filename3); 425 | 426 | // Copy the absolute volume. 427 | if (FileInfo3.StartVolume) 428 | { 429 | memcpy(Filename2, Filename3, FileInfo3.StartPath); 430 | Filename2[FileInfo3.StartPath] = '\0'; 431 | } 432 | else 433 | { 434 | if (BaseDir != NULL) 435 | { 436 | if (!GetPlatformFilename(Filename2, 8192, BaseDir)) return false; 437 | } 438 | else 439 | { 440 | if (!Dir::Getcwd(Filename2, 8192)) return false; 441 | } 442 | 443 | // Require the base directory to be absolute. 444 | if (strncmp(Filename2, "\\\\?\\", 4)) return false; 445 | } 446 | 447 | y = strlen(Filename2); 448 | if (y && Filename2[y - 1] != '\\') 449 | { 450 | if (y >= 8191) return false; 451 | Filename2[y++] = '\\'; 452 | Filename2[y] = '\0'; 453 | } 454 | 455 | GetPlatformFilenameInfo(FileInfo2, Filename2); 456 | if (Filename3[FileInfo3.StartPath] == '\\') FileInfo2.Length = FileInfo2.StartPath; 457 | 458 | // Make sure the string always starts with a path separator. 459 | if (FileInfo2.Length >= 8191) return false; 460 | if (FileInfo2.StartPath == FileInfo2.Length) Filename2[FileInfo2.Length++] = '\\'; 461 | 462 | // Parse the filename into the base directory. 463 | for (x = FileInfo3.StartPath; x < FileInfo3.Length; x++) 464 | { 465 | while (Filename3[x] == '\\' && Filename3[x + 1] == '.' && Filename3[x + 2] == '.' && Filename3[x + 3] == '\\') 466 | { 467 | if (FileInfo2.Length > FileInfo2.StartPath) 468 | { 469 | for (; FileInfo2.Length > FileInfo2.StartPath && Filename2[FileInfo2.Length - 1] == '\\'; FileInfo2.Length--); 470 | for (; FileInfo2.Length > FileInfo2.StartPath && Filename2[FileInfo2.Length - 1] != '\\'; FileInfo2.Length--); 471 | } 472 | 473 | x += 3; 474 | } 475 | 476 | if (FileInfo2.Length >= 8191) return false; 477 | Filename2[FileInfo2.Length++] = Filename3[x]; 478 | } 479 | 480 | // Copy the volume. 481 | for (x = 0; x < ResultSize && x < FileInfo2.StartPath; x++) Result[x] = Filename2[x]; 482 | 483 | // Copy the rest. 484 | x2 = x; 485 | for (; x2 < ResultSize && x < FileInfo2.Length; x++) 486 | { 487 | while (Filename2[x] == '\\' && Filename2[x + 1] == '\\') x++; 488 | 489 | Result[x2++] = Filename2[x]; 490 | } 491 | if (TrailingSlash && x2 && Result[x2 - 1] != '\\' && x2 < ResultSize) Result[x2++] = '\\'; 492 | if (!TrailingSlash && x2 && Result[x2 - 1] == '\\') x2--; 493 | if (x2 >= ResultSize) return false; 494 | Result[x2] = '\0'; 495 | 496 | // Sanity check. 497 | for (x = 0; x < x2; x++) 498 | { 499 | if (Result[x] == '\\' && Result[x + 1] == '.') 500 | { 501 | if (Result[x + 2] == '\\') return false; 502 | if (Result[x + 2] == '.' && Result[x + 3] == '\\') return false; 503 | } 504 | } 505 | 506 | return true; 507 | } 508 | 509 | bool File::GetWindowsPlatformFilename(LPWSTR Result, size_t ResultSize, const char *Filename, bool AllowUnsafe) 510 | { 511 | char Filename2[8192]; 512 | 513 | if (!GetPlatformFilename(Filename2, 8192, Filename, AllowUnsafe)) return false; 514 | 515 | Util::ConvertFromUTF8((std::uint8_t *)Filename2, strlen(Filename2) + 1, Result, ResultSize, sizeof(WCHAR)); 516 | 517 | return true; 518 | } 519 | 520 | bool File::GetWindowsFilenameInfo(BY_HANDLE_FILE_INFORMATION *Result, LPWSTR Filename) 521 | { 522 | SECURITY_ATTRIBUTES SecAttr; 523 | SecAttr.nLength = sizeof(SecAttr); 524 | SecAttr.lpSecurityDescriptor = NULL; 525 | SecAttr.bInheritHandle = TRUE; 526 | 527 | HANDLE TempFile = ::CreateFileW(Filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SecAttr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 528 | if (TempFile == INVALID_HANDLE_VALUE) return false; 529 | 530 | // Retrieve the information about the file. 531 | bool Result2 = (::GetFileInformationByHandle(TempFile, Result) != 0); 532 | 533 | ::CloseHandle(TempFile); 534 | 535 | return Result2; 536 | } 537 | 538 | bool File::Exists(const char *Filename) 539 | { 540 | WCHAR Filename2[8192]; 541 | if (!GetWindowsPlatformFilename(Filename2, 8192, Filename)) return false; 542 | 543 | DWORD TempAttrs; 544 | if ((TempAttrs = ::GetFileAttributesW(Filename2)) == (DWORD)-1) return false; 545 | 546 | return true; 547 | } 548 | 549 | // Only pass in absolute filenames. 550 | bool File::Realpath(char *Result, size_t ResultSize, const char *Filename) 551 | { 552 | char Filename2[8192], Filename3[8192]; 553 | 554 | // Normalize input. 555 | if (!File::GetPlatformFilename(Filename2, 8192, Filename)) return false; 556 | 557 | // Require the filename to be absolute. 558 | if (strncmp(Filename2, "\\\\?\\", 4)) return false; 559 | 560 | // Get some parsed information about the input data. 561 | FilenameInfo FileInfo; 562 | GetPlatformFilenameInfo(FileInfo, Filename2); 563 | 564 | // Copy the prefix and volume. 565 | memcpy(Filename3, Filename2, FileInfo.StartPath); 566 | 567 | size_t x, x2 = FileInfo.StartPath, x3, NumLinks = 0; 568 | HANDLE FindHandle; 569 | WCHAR FindFilename[8192]; 570 | WIN32_FIND_DATAW FindData; 571 | bool Processed; 572 | while (FileInfo.StartPath < FileInfo.Length) 573 | { 574 | // Append the next chunk. 575 | if (x2 >= 8191) return false; 576 | Filename3[x2++] = '\\'; 577 | x = x2; 578 | for (; FileInfo.StartPath < FileInfo.Length && Filename2[FileInfo.StartPath] == '\\'; FileInfo.StartPath++); 579 | for (; FileInfo.StartPath < FileInfo.Length && Filename2[FileInfo.StartPath] != '\\'; FileInfo.StartPath++) 580 | { 581 | if (x2 >= 8191) return false; 582 | Filename3[x2++] = Filename2[FileInfo.StartPath]; 583 | } 584 | Filename3[x2] = '\0'; 585 | 586 | // If possible, convert the chunk to a long filename. 587 | x3 = 8192; 588 | Util::ConvertFromUTF8((std::uint8_t *)Filename3, x2 + 1, FindFilename, x3, sizeof(WCHAR)); 589 | FindHandle = ::FindFirstFileW(FindFilename, &FindData); 590 | if (FindHandle != INVALID_HANDLE_VALUE) 591 | { 592 | ::FindClose(FindHandle); 593 | 594 | Processed = false; 595 | if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (FindData.dwReserved0 == IO_REPARSE_TAG_SYMLINK || FindData.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)) 596 | { 597 | NumLinks++; 598 | if (NumLinks > 20) return false; 599 | 600 | // Process the symbolic link/mount point. 601 | SECURITY_ATTRIBUTES SecAttr; 602 | SecAttr.nLength = sizeof(SecAttr); 603 | SecAttr.lpSecurityDescriptor = NULL; 604 | SecAttr.bInheritHandle = TRUE; 605 | 606 | FindHandle = ::CreateFileW(FindFilename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SecAttr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); 607 | if (FindHandle != INVALID_HANDLE_VALUE) 608 | { 609 | char TempReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0}; 610 | REPARSE_DATA_BUFFER *TempReparse = (REPARSE_DATA_BUFFER *)TempReparseBuffer; 611 | DWORD ReparseSize; 612 | 613 | if (::DeviceIoControl(FindHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, TempReparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &ReparseSize, NULL)) 614 | { 615 | // A symbolic link or mount point has been successfully accessed. 616 | char Filename4[8192]; 617 | x3 = 8192; 618 | 619 | if (FindData.dwReserved0 == IO_REPARSE_TAG_SYMLINK) Util::ConvertToUTF8(TempReparse->SymbolicLinkReparseBuffer.PathBuffer + TempReparse->SymbolicLinkReparseBuffer.SubstituteNameOffset, TempReparse->SymbolicLinkReparseBuffer.SubstituteNameLength, sizeof(WCHAR), (std::uint8_t *)Filename4, x3); 620 | else Util::ConvertToUTF8(TempReparse->MountPointReparseBuffer.PathBuffer + TempReparse->MountPointReparseBuffer.SubstituteNameOffset, TempReparse->MountPointReparseBuffer.SubstituteNameLength, sizeof(WCHAR), (std::uint8_t *)Filename4, x3); 621 | 622 | // Create a new absolute source path. 623 | Filename2[FileInfo.StartPath] = '\0'; 624 | if (!GetAbsoluteFilename(Filename3, 8192, Filename2, Filename4, false)) return false; 625 | if (FileInfo.StartPath < FileInfo.Length) Filename2[FileInfo.StartPath] = '\\'; 626 | x = strlen(Filename3); 627 | if (x + FileInfo.Length - FileInfo.StartPath + 1 > 8191) return false; 628 | memcpy(Filename3 + x, Filename2 + FileInfo.StartPath, FileInfo.Length - FileInfo.StartPath + 1); 629 | strcpy(Filename2, Filename3); 630 | 631 | // Reset the loop. 632 | // Require the filename to be absolute. 633 | if (strncmp(Filename2, "\\\\?\\", 4)) return false; 634 | 635 | // Get some parsed information about the input data. 636 | GetPlatformFilenameInfo(FileInfo, Filename2); 637 | 638 | // Copy the prefix and volume. 639 | memcpy(Filename3, Filename2, FileInfo.StartPath); 640 | 641 | x2 = FileInfo.StartPath; 642 | x3 = 0; 643 | 644 | Processed = true; 645 | } 646 | 647 | ::CloseHandle(FindHandle); 648 | } 649 | } 650 | 651 | if (!Processed) 652 | { 653 | // Exchange the current chunk with the new one. 654 | x2 = 8192 - x; 655 | Util::ConvertToUTF8(FindData.cFileName, wcslen(FindData.cFileName), sizeof(WCHAR), (std::uint8_t *)Filename3 + x, x2); 656 | x2 += x; 657 | } 658 | } 659 | } 660 | Filename3[x2++] = '\0'; 661 | if (ResultSize < x2) return false; 662 | memcpy(Result, Filename3, x2); 663 | 664 | return Exists(Result); 665 | } 666 | 667 | bool File::Chmod(const char *Filename, int Mode) 668 | { 669 | WCHAR Filename2[8192]; 670 | if (!GetWindowsPlatformFilename(Filename2, 8192, Filename)) return false; 671 | 672 | DWORD TempAttrs; 673 | 674 | if ((TempAttrs = ::GetFileAttributesW(Filename2)) == (DWORD)-1) return false; 675 | if (TempAttrs & FILE_ATTRIBUTE_DIRECTORY) return false; 676 | 677 | TempAttrs &= ~FILE_ATTRIBUTE_READONLY; 678 | TempAttrs &= ~FILE_ATTRIBUTE_NORMAL; 679 | if ((Mode & 0333) == 0 && (Mode & 0444) != 0) TempAttrs |= FILE_ATTRIBUTE_READONLY; 680 | if (TempAttrs == 0) TempAttrs |= FILE_ATTRIBUTE_NORMAL; 681 | if (!::SetFileAttributesW(Filename2, TempAttrs)) return false; 682 | 683 | return true; 684 | } 685 | 686 | // Owner and group are mostly Linux. Windows has (D)ACLs, which are tricky to manipulate. 687 | bool File::Chown(const char *, const char *) 688 | { 689 | return true; 690 | } 691 | 692 | bool File::Chgrp(const char *, const char *) 693 | { 694 | return true; 695 | } 696 | 697 | bool File::Copy(const char *SrcFilename, const char *DestFilename) 698 | { 699 | WCHAR SrcFilename2[8192], DestFilename2[8192]; 700 | if (!GetWindowsPlatformFilename(SrcFilename2, 8192, SrcFilename)) return false; 701 | if (!GetWindowsPlatformFilename(DestFilename2, 8192, DestFilename)) return false; 702 | 703 | // Prevent copying a file onto itself AND get file time information. 704 | BY_HANDLE_FILE_INFORMATION SrcFileInfo, DestFileInfo; 705 | if (!File::GetWindowsFilenameInfo(&SrcFileInfo, SrcFilename2)) return false; 706 | 707 | if (File::GetWindowsFilenameInfo(&DestFileInfo, DestFilename2) && SrcFileInfo.dwVolumeSerialNumber == DestFileInfo.dwVolumeSerialNumber && SrcFileInfo.nFileSizeHigh == DestFileInfo.nFileSizeHigh && SrcFileInfo.nFileSizeLow == DestFileInfo.nFileSizeLow && SrcFileInfo.nFileIndexHigh == DestFileInfo.nFileIndexHigh && SrcFileInfo.nFileIndexLow == DestFileInfo.nFileIndexLow) return false; 708 | 709 | DWORD TempAttrs2 = ::GetFileAttributesW(DestFilename2); 710 | ::SetFileAttributesW(DestFilename2, FILE_ATTRIBUTE_NORMAL); 711 | bool Result = (::CopyFileW(SrcFilename2, DestFilename2, FALSE) != 0); 712 | ::SetFileAttributesW(DestFilename2, (Result ? SrcFileInfo.dwFileAttributes : (TempAttrs2 != (DWORD)-1 ? TempAttrs2 : FILE_ATTRIBUTE_NORMAL))); 713 | 714 | if (Result) 715 | { 716 | // Update the last modified timestamp (might have been changed by SetFileAttributesW()). 717 | std::uint64_t TempLastModified = (std::uint64_t)(((std::uint64_t)(((std::uint64_t)SrcFileInfo.ftLastWriteTime.dwHighDateTime) << 32 | ((std::uint64_t)SrcFileInfo.ftLastWriteTime.dwLowDateTime)) - (std::uint64_t)116444736000000000ULL) / (std::uint64_t)10); 718 | SetFileTimes(DestFilename, NULL, NULL, &TempLastModified); 719 | } 720 | 721 | return Result; 722 | } 723 | 724 | bool File::Move(const char *SrcFilename, const char *DestFilename) 725 | { 726 | WCHAR SrcFilename2[8192], DestFilename2[8192]; 727 | if (!GetWindowsPlatformFilename(SrcFilename2, 8192, SrcFilename)) return false; 728 | if (!GetWindowsPlatformFilename(DestFilename2, 8192, DestFilename)) return false; 729 | 730 | bool Result = (::MoveFileW(SrcFilename2, DestFilename2) != 0); 731 | if (!Result) 732 | { 733 | Result = File::Copy(SrcFilename, DestFilename); 734 | if (Result) ::DeleteFileW(SrcFilename2); 735 | } 736 | 737 | return Result; 738 | } 739 | 740 | bool File::Delete(const char *Filename) 741 | { 742 | WCHAR Filename2[8192]; 743 | if (!GetWindowsPlatformFilename(Filename2, 8192, Filename)) return false; 744 | 745 | ::SetFileAttributesW(Filename2, FILE_ATTRIBUTE_NORMAL); 746 | 747 | return (::DeleteFileW(Filename2) != 0); 748 | } 749 | 750 | bool File::Stat(FileStat &Result, const char *Filename, bool Link) 751 | { 752 | char Filename2[8192]; 753 | WCHAR Filename3[8192]; 754 | size_t TempSize = 8192; 755 | 756 | if (!GetPlatformFilename(Filename2, 8192, Filename)) return false; 757 | 758 | Util::ConvertFromUTF8((std::uint8_t *)Filename2, strlen(Filename2) + 1, Filename3, TempSize, sizeof(WCHAR)); 759 | 760 | WIN32_FILE_ATTRIBUTE_DATA AttrData = {0}; 761 | 762 | if (!::GetFileAttributesExW(Filename3, GetFileExInfoStandard, &AttrData)) return false; 763 | 764 | // Build the stat structure manually. 765 | // Get some parsed information about the input data for st_dev. 766 | FilenameInfo FileInfo; 767 | GetPlatformFilenameInfo(FileInfo, Filename2); 768 | 769 | if (FileInfo.StartPath - FileInfo.StartVolume == 2 && Filename2[FileInfo.StartVolume] >= 'A' && Filename2[FileInfo.StartVolume] <= 'Z' && Filename2[FileInfo.StartVolume + 1] == ':') 770 | { 771 | Result.st_dev = Filename2[FileInfo.StartVolume] - 'A'; 772 | } 773 | else if (FileInfo.StartVolume > 0) 774 | { 775 | Result.st_dev = 0; 776 | } 777 | else 778 | { 779 | char Filename4[8192]; 780 | 781 | if (!Dir::Getcwd(Filename4, 8192)) return false; 782 | 783 | FilenameInfo FileInfo2; 784 | GetPlatformFilenameInfo(FileInfo2, Filename4); 785 | 786 | if (FileInfo2.StartPath - FileInfo2.StartVolume == 2 && Filename4[FileInfo2.StartVolume] >= 'A' && Filename4[FileInfo2.StartVolume] <= 'Z' && Filename4[FileInfo2.StartVolume + 1] == ':') 787 | { 788 | Result.st_dev = Filename4[FileInfo2.StartVolume] - 'A'; 789 | } 790 | else if (FileInfo2.StartVolume > 0) 791 | { 792 | Result.st_dev = 0; 793 | } 794 | else 795 | { 796 | Result.st_dev = -1; 797 | } 798 | } 799 | 800 | Result.st_ino = 0; 801 | Result.st_nlink = 1; 802 | bool Processed = false; 803 | if (Link && AttrData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) 804 | { 805 | // Process the symbolic link/mount point. 806 | HANDLE TempHandle; 807 | SECURITY_ATTRIBUTES SecAttr; 808 | SecAttr.nLength = sizeof(SecAttr); 809 | SecAttr.lpSecurityDescriptor = NULL; 810 | SecAttr.bInheritHandle = TRUE; 811 | 812 | TempHandle = ::CreateFileW(Filename3, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SecAttr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); 813 | if (TempHandle != INVALID_HANDLE_VALUE) 814 | { 815 | char TempReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0}; 816 | REPARSE_DATA_BUFFER *TempReparse = (REPARSE_DATA_BUFFER *)TempReparseBuffer; 817 | DWORD ReparseSize; 818 | 819 | if (::DeviceIoControl(TempHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, TempReparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &ReparseSize, NULL)) 820 | { 821 | if (TempReparse->ReparseTag == IO_REPARSE_TAG_SYMLINK || TempReparse->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) 822 | { 823 | Result.st_mode = _S_IFLNK; 824 | 825 | Processed = true; 826 | } 827 | } 828 | 829 | ::CloseHandle(TempHandle); 830 | } 831 | } 832 | 833 | if (!Processed) Result.st_mode = (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? (_S_IFDIR | 0111) : _S_IFREG); 834 | 835 | Result.st_mode |= (AttrData.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0444 : 0666); 836 | 837 | if (!(AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && FileInfo.Length > 4 && Filename2[FileInfo.Length - 4] == '.') 838 | { 839 | if (!_strnicmp(Filename2 + FileInfo.Length - 3, "exe", 3) || !_strnicmp(Filename2 + FileInfo.Length - 3, "com", 3) || !_strnicmp(Filename2 + FileInfo.Length - 3, "bat", 3) || !_strnicmp(Filename2 + FileInfo.Length - 3, "cmd", 3)) 840 | { 841 | Result.st_mode |= 0111; 842 | } 843 | } 844 | 845 | Result.st_uid = 0; 846 | Result.st_gid = 0; 847 | Result.st_rdev = Result.st_dev; 848 | Result.st_size = (((std::uint64_t)AttrData.nFileSizeHigh) << 32) | ((std::uint64_t)AttrData.nFileSizeLow); 849 | Result.st_atime = ConvertFILETIMEToUnixTime(AttrData.ftLastAccessTime); 850 | Result.st_mtime = ConvertFILETIMEToUnixTime(AttrData.ftLastWriteTime); 851 | Result.st_ctime = ConvertFILETIMEToUnixTime(AttrData.ftCreationTime); 852 | 853 | return true; 854 | } 855 | 856 | bool File::SetThreadProcessPrivilege(LPCWSTR PrivilegeName, bool Enable) 857 | { 858 | HANDLE Token; 859 | TOKEN_PRIVILEGES TokenPrivs; 860 | LUID TempLuid; 861 | bool Result; 862 | 863 | if (!::LookupPrivilegeValueW(NULL, PrivilegeName, &TempLuid)) return false; 864 | 865 | if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &Token)) 866 | { 867 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token)) return false; 868 | } 869 | 870 | TokenPrivs.PrivilegeCount = 1; 871 | TokenPrivs.Privileges[0].Luid = TempLuid; 872 | TokenPrivs.Privileges[0].Attributes = (Enable ? SE_PRIVILEGE_ENABLED : 0); 873 | 874 | Result = (::AdjustTokenPrivileges(Token, FALSE, &TokenPrivs, 0, NULL, NULL) && ::GetLastError() == ERROR_SUCCESS); 875 | 876 | ::CloseHandle(Token); 877 | 878 | return Result; 879 | } 880 | 881 | // Unfortunately, creating a symbolic link on Windows requires the SE_CREATE_SYMBOLIC_LINK_NAME privilege. 882 | // The privilege is only available to elevated processes. Run from a command-line: whoami /priv 883 | bool File::Symlink(const char *Src, const char *Dest) 884 | { 885 | // Enable the privilege in the thread/process token if the user has the privilege but it is disabled. 886 | SetThreadProcessPrivilege(L"SeCreateSymbolicLinkPrivilege", true); 887 | 888 | HMODULE TempModule = ::LoadLibraryA("KERNEL32.DLL"); 889 | if (TempModule == NULL) return false; 890 | CreateSymbolicLinkWFunc CreateSymbolicLinkWPtr = (CreateSymbolicLinkWFunc)::GetProcAddress(TempModule, "CreateSymbolicLinkW"); 891 | if (CreateSymbolicLinkWPtr == NULL) return false; 892 | 893 | char Src2[8192], Dest2[8192]; 894 | if (!GetAbsoluteFilename(Src2, 8192, NULL, Src, false)) return false; 895 | if (!GetAbsoluteFilename(Dest2, 8192, Src2, Dest, false)) return false; 896 | 897 | WCHAR Filename2[8192], Filename3[8192]; 898 | if (!GetWindowsPlatformFilename(Filename2, 8192, Dest2)) return false; 899 | DWORD Flags = ::GetFileAttributesW(Filename2); 900 | if (Flags == (DWORD)-1) return false; 901 | Flags = (Flags & FILE_ATTRIBUTE_DIRECTORY ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0); 902 | 903 | if (!GetWindowsPlatformFilename(Filename2, 8192, Src)) return false; 904 | if (!GetWindowsPlatformFilename(Filename3, 8192, Dest)) return false; 905 | 906 | return ((*CreateSymbolicLinkWPtr)(Filename2, Filename3, Flags) != 0); 907 | } 908 | 909 | bool File::Readlink(char *Result, size_t ResultSize, const char *Filename) 910 | { 911 | WCHAR Filename2[8192]; 912 | if (!GetWindowsPlatformFilename(Filename2, 8192, Filename)) return false; 913 | 914 | DWORD TempAttrs; 915 | if ((TempAttrs = ::GetFileAttributesW(Filename2)) == (DWORD)-1) return false; 916 | 917 | bool Found = false; 918 | 919 | if (TempAttrs & FILE_ATTRIBUTE_REPARSE_POINT) 920 | { 921 | // Process the symbolic link/mount point. 922 | HANDLE TempHandle; 923 | SECURITY_ATTRIBUTES SecAttr; 924 | SecAttr.nLength = sizeof(SecAttr); 925 | SecAttr.lpSecurityDescriptor = NULL; 926 | SecAttr.bInheritHandle = TRUE; 927 | 928 | TempHandle = ::CreateFileW(Filename2, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SecAttr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); 929 | if (TempHandle != INVALID_HANDLE_VALUE) 930 | { 931 | char TempReparseBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0}; 932 | REPARSE_DATA_BUFFER *TempReparse = (REPARSE_DATA_BUFFER *)TempReparseBuffer; 933 | DWORD ReparseSize; 934 | 935 | if (::DeviceIoControl(TempHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, TempReparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &ReparseSize, NULL)) 936 | { 937 | // Copy the symbolic link or mount point. 938 | if (TempReparse->ReparseTag == IO_REPARSE_TAG_SYMLINK || TempReparse->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) 939 | { 940 | size_t y = ResultSize; 941 | 942 | if (TempReparse->ReparseTag == IO_REPARSE_TAG_SYMLINK) Util::ConvertToUTF8(TempReparse->SymbolicLinkReparseBuffer.PathBuffer + TempReparse->SymbolicLinkReparseBuffer.SubstituteNameOffset, TempReparse->SymbolicLinkReparseBuffer.SubstituteNameLength, sizeof(WCHAR), (std::uint8_t *)Result, y); 943 | else Util::ConvertToUTF8(TempReparse->MountPointReparseBuffer.PathBuffer + TempReparse->MountPointReparseBuffer.SubstituteNameOffset, TempReparse->MountPointReparseBuffer.SubstituteNameLength, sizeof(WCHAR), (std::uint8_t *)Result, y); 944 | 945 | if (y < ResultSize) 946 | { 947 | Result[y] = '\0'; 948 | 949 | Found = true; 950 | } 951 | } 952 | } 953 | 954 | ::CloseHandle(TempHandle); 955 | } 956 | } 957 | 958 | return Found; 959 | } 960 | 961 | __time64_t File::ConvertFILETIMEToUnixTime(FILETIME TempTime) 962 | { 963 | __time64_t Result; 964 | ULARGE_INTEGER TempTime2; 965 | 966 | TempTime2.HighPart = TempTime.dwHighDateTime; 967 | TempTime2.LowPart = TempTime.dwLowDateTime; 968 | Result = (__time64_t)((TempTime2.QuadPart - (std::uint64_t)116444736000000000ULL) / (std::uint64_t)10000000); 969 | 970 | return Result; 971 | } 972 | 973 | FILETIME File::ConvertUnixMicrosecondTimeToFILETIME(std::uint64_t TempTime) 974 | { 975 | ULARGE_INTEGER TempTime2; 976 | FILETIME Result; 977 | 978 | TempTime = (TempTime * 10) + (std::uint64_t)116444736000000000ULL; 979 | 980 | TempTime2.QuadPart = TempTime; 981 | Result.dwHighDateTime = TempTime2.HighPart; 982 | Result.dwLowDateTime = TempTime2.LowPart; 983 | 984 | return Result; 985 | } 986 | 987 | bool File::SetFileTimes(const char *Filename, std::uint64_t *Creation, std::uint64_t *LastAccess, std::uint64_t *LastUpdate) 988 | { 989 | if (Creation == NULL && LastAccess == NULL && LastUpdate == NULL) return false; 990 | 991 | WCHAR Filename2[8192]; 992 | if (!GetWindowsPlatformFilename(Filename2, 8192, Filename)) return false; 993 | 994 | SECURITY_ATTRIBUTES SecAttr; 995 | SecAttr.nLength = sizeof(SecAttr); 996 | SecAttr.lpSecurityDescriptor = NULL; 997 | SecAttr.bInheritHandle = TRUE; 998 | 999 | HANDLE TempFile = ::CreateFileW(Filename2, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SecAttr, OPEN_EXISTING, 0, NULL); 1000 | if (TempFile == INVALID_HANDLE_VALUE) return false; 1001 | 1002 | // Prevent the OS from updating the timestamps. 1003 | FILETIME TempTime, TempTime2, TempTime3; 1004 | TempTime.dwHighDateTime = 0xFFFFFFFF; 1005 | TempTime.dwLowDateTime = 0xFFFFFFFF; 1006 | ::SetFileTime(TempFile, NULL, &TempTime, &TempTime); 1007 | 1008 | // Update the desired timestamps. 1009 | if (Creation != NULL) TempTime = File::ConvertUnixMicrosecondTimeToFILETIME(*Creation); 1010 | if (LastAccess != NULL) TempTime2 = File::ConvertUnixMicrosecondTimeToFILETIME(*LastAccess); 1011 | if (LastUpdate != NULL) TempTime3 = File::ConvertUnixMicrosecondTimeToFILETIME(*LastUpdate); 1012 | bool Result2 = (::SetFileTime(TempFile, (Creation != NULL ? &TempTime : NULL), (LastAccess != NULL ? &TempTime2 : NULL), (LastUpdate != NULL ? &TempTime3 : NULL)) != 0); 1013 | 1014 | ::CloseHandle(TempFile); 1015 | 1016 | return Result2; 1017 | } 1018 | 1019 | 1020 | Dir::Dir() : MxDir(INVALID_HANDLE_VALUE) 1021 | { 1022 | } 1023 | 1024 | Dir::~Dir() 1025 | { 1026 | Close(); 1027 | } 1028 | 1029 | bool Dir::Open(const char *Dirname) 1030 | { 1031 | Close(); 1032 | 1033 | char Dirname2[8192]; 1034 | size_t y; 1035 | 1036 | if (!File::GetAbsoluteFilename(Dirname2, 8192, NULL, Dirname, true)) return false; 1037 | y = strlen(Dirname2); 1038 | if (y >= 8191) return false; 1039 | Dirname2[y++] = '*'; 1040 | Dirname2[y++] = '\0'; 1041 | 1042 | WCHAR Dirname3[8192]; 1043 | size_t y2 = 8192; 1044 | Util::ConvertFromUTF8((std::uint8_t *)Dirname2, y, Dirname3, y2, sizeof(WCHAR)); 1045 | 1046 | MxDir = ::FindFirstFileW(Dirname3, &MxFindData); 1047 | 1048 | return (MxDir != INVALID_HANDLE_VALUE); 1049 | } 1050 | 1051 | bool Dir::Read(char *Filename, size_t Size) 1052 | { 1053 | if (MxDir == INVALID_HANDLE_VALUE) return false; 1054 | 1055 | size_t y = Size; 1056 | 1057 | Util::ConvertToUTF8(MxFindData.cFileName, wcslen(MxFindData.cFileName) + 1, sizeof(WCHAR), (std::uint8_t *)Filename, Size); 1058 | if (y == Size) return false; 1059 | 1060 | if (!::FindNextFileW(MxDir, &MxFindData)) Close(); 1061 | 1062 | return true; 1063 | } 1064 | 1065 | bool Dir::Close() 1066 | { 1067 | if (MxDir == INVALID_HANDLE_VALUE) return false; 1068 | 1069 | ::FindClose(MxDir); 1070 | MxDir = INVALID_HANDLE_VALUE; 1071 | 1072 | return true; 1073 | } 1074 | 1075 | bool Dir::Getcwd(char *Result, size_t ResultSize) 1076 | { 1077 | WCHAR Dirname[8192]; 1078 | 1079 | // This API is weird. It isn't thread-safe and it has odd return values. 1080 | if (!::GetCurrentDirectoryW(8192, Dirname)) return false; 1081 | Dirname[8191] = L'\0'; 1082 | 1083 | // Normalize result. 1084 | char Dirname2[8192]; 1085 | size_t y = 8192; 1086 | 1087 | Util::ConvertToUTF8(Dirname, wcslen(Dirname) + 1, sizeof(WCHAR), (std::uint8_t *)Dirname2, y); 1088 | if (!File::GetPlatformFilename(Result, ResultSize, Dirname2)) return false; 1089 | 1090 | return true; 1091 | } 1092 | 1093 | bool Dir::Mkdir(const char *Dirname, int, bool Recursive) 1094 | { 1095 | char Dirname2[8192]; 1096 | size_t y; 1097 | 1098 | if (!File::GetAbsoluteFilename(Dirname2, 8192, NULL, Dirname, false)) return false; 1099 | y = strlen(Dirname2) + 1; 1100 | 1101 | WCHAR Dirname3[8192]; 1102 | size_t y2 = 8192; 1103 | Util::ConvertFromUTF8((std::uint8_t *)Dirname2, y, Dirname3, y2, sizeof(WCHAR)); 1104 | 1105 | SECURITY_ATTRIBUTES SecAttr; 1106 | SecAttr.nLength = sizeof(SecAttr); 1107 | SecAttr.lpSecurityDescriptor = NULL; 1108 | SecAttr.bInheritHandle = TRUE; 1109 | 1110 | if (Recursive) 1111 | { 1112 | size_t x; 1113 | File::FilenameInfo FileInfo; 1114 | 1115 | File::GetPlatformFilenameInfo(FileInfo, Dirname2); 1116 | 1117 | for (x = FileInfo.StartVolume; Dirname3[x] != L'\0' && Dirname3[x] != L'\\'; x++); 1118 | if (Dirname3[x] == L'\\') x++; 1119 | for (; Dirname3[x] != L'\0'; x++) 1120 | { 1121 | if (Dirname3[x] == L'\\') 1122 | { 1123 | Dirname3[x] = L'\0'; 1124 | ::CreateDirectoryW(Dirname3, &SecAttr); 1125 | Dirname3[x] = L'\\'; 1126 | } 1127 | } 1128 | } 1129 | 1130 | return (::CreateDirectoryW(Dirname3, &SecAttr) != 0); 1131 | } 1132 | 1133 | bool Dir::Rmdir(const char *Dirname, bool Recursive) 1134 | { 1135 | char Dirname2[8192]; 1136 | size_t y; 1137 | 1138 | if (!File::GetAbsoluteFilename(Dirname2, 8192, NULL, Dirname, false)) return false; 1139 | y = strlen(Dirname2) + 1; 1140 | 1141 | WCHAR Dirname3[8192]; 1142 | size_t y2 = 8192; 1143 | Util::ConvertFromUTF8((std::uint8_t *)Dirname2, y, Dirname3, y2, sizeof(WCHAR)); 1144 | 1145 | if (Recursive) 1146 | { 1147 | // Don't traverse reparse points. 1148 | DWORD TempAttrs = ::GetFileAttributesW(Dirname3); 1149 | if (TempAttrs != (DWORD)-1 && !(TempAttrs & FILE_ATTRIBUTE_REPARSE_POINT)) Rmdir_RecurseInternal(Dirname3, y2 - 1); 1150 | } 1151 | 1152 | return (::RemoveDirectoryW(Dirname3) != 0); 1153 | } 1154 | 1155 | void Dir::Rmdir_RecurseInternal(WCHAR *Dirname, size_t Size) 1156 | { 1157 | if (Size >= 8190) return; 1158 | Dirname[Size] = L'\\'; 1159 | Dirname[Size + 1] = L'*'; 1160 | Dirname[Size + 2] = L'\0'; 1161 | 1162 | HANDLE TempHandle; 1163 | WIN32_FIND_DATAW FindData; 1164 | 1165 | TempHandle = ::FindFirstFileW(Dirname, &FindData); 1166 | if (TempHandle != INVALID_HANDLE_VALUE) 1167 | { 1168 | do 1169 | { 1170 | // Ignore . and .. directories. 1171 | if (wcscmp(FindData.cFileName, L".") && wcscmp(FindData.cFileName, L"..")) 1172 | { 1173 | size_t y = wcslen(FindData.cFileName) + 1; 1174 | if (Size + 1 + y <= 8191) 1175 | { 1176 | wcscpy(Dirname + Size + 1, FindData.cFileName); 1177 | if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) ::DeleteFileW(Dirname); 1178 | else 1179 | { 1180 | if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) Rmdir_RecurseInternal(Dirname, Size + y); 1181 | ::RemoveDirectoryW(Dirname); 1182 | } 1183 | } 1184 | } 1185 | } while (::FindNextFileW(TempHandle, &FindData)); 1186 | 1187 | ::FindClose(TempHandle); 1188 | } 1189 | 1190 | Dirname[Size] = L'\0'; 1191 | } 1192 | #else 1193 | File::File() : MxFile(-1), MxLocked(false), MxRead(false), MxWrite(false), MxCurrPos(0), MxMaxPos(0) 1194 | { 1195 | } 1196 | 1197 | File::~File() 1198 | { 1199 | Close(); 1200 | } 1201 | 1202 | bool File::IsOpen() const 1203 | { 1204 | return (MxFile != -1); 1205 | } 1206 | 1207 | bool File::Open(const char *Filename, int Flags, ShareType ShareFlag, int Mode) 1208 | { 1209 | Close(); 1210 | 1211 | int Access = Flags & O_ACCMODE; 1212 | 1213 | if (Access == O_RDONLY) 1214 | { 1215 | MxRead = true; 1216 | MxWrite = false; 1217 | } 1218 | else if (Access == O_WRONLY) 1219 | { 1220 | MxRead = false; 1221 | MxWrite = true; 1222 | } 1223 | else if (Access == O_RDWR) 1224 | { 1225 | MxRead = true; 1226 | MxWrite = true; 1227 | } 1228 | else 1229 | { 1230 | return false; 1231 | } 1232 | 1233 | char Filename2[8192]; 1234 | if (!GetPlatformFilename(Filename2, 8192, Filename)) return false; 1235 | 1236 | #ifdef O_BINARY 1237 | Flags &= ~O_TEXT; 1238 | Flags |= O_BINARY; 1239 | #endif 1240 | 1241 | #ifdef O_LARGEFILE 1242 | Flags |= O_LARGEFILE; 1243 | #endif 1244 | 1245 | #ifdef O_CLOEXEC 1246 | Flags |= O_CLOEXEC; 1247 | #endif 1248 | 1249 | MxFile = ::open(Filename2, Flags, Mode & 0777); 1250 | if (MxFile < 0) 1251 | { 1252 | MxFile = -1; 1253 | 1254 | return false; 1255 | } 1256 | 1257 | MxLocked = false; 1258 | 1259 | // Closest to Windows support is to just lock exclusively for ShareRead and ShareWrite. 1260 | if (ShareFlag == ShareNone || ShareFlag == ShareRead || ShareFlag == ShareWrite) 1261 | { 1262 | if (::flock(MxFile, LOCK_EX | LOCK_NB) < 0) return false; 1263 | 1264 | MxLocked = true; 1265 | } 1266 | else if (ShareFlag == ShareBoth) 1267 | { 1268 | if (::flock(MxFile, LOCK_SH | LOCK_NB) < 0) return false; 1269 | 1270 | MxLocked = true; 1271 | } 1272 | else return false; 1273 | 1274 | off64_t Pos = ::lseek64(MxFile, 0, SEEK_CUR); 1275 | MxCurrPos = (Pos >= 0 ? (std::uint64_t)Pos : 0); 1276 | UpdateMaxPos(); 1277 | 1278 | return true; 1279 | } 1280 | 1281 | bool File::Seek(SeekType whence, std::uint64_t Pos) 1282 | { 1283 | if (MxFile == -1) return false; 1284 | 1285 | std::uint64_t TempPos; 1286 | 1287 | switch (whence) 1288 | { 1289 | case File::SeekStart: TempPos = (Pos <= MxMaxPos ? Pos : MxMaxPos); break; 1290 | case File::SeekEnd: TempPos = (Pos <= MxMaxPos ? MxMaxPos - Pos : 0); break; 1291 | case File::SeekForward: TempPos = (MxCurrPos + Pos <= MxMaxPos ? MxCurrPos + Pos : MxMaxPos); break; 1292 | case File::SeekBackward: TempPos = (MxCurrPos >= Pos ? MxCurrPos - Pos : 0); break; 1293 | default: TempPos = MxCurrPos; break; 1294 | } 1295 | 1296 | if (::lseek64(MxFile, (off64_t)TempPos, SEEK_SET) < 0) return false; 1297 | 1298 | MxCurrPos = TempPos; 1299 | 1300 | return true; 1301 | } 1302 | 1303 | bool File::Read(std::uint8_t *Data, size_t DataSize, size_t &DataRead) 1304 | { 1305 | if (MxFile == -1 || !MxRead) return false; 1306 | 1307 | if ((std::uint64_t)DataSize > MxMaxPos - MxCurrPos) 1308 | { 1309 | UpdateMaxPos(); 1310 | if ((std::uint64_t)DataSize > MxMaxPos - MxCurrPos) DataSize = (size_t)(MxMaxPos - MxCurrPos); 1311 | } 1312 | 1313 | DataRead = 0; 1314 | if (DataSize == 0) return true; 1315 | 1316 | ssize_t BytesRead; 1317 | do 1318 | { 1319 | BytesRead = ::read(MxFile, Data, DataSize); 1320 | } while (BytesRead < 0 && errno == EINTR); 1321 | if (BytesRead < 0) return false; 1322 | DataRead = (size_t)BytesRead; 1323 | MxCurrPos += (std::uint64_t)BytesRead; 1324 | 1325 | return true; 1326 | } 1327 | 1328 | bool File::Write(const std::uint8_t *Data, size_t DataSize, size_t &DataWritten) 1329 | { 1330 | if (MxFile == -1 || !MxWrite) return false; 1331 | 1332 | DataWritten = 0; 1333 | if (DataSize == 0) return true; 1334 | 1335 | ssize_t BytesWritten; 1336 | do 1337 | { 1338 | BytesWritten = ::write(MxFile, Data, DataSize); 1339 | if (BytesWritten < 0 && errno != EINTR) return false; 1340 | 1341 | DataSize -= BytesWritten; 1342 | Data += BytesWritten; 1343 | DataWritten += BytesWritten; 1344 | } while (DataSize); 1345 | MxCurrPos += (std::uint64_t)DataWritten; 1346 | if (MxCurrPos > MxMaxPos) MxMaxPos = MxCurrPos; 1347 | 1348 | return true; 1349 | } 1350 | 1351 | bool File::Flush() 1352 | { 1353 | if (MxFile == -1 || !MxWrite) return false; 1354 | 1355 | ::sync(); 1356 | 1357 | return true; 1358 | } 1359 | 1360 | bool File::UpdateMaxPos() 1361 | { 1362 | if (MxFile == -1) return false; 1363 | 1364 | struct stat TempStat; 1365 | if (::fstat(MxFile, &TempStat) < 0) return false; 1366 | MxMaxPos = (std::uint64_t)TempStat.st_size; 1367 | 1368 | return true; 1369 | } 1370 | 1371 | bool File::Close() 1372 | { 1373 | if (MxFile != -1) 1374 | { 1375 | ::close(MxFile); 1376 | MxFile = -1; 1377 | } 1378 | 1379 | return true; 1380 | } 1381 | 1382 | bool File::GetPlatformFilename(char *Result, size_t ResultSize, const char *Filename, bool) 1383 | { 1384 | // Check for invalid characters in the path/filename. Replace '\' with '/'. 1385 | char TempCP; 1386 | size_t x, y = strlen(Filename); 1387 | if (y + 1 > ResultSize) return false; 1388 | Util::ConvertToUTF8(Filename, y + 1, sizeof(char), (std::uint8_t *)Result, ResultSize); 1389 | if (!strlen(Result) || strcmp(Result, Filename)) return false; 1390 | 1391 | // Ignore combining code points since kernels are generally not fully UTF-8 aware. 1392 | for (x = 0; x < y; x++) 1393 | { 1394 | TempCP = Result[x]; 1395 | 1396 | if (TempCP < 0x20 || TempCP == 0x7F || TempCP == '*' || TempCP == '?' || TempCP == '\"' || TempCP == '<' || TempCP == '>' || TempCP == '|') return false; 1397 | else if (TempCP == '\\') Result[x] = '/'; 1398 | } 1399 | 1400 | return true; 1401 | } 1402 | 1403 | void File::GetPlatformFilenameInfo(FilenameInfo &Result, const char *Filename) 1404 | { 1405 | size_t x, y = strlen(Filename); 1406 | 1407 | Result.Length = y; 1408 | 1409 | Result.StartVolume = 0; 1410 | Result.StartPath = 0; 1411 | 1412 | for (x = y; x > Result.StartPath && Filename[x - 1] != '/'; x--); 1413 | Result.StartFilename = x; 1414 | 1415 | for (; x < y && Filename[x] != '.'; x++); 1416 | Result.StartLastExtension = Result.StartExtension = x; 1417 | 1418 | for (; x < y; x++) 1419 | { 1420 | if (Filename[x] == '.') Result.StartLastExtension = x; 1421 | } 1422 | } 1423 | 1424 | bool File::GetAbsoluteFilename(char *Result, size_t ResultSize, const char *BaseDir, const char *Filename, bool TrailingSlash) 1425 | { 1426 | char Filename2[8192], Filename3[8192]; 1427 | 1428 | if (!GetPlatformFilename(Filename3, 8192, Filename)) return false; 1429 | 1430 | // Replace '/./' with '/'. 1431 | size_t x, x2, y = strlen(Filename3); 1432 | x2 = 0; 1433 | for (x = 0; x < y; x++) 1434 | { 1435 | while (Filename3[x] == '/' && Filename3[x + 1] == '.' && Filename3[x + 2] == '/') x += 2; 1436 | 1437 | Filename3[x2++] = Filename3[x]; 1438 | 1439 | if (Filename3[x] == '/' && Filename3[x + 1] == '.' && Filename3[x + 2] == '\0') break; 1440 | } 1441 | if (x2 >= 3 && Filename3[x2 - 3] == '/' && Filename3[x2 - 2] == '.' && Filename3[x2 - 1] == '.') 1442 | { 1443 | if (x2 >= 8191) return false; 1444 | Filename3[x2++] = '/'; 1445 | } 1446 | Filename3[x2] = '\0'; 1447 | 1448 | // Get some parsed information about the input data. 1449 | FilenameInfo FileInfo2, FileInfo3; 1450 | GetPlatformFilenameInfo(FileInfo3, Filename3); 1451 | 1452 | // Copy the absolute volume. 1453 | if (FileInfo3.StartVolume) 1454 | { 1455 | memcpy(Filename2, Filename3, FileInfo3.StartPath); 1456 | Filename2[FileInfo3.StartPath] = '\0'; 1457 | } 1458 | else 1459 | { 1460 | if (BaseDir != NULL) 1461 | { 1462 | if (!GetPlatformFilename(Filename2, 8192, BaseDir)) return false; 1463 | } 1464 | else 1465 | { 1466 | if (!Dir::Getcwd(Filename2, 8192)) return false; 1467 | } 1468 | 1469 | // Require the base directory to be absolute. 1470 | if (Filename2[0] != '/') return false; 1471 | } 1472 | 1473 | y = strlen(Filename2); 1474 | if (y && Filename2[y - 1] != '/') 1475 | { 1476 | if (y >= 8191) return false; 1477 | Filename2[y++] = '/'; 1478 | Filename2[y] = '\0'; 1479 | } 1480 | 1481 | GetPlatformFilenameInfo(FileInfo2, Filename2); 1482 | if (Filename3[FileInfo3.StartPath] == '/') FileInfo2.Length = FileInfo2.StartPath; 1483 | 1484 | // Make sure the string always starts with a path separator. 1485 | if (FileInfo2.Length >= 8191) return false; 1486 | if (FileInfo2.StartPath == FileInfo2.Length) Filename2[FileInfo2.Length++] = '/'; 1487 | 1488 | // Parse the filename into the base directory. 1489 | for (x = FileInfo3.StartPath; x < FileInfo3.Length; x++) 1490 | { 1491 | while (Filename3[x] == '/' && Filename3[x + 1] == '.' && Filename3[x + 2] == '.' && Filename3[x + 3] == '/') 1492 | { 1493 | if (FileInfo2.Length > FileInfo2.StartPath) 1494 | { 1495 | for (; FileInfo2.Length > FileInfo2.StartPath && Filename2[FileInfo2.Length - 1] == '/'; FileInfo2.Length--); 1496 | for (; FileInfo2.Length > FileInfo2.StartPath && Filename2[FileInfo2.Length - 1] != '/'; FileInfo2.Length--); 1497 | } 1498 | 1499 | x += 3; 1500 | } 1501 | 1502 | if (FileInfo2.Length >= 8191) return false; 1503 | Filename2[FileInfo2.Length++] = Filename3[x]; 1504 | } 1505 | 1506 | // Copy the rest. 1507 | x = 0; 1508 | x2 = x; 1509 | for (; x2 < ResultSize && x < FileInfo2.Length; x++) 1510 | { 1511 | while (Filename2[x] == '/' && Filename2[x + 1] == '/') x++; 1512 | 1513 | Result[x2++] = Filename2[x]; 1514 | } 1515 | if (TrailingSlash && x2 && Result[x2 - 1] != '/' && x2 < ResultSize) Result[x2++] = '/'; 1516 | if (!TrailingSlash && x2 && Result[x2 - 1] == '/') x2--; 1517 | if (x2 >= ResultSize) return false; 1518 | Result[x2] = '\0'; 1519 | 1520 | // Sanity check. 1521 | for (x = 0; x < x2; x++) 1522 | { 1523 | if (Result[x] == '/' && Result[x + 1] == '.') 1524 | { 1525 | if (Result[x + 2] == '/') return false; 1526 | if (Result[x + 2] == '.' && Result[x + 3] == '/') return false; 1527 | } 1528 | } 1529 | 1530 | return true; 1531 | } 1532 | 1533 | bool File::Exists(const char *Filename) 1534 | { 1535 | FileStat TempStat; 1536 | 1537 | if (!Stat(TempStat, Filename)) return false; 1538 | 1539 | return true; 1540 | } 1541 | 1542 | bool File::Realpath(char *Result, size_t ResultSize, const char *Filename) 1543 | { 1544 | char *TempResult = ::realpath(Filename, NULL); 1545 | if (TempResult == NULL) return false; 1546 | 1547 | size_t y = strlen(TempResult); 1548 | if (y + 1 <= ResultSize) strcpy(Result, TempResult); 1549 | ::free(TempResult); 1550 | 1551 | return (y + 1 <= ResultSize); 1552 | } 1553 | 1554 | bool File::Chmod(const char *Filename, int Mode) 1555 | { 1556 | char Filename2[8192]; 1557 | if (!GetPlatformFilename(Filename2, 8192, Filename)) return false; 1558 | 1559 | return (::chmod(Filename2, (mode_t)Mode) == 0); 1560 | } 1561 | 1562 | bool File::Chown(const char *Filename, const char *Owner) 1563 | { 1564 | char Filename2[8192]; 1565 | if (!GetPlatformFilename(Filename2, 8192, Filename)) return false; 1566 | 1567 | struct passwd *UserInfo = getpwnam(Owner); 1568 | if (UserInfo == NULL) return false; 1569 | 1570 | return (::chown(Filename2, UserInfo->pw_uid, -1) == 0); 1571 | } 1572 | 1573 | bool File::Chgrp(const char *Filename, const char *Group) 1574 | { 1575 | char Filename2[8192]; 1576 | if (!GetPlatformFilename(Filename2, 8192, Filename)) return false; 1577 | 1578 | struct passwd *UserInfo = getpwnam(Group); 1579 | if (UserInfo == NULL) return false; 1580 | 1581 | return (::chown(Filename2, -1, UserInfo->pw_gid) == 0); 1582 | } 1583 | 1584 | bool File::Copy(const char *SrcFilename, const char *DestFilename) 1585 | { 1586 | char SrcFilename2[8192], DestFilename2[8192]; 1587 | if (!GetPlatformFilename(SrcFilename2, 8192, SrcFilename)) return false; 1588 | if (!GetPlatformFilename(DestFilename2, 8192, DestFilename)) return false; 1589 | 1590 | FileStat SrcStat, DestStat; 1591 | if (stat(SrcFilename2, &SrcStat) != 0 || !S_ISREG(SrcStat.st_mode)) return false; 1592 | 1593 | if (stat(DestFilename2, &DestStat) == 0 && SrcStat.st_dev == DestStat.st_dev && SrcStat.st_ino == DestStat.st_ino) return false; 1594 | 1595 | File SrcFile, DestFile; 1596 | 1597 | if (!SrcFile.Open(SrcFilename2, O_RDONLY, File::ShareBoth)) return false; 1598 | if (!DestFile.Open(DestFilename2, O_WRONLY | O_CREAT, File::ShareNone, SrcStat.st_mode)) return false; 1599 | 1600 | std::uint8_t Buffer[8192]; 1601 | size_t y; 1602 | 1603 | // Copy the data. 1604 | while (SrcFile.Read(Buffer, 8192, y) && y) 1605 | { 1606 | DestFile.Write(Buffer, 8192, y); 1607 | } 1608 | 1609 | // Truncate the file to the current point. 1610 | if (::ftruncate(DestFile.MxFile, (off_t)DestFile.MxCurrPos) == 0) {} 1611 | 1612 | DestFile.Close(); 1613 | SrcFile.Close(); 1614 | 1615 | // Clone user, group, and permissions. 1616 | if (::chown(DestFilename2, SrcStat.st_uid, SrcStat.st_gid) == 0) {} 1617 | if (::chmod(DestFilename2, SrcStat.st_mode) == 0) {} 1618 | 1619 | // Clone the modified timestamp. 1620 | stat(DestFilename2, &DestStat); 1621 | struct utimbuf TempUTime; 1622 | TempUTime.actime = DestStat.st_atime; 1623 | TempUTime.modtime = SrcStat.st_mtime; 1624 | utime(DestFilename2, &TempUTime); 1625 | 1626 | return true; 1627 | } 1628 | 1629 | bool File::Move(const char *SrcFilename, const char *DestFilename) 1630 | { 1631 | char SrcFilename2[8192], DestFilename2[8192]; 1632 | if (!GetPlatformFilename(SrcFilename2, 8192, SrcFilename)) return false; 1633 | if (!GetPlatformFilename(DestFilename2, 8192, DestFilename)) return false; 1634 | 1635 | bool Result = (::rename(SrcFilename2, DestFilename2) == 0); 1636 | if (!Result) 1637 | { 1638 | Result = File::Copy(SrcFilename2, DestFilename2); 1639 | if (Result) ::unlink(SrcFilename2); 1640 | } 1641 | 1642 | return Result; 1643 | } 1644 | 1645 | bool File::Delete(const char *Filename) 1646 | { 1647 | char Filename2[8192]; 1648 | if (!GetPlatformFilename(Filename2, 8192, Filename)) return false; 1649 | 1650 | return (::unlink(Filename2) == 0); 1651 | } 1652 | 1653 | bool File::Stat(FileStat &Result, const char *Filename, bool Link) 1654 | { 1655 | char Filename2[8192]; 1656 | if (!GetPlatformFilename(Filename2, 8192, Filename)) return false; 1657 | 1658 | return (Link ? (::lstat(Filename2, &Result) == 0) : (::stat(Filename2, &Result) == 0)); 1659 | } 1660 | 1661 | bool File::Symlink(const char *Src, const char *Dest) 1662 | { 1663 | return (::symlink(Dest, Src) == 0); 1664 | } 1665 | 1666 | bool File::Readlink(char *Result, size_t ResultSize, const char *Filename) 1667 | { 1668 | ssize_t Size = ::readlink(Filename, Result, ResultSize); 1669 | 1670 | return (Size != -1 && (size_t)Size < ResultSize); 1671 | } 1672 | 1673 | // Only Windows supports Creation timestamp modification. 1674 | bool File::SetFileTimes(const char *Filename, std::uint64_t *, std::uint64_t *LastAccess, std::uint64_t *LastUpdate) 1675 | { 1676 | char Filename2[8192]; 1677 | if (!GetPlatformFilename(Filename2, 8192, Filename)) return false; 1678 | 1679 | FileStat TempStat; 1680 | 1681 | if (stat(Filename2, &TempStat) != 0) return false; 1682 | 1683 | struct timeval TempTimes[2]; 1684 | 1685 | if (LastAccess == NULL) 1686 | { 1687 | TempTimes[0].tv_sec = TempStat.st_atime; 1688 | TempTimes[0].tv_usec = 0; 1689 | } 1690 | else 1691 | { 1692 | TempTimes[0].tv_sec = (long)(*LastAccess / 1000000); 1693 | TempTimes[0].tv_usec = (long)(*LastAccess % 1000000); 1694 | } 1695 | 1696 | if (LastUpdate == NULL) 1697 | { 1698 | TempTimes[1].tv_sec = TempStat.st_mtime; 1699 | TempTimes[1].tv_usec = 0; 1700 | } 1701 | else 1702 | { 1703 | TempTimes[1].tv_sec = (long)(*LastUpdate / 1000000); 1704 | TempTimes[1].tv_usec = (long)(*LastUpdate % 1000000); 1705 | } 1706 | 1707 | return (utimes(Filename2, TempTimes) == 0); 1708 | } 1709 | 1710 | 1711 | Dir::Dir() : MxDir(NULL) 1712 | { 1713 | } 1714 | 1715 | Dir::~Dir() 1716 | { 1717 | Close(); 1718 | } 1719 | 1720 | bool Dir::Open(const char *Dirname) 1721 | { 1722 | Close(); 1723 | 1724 | char Dirname2[8192]; 1725 | 1726 | if (!File::GetAbsoluteFilename(Dirname2, 8192, NULL, Dirname, false)) return false; 1727 | 1728 | MxDir = ::opendir(Dirname2); 1729 | 1730 | return (MxDir != NULL); 1731 | } 1732 | 1733 | bool Dir::Read(char *Filename, size_t Size) 1734 | { 1735 | if (MxDir == NULL) return false; 1736 | 1737 | // After much reading and deliberation, readdir() is correct. 1738 | // readdir_r() is wrong, broken, and a bad idea. Your favorite lint tool is wrong. 1739 | struct dirent *Entry = ::readdir(MxDir); 1740 | if (Entry == NULL) 1741 | { 1742 | Close(); 1743 | 1744 | return false; 1745 | } 1746 | 1747 | size_t y = strlen(Entry->d_name); 1748 | if (y + 1 > Size) return false; 1749 | 1750 | strcpy(Filename, Entry->d_name); 1751 | 1752 | return true; 1753 | } 1754 | 1755 | bool Dir::Close() 1756 | { 1757 | if (MxDir == NULL) return false; 1758 | 1759 | ::closedir(MxDir); 1760 | MxDir = NULL; 1761 | 1762 | return true; 1763 | } 1764 | 1765 | bool Dir::Getcwd(char *Result, size_t ResultSize) 1766 | { 1767 | return (::getcwd(Result, ResultSize) != NULL); 1768 | } 1769 | 1770 | bool Dir::Mkdir(const char *Dirname, int Mode, bool Recursive) 1771 | { 1772 | char Dirname2[8192]; 1773 | 1774 | if (!File::GetAbsoluteFilename(Dirname2, 8192, NULL, Dirname, false)) return false; 1775 | 1776 | if (Recursive) 1777 | { 1778 | size_t x; 1779 | 1780 | for (x = 0; Dirname2[x] != '\0' && Dirname2[x] != '/'; x++); 1781 | if (Dirname2[x] == '/') x++; 1782 | for (; Dirname2[x] != '\0'; x++) 1783 | { 1784 | if (Dirname2[x] == '/') 1785 | { 1786 | Dirname2[x] = '\0'; 1787 | ::mkdir(Dirname2, Mode); 1788 | Dirname2[x] = '/'; 1789 | } 1790 | } 1791 | } 1792 | 1793 | return (::mkdir(Dirname2, Mode) == 0); 1794 | } 1795 | 1796 | bool Dir::Rmdir(const char *Dirname, bool Recursive) 1797 | { 1798 | char Dirname2[8192]; 1799 | 1800 | if (!File::GetAbsoluteFilename(Dirname2, 8192, NULL, Dirname, false)) return false; 1801 | 1802 | if (Recursive) 1803 | { 1804 | // Don't traverse symlinks. 1805 | File::FileStat TempStat; 1806 | if (::lstat(Dirname2, &TempStat) == 0 && !S_ISLNK(TempStat.st_mode)) Rmdir_RecurseInternal(Dirname2, strlen(Dirname2)); 1807 | } 1808 | 1809 | return (::rmdir(Dirname2) == 0); 1810 | } 1811 | 1812 | void Dir::Rmdir_RecurseInternal(char *Dirname, size_t Size) 1813 | { 1814 | if (Size >= 8190) return; 1815 | 1816 | File::FileStat TempStat; 1817 | Dir TempDir; 1818 | 1819 | if (TempDir.Open(Dirname)) 1820 | { 1821 | Dirname[Size] = '/'; 1822 | 1823 | while (TempDir.Read(Dirname + Size + 1, 8192 - Size - 1)) 1824 | { 1825 | // Ignore . and .. directories. 1826 | if (strcmp(Dirname + Size + 1, ".") && strcmp(Dirname + Size + 1, "..")) 1827 | { 1828 | if (::lstat(Dirname, &TempStat) == 0) 1829 | { 1830 | if (!S_ISDIR(TempStat.st_mode) || S_ISLNK(TempStat.st_mode)) ::unlink(Dirname); 1831 | else 1832 | { 1833 | Rmdir_RecurseInternal(Dirname, strlen(Dirname)); 1834 | ::rmdir(Dirname); 1835 | } 1836 | } 1837 | } 1838 | } 1839 | 1840 | TempDir.Close(); 1841 | 1842 | Dirname[Size] = '\0'; 1843 | } 1844 | } 1845 | #endif 1846 | 1847 | // All platforms. 1848 | bool File::Write(const char *Data, size_t &DataWritten) 1849 | { 1850 | return Write((const std::uint8_t *)Data, strlen(Data), DataWritten); 1851 | } 1852 | 1853 | bool File::IsValidFilenameFormat(const char *Filename) 1854 | { 1855 | char Filename2[8192]; 1856 | 1857 | return GetPlatformFilename(Filename2, 8192, Filename); 1858 | } 1859 | 1860 | char *File::LineInput(size_t SizeHint, void *AltMallocManager, void *(*AltRealloc)(void *, void *, size_t)) 1861 | { 1862 | char *Result, *Result2, *RPos, *NPos; 1863 | size_t x, CurrSize, ReadSize; 1864 | 1865 | if (!IsOpen() || !MxRead) return NULL; 1866 | 1867 | Result = NULL; 1868 | CurrSize = SizeHint; 1869 | do 1870 | { 1871 | if (Result == NULL) Result = (AltRealloc != NULL ? (char *)AltRealloc(AltMallocManager, NULL, CurrSize + 1) : new char[CurrSize + 1]); 1872 | else 1873 | { 1874 | // Enlarge storage space by SizeHint. 1875 | CurrSize += SizeHint; 1876 | 1877 | if (AltRealloc != NULL) Result = (char *)AltRealloc(AltMallocManager, Result, CurrSize + 1); 1878 | else 1879 | { 1880 | Result2 = new char[CurrSize + 1]; 1881 | memcpy(Result2, Result, CurrSize); 1882 | delete[] Result; 1883 | Result = Result2; 1884 | } 1885 | } 1886 | 1887 | Read((std::uint8_t *)(Result + CurrSize - SizeHint), SizeHint, ReadSize); 1888 | if (ReadSize < SizeHint) 1889 | { 1890 | CurrSize -= (SizeHint - ReadSize); 1891 | 1892 | break; 1893 | } 1894 | 1895 | NPos = (char *)memchr(Result, '\n', CurrSize); 1896 | if (NPos != NULL) break; 1897 | RPos = (char *)memchr(Result, '\r', CurrSize); 1898 | if (RPos != NULL && RPos < Result + CurrSize - 1) break; 1899 | } while (1); 1900 | 1901 | for (x = 0; x < CurrSize; x++) 1902 | { 1903 | if (Result[x] == '\0') Result[x] = ' '; 1904 | } 1905 | 1906 | NPos = (char *)memchr(Result, '\n', CurrSize); 1907 | RPos = (char *)memchr(Result, '\r', CurrSize); 1908 | 1909 | if (NPos != NULL && RPos != NULL) 1910 | { 1911 | if (NPos < RPos) RPos = NULL; 1912 | else if (RPos < NPos - 1) NPos = NULL; 1913 | } 1914 | 1915 | if (NPos != NULL) 1916 | { 1917 | Seek(File::SeekBackward, CurrSize - (size_t)(NPos - Result) - 1); 1918 | if (RPos != NULL) *RPos = '\0'; 1919 | else *NPos = '\0'; 1920 | } 1921 | else if (RPos != NULL) 1922 | { 1923 | Seek(File::SeekBackward, CurrSize - (size_t)(RPos - Result) - 1); 1924 | *RPos = '\0'; 1925 | } 1926 | else 1927 | { 1928 | Result[CurrSize] = '\0'; 1929 | } 1930 | 1931 | return Result; 1932 | } 1933 | 1934 | bool File::LoadEntireFile(const char *Filename, char *&Result, size_t &BytesRead, void *AltMallocManager, void *(*AltMalloc)(void *, size_t), void (*AltFree)(void *, void *)) 1935 | { 1936 | File TempFile; 1937 | 1938 | Result = NULL; 1939 | BytesRead = 0; 1940 | 1941 | if (!TempFile.Open(Filename, O_RDONLY)) return false; 1942 | 1943 | char *TempResult; 1944 | size_t TempSize = (size_t)TempFile.GetMaxPos(), TempSize2; 1945 | if (AltMallocManager != NULL) TempResult = (char *)AltMalloc(AltMallocManager, TempSize); 1946 | else TempResult = new char[TempSize]; 1947 | 1948 | if (!TempFile.Read((std::uint8_t *)TempResult, TempSize, TempSize2) || TempSize != TempSize2) 1949 | { 1950 | if (AltMallocManager != NULL) AltFree(AltMallocManager, TempResult); 1951 | else delete[] TempResult; 1952 | 1953 | return false; 1954 | } 1955 | 1956 | TempFile.Close(); 1957 | 1958 | Result = TempResult; 1959 | BytesRead = TempSize; 1960 | 1961 | return true; 1962 | } 1963 | } 1964 | } --------------------------------------------------------------------------------