/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: FS.cpp Date: 2021-6-16 Author: Reece ***/ #include #include "FS.hpp" //#include namespace Aurora::IO::FS { static bool IsMagicCharacter(char c) { return (c == '.') || (c == '!') || (c == '~') || (c == '?'); } static void ResolveAbsolutePath(const AuString &path, AuString &result) { AuString buffer; bool isResource {}; buffer.reserve(4096); result.reserve(4096); /** Resolve special character prefixes */ if (path.size() > 2) { if (path[1] == kPathSplitter) { // Working directory if (path[0] == '.') { SysAssert(Process::GetWorkingDirectory(buffer)); buffer += kPathSplitter; } // Local Aurora profile else if (path[0] == '~') { SysAssert(FS::GetProfileDomain(buffer)); } // Global Aurora profile else if (path[0] == '!') { SysAssert(FS::GetSystemDomain(buffer)); } else if (path[0] == '?') { // ...except for this one isResource = true; } else { buffer.insert(buffer.begin(), path.begin(), path.begin() + 2); // ??? } buffer.insert(buffer.end(), path.begin() + 2, path.end()); } else { buffer += path; } } else { buffer += path; } /** Quickly handle the edge case in some modern UNIX derived systems, and in Windows UNC paths, where paths may start with '//' or '\\' respectively */ if (StartsWith(buffer, kDoublePathSplitter)) { result += kDoublePathSplitter; } /** Technically, UTF-8 strings may contain the "NUL" byte. o Character numbers from U+0000 to U+007F (US-ASCII repertoire) correspond to octets 00 to 7F (7 bit US-ASCII paths). A direct consequence is that a plain ASCII string is also a valid UTF-8 string. You know what would suck? If we were targeting an esoteric platform, say a kernel, and we we're trying to avoid string exploits by potential attackers. I'm sure that would never happen to poor anticheat devs. */ const auto parts = SplitString(buffer, AuString(1, kPathSplitter)); /// zzzz im going to FUCKING SLEEP for (const auto &ch : parts) // can you tell why FIO shouldn't be in hot paths yet? { if (ch == "..") { auto i = result.size() - 1; while (i >= 0 && result[i] != kPathSplitter) { --i; } if (i >= 0) { result.resize(i); } continue; } if ((ch.size() == 1) && (IsMagicCharacter(ch[0]))) { continue; } result += ch; result += kPathSplitter; } auto i = result.size() - 1; if (result[i] == kPathSplitter) { result.resize(i); } if (isResource) { AuString path; if (!FS::GetSystemResourcePath(result, path)) { result.clear(); } else { result = path; } } } void /* internal, local export */ _NormalizePath(AuString &str) { bool requiresExpanding = false; requiresExpanding = str.size() && IsMagicCharacter(str[0]); // best case -> O(n) wherein we merely check each BYTE with a handful of branch conditions int doubleDots = 0; int doubleSlash = 0; for (auto &character : str) { if ((character == '\\') || (character == '/')) { character = kPathSplitter; doubleSlash++; } else { doubleSlash = 0; } if (character == '.') { doubleDots++; } else { doubleDots = 0; } requiresExpanding |= doubleDots >= 2; requiresExpanding |= doubleSlash >= 2; } // plus this, i guess if (str.size() > 2) { if (str[1] == kPathSplitter) { auto c = str[0]; if ((c == '.') || (c == '~') || (c == '!') || (c == '?')) { requiresExpanding = true; } } } // worst case -> yea have fun if (requiresExpanding) { AuString temp; ResolveAbsolutePath(str, temp); str = temp; } } }