mirror of https://gitlab.com/nakst/essence
222 lines
5.2 KiB
C++
222 lines
5.2 KiB
C++
// This file is part of the Essence operating system.
|
|
// It is released under the terms of the MIT license -- see LICENSE.md.
|
|
// Written by: nakst.
|
|
|
|
struct String {
|
|
char *text;
|
|
size_t bytes, allocated;
|
|
};
|
|
|
|
String StringAllocateAndFormat(const char *format, ...) {
|
|
String string = {};
|
|
va_list arguments;
|
|
va_start(arguments, format);
|
|
string.text = EsStringAllocateAndFormatV(&string.bytes, format, arguments);
|
|
va_end(arguments);
|
|
string.allocated = string.bytes;
|
|
return string;
|
|
}
|
|
|
|
String StringFromLiteral(const char *literal) {
|
|
String string = {};
|
|
string.text = (char *) literal;
|
|
string.bytes = EsCStringLength(literal);
|
|
return string;
|
|
}
|
|
|
|
String StringFromLiteralWithSize(const char *literal, ptrdiff_t bytes) {
|
|
String string = {};
|
|
string.text = (char *) literal;
|
|
string.bytes = bytes == -1 ? EsCStringLength(literal) : bytes;
|
|
return string;
|
|
}
|
|
|
|
void StringAppend(String *string, String with) {
|
|
if (string->bytes + with.bytes > string->allocated) {
|
|
string->allocated = (string->allocated + with.bytes) * 2;
|
|
string->text = (char *) EsHeapReallocate(string->text, string->allocated, false);
|
|
}
|
|
|
|
EsMemoryCopy(string->text + string->bytes, with.text, with.bytes);
|
|
string->bytes += with.bytes;
|
|
}
|
|
|
|
void StringDestroy(String *string) {
|
|
EsAssert(string->allocated == string->bytes); // Attempting to free a partial string.
|
|
EsHeapFree(string->text);
|
|
string->text = nullptr;
|
|
string->bytes = string->allocated = 0;
|
|
}
|
|
|
|
String StringDuplicate(String string) {
|
|
String result = {};
|
|
result.bytes = result.allocated = string.bytes;
|
|
result.text = (char *) EsHeapAllocate(result.bytes + 1, false);
|
|
result.text[result.bytes] = 0;
|
|
EsMemoryCopy(result.text, string.text, result.bytes);
|
|
return result;
|
|
}
|
|
|
|
inline bool StringStartsWith(String a, String b) {
|
|
return a.bytes >= b.bytes && 0 == EsMemoryCompare(a.text, b.text, b.bytes);
|
|
}
|
|
|
|
inline bool StringEndsWith(String a, String b) {
|
|
return a.bytes >= b.bytes && 0 == EsMemoryCompare(a.text + a.bytes - b.bytes, b.text, b.bytes);
|
|
}
|
|
|
|
inline bool StringEquals(String a, String b) {
|
|
return a.bytes == b.bytes && 0 == EsMemoryCompare(a.text, b.text, a.bytes);
|
|
}
|
|
|
|
String StringSlice(String string, uintptr_t offset, ptrdiff_t length) {
|
|
if (length == -1) {
|
|
length = string.bytes - offset;
|
|
}
|
|
|
|
string.text += offset;
|
|
string.bytes = length;
|
|
string.allocated = 0;
|
|
return string;
|
|
}
|
|
|
|
#define STRING(x) x.text, x.bytes
|
|
#define STRFMT(x) x.bytes, x.text
|
|
|
|
uintptr_t PathCountSections(String string) {
|
|
ptrdiff_t sectionCount = 0;
|
|
|
|
for (uintptr_t i = 0; i < string.bytes; i++) {
|
|
if (string.text[i] == '/') {
|
|
sectionCount++;
|
|
}
|
|
}
|
|
|
|
return sectionCount;
|
|
}
|
|
|
|
String PathGetSection(String string, ptrdiff_t index) {
|
|
String output = {};
|
|
size_t stringBytes = string.bytes;
|
|
char *text = string.text;
|
|
|
|
if (index < 0) {
|
|
index += PathCountSections(string);
|
|
}
|
|
|
|
if (index < 0) {
|
|
return output;
|
|
}
|
|
|
|
uintptr_t i = 0, bytes = 0;
|
|
|
|
for (; index && i < stringBytes; i++) {
|
|
if (text[i] == '/') {
|
|
index--;
|
|
}
|
|
}
|
|
|
|
if (index) {
|
|
return output;
|
|
}
|
|
|
|
output.text = text + i;
|
|
|
|
for (; i < stringBytes; i++) {
|
|
if (text[i] == '/') {
|
|
break;
|
|
} else {
|
|
bytes++;
|
|
}
|
|
}
|
|
|
|
output.bytes = bytes;
|
|
|
|
return output;
|
|
}
|
|
|
|
String PathGetParent(String string, uintptr_t index) {
|
|
if (index == PathCountSections(string)) return string;
|
|
String section = PathGetSection(string, index);
|
|
string.bytes = section.bytes + section.text - string.text + 1;
|
|
return string;
|
|
}
|
|
|
|
String PathGetExtension(String string) {
|
|
String extension = {};
|
|
int lastSeparator = 0;
|
|
|
|
for (intptr_t i = string.bytes - 1; i >= 0; i--) {
|
|
if (string.text[i] == '.') {
|
|
lastSeparator = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!lastSeparator && string.text[0] != '.') {
|
|
extension.text = string.text + string.bytes;
|
|
extension.bytes = 0;
|
|
return extension;
|
|
} else {
|
|
extension.text = string.text + lastSeparator + 1;
|
|
extension.bytes = string.bytes - lastSeparator - 1;
|
|
return extension;
|
|
}
|
|
}
|
|
|
|
String PathGetParent(String string) {
|
|
size_t newPathBytes = 0;
|
|
|
|
for (uintptr_t i = 0; i < string.bytes - 1; i++) {
|
|
if (string.text[i] == '/') {
|
|
newPathBytes = i + 1;
|
|
}
|
|
}
|
|
|
|
String result = {};
|
|
result.bytes = newPathBytes;
|
|
result.text = string.text;
|
|
|
|
return result;
|
|
}
|
|
|
|
bool PathHasTrailingSlash(String path) {
|
|
return path.bytes && path.text[path.bytes - 1] == '/';
|
|
}
|
|
|
|
String PathRemoveTrailingSlash(String path) {
|
|
if (PathHasTrailingSlash(path)) path.bytes--;
|
|
return path;
|
|
}
|
|
|
|
String PathGetName(String path) {
|
|
intptr_t i = path.bytes - 2;
|
|
while (i >= 0 && path.text[i] != '/') i--;
|
|
path.text += i + 1, path.bytes -= i + 1;
|
|
return path;
|
|
}
|
|
|
|
String PathGetDrive(String path) {
|
|
uintptr_t i = 0;
|
|
while (i < path.bytes && path.text[i] != '/') i++;
|
|
path.bytes = i;
|
|
return path;
|
|
}
|
|
|
|
bool PathHasPrefix(String path, String prefix) {
|
|
prefix = PathRemoveTrailingSlash(prefix);
|
|
return StringStartsWith(path, prefix) && ((path.bytes > prefix.bytes && path.text[prefix.bytes] == '/') || (path.bytes == prefix.bytes));
|
|
}
|
|
|
|
bool PathReplacePrefix(String *knownPath, String oldPath, String newPath) {
|
|
if (PathHasPrefix(*knownPath, oldPath)) {
|
|
String after = StringSlice(*knownPath, oldPath.bytes, -1);
|
|
String path = StringAllocateAndFormat("%s%s", STRFMT(newPath), STRFMT(after));
|
|
StringDestroy(knownPath);
|
|
*knownPath = path;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|