summaryrefslogtreecommitdiffstats
path: root/src/common/strutil.h
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/common/strutil.h
Add the files
Diffstat (limited to 'src/common/strutil.h')
-rw-r--r--src/common/strutil.h115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/common/strutil.h b/src/common/strutil.h
new file mode 100644
index 0000000..00a8a08
--- /dev/null
+++ b/src/common/strutil.h
@@ -0,0 +1,115 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "common/wcwidth.h"
+
+#ifdef _WIN32
+// #include <shlwapi.h>
+__stdcall char* StrStrIA(const char* lpFirst, const char* lpSrch);
+ #define strcasestr StrStrIA
+#endif
+
+static inline bool ffStrSet(const char* str) {
+ if (str == NULL) {
+ return false;
+ }
+
+ while (isspace(*str)) {
+ str++;
+ }
+
+ return *str != '\0';
+}
+
+static inline bool ffStrStartsWithIgnCase(const char* str, const char* compareTo) {
+ return strncasecmp(str, compareTo, strlen(compareTo)) == 0;
+}
+
+static inline bool ffStrEqualsIgnCase(const char* str, const char* compareTo) {
+ return strcasecmp(str, compareTo) == 0;
+}
+
+static inline bool ffStrStartsWith(const char* str, const char* compareTo) {
+ return strncmp(str, compareTo, strlen(compareTo)) == 0;
+}
+
+static inline bool ffStrEndsWith(const char* str, const char* compareTo) {
+ size_t strLength = strlen(str);
+ size_t compareToLength = strlen(compareTo);
+ if (strLength < compareToLength) {
+ return false;
+ }
+ return memcmp(str + strLength - compareToLength, compareTo, compareToLength) == 0;
+}
+
+static inline bool ffStrEndsWithIgnCase(const char* str, const char* compareTo) {
+ size_t strLength = strlen(str);
+ size_t compareToLength = strlen(compareTo);
+ if (strLength < compareToLength) {
+ return false;
+ }
+ return strncasecmp(str + strLength - compareToLength, compareTo, compareToLength) == 0;
+}
+
+static inline bool ffStrEquals(const char* str, const char* compareTo) {
+ return strcmp(str, compareTo) == 0;
+}
+
+static inline bool ffStrContains(const char* str, const char* compareTo) {
+ return strstr(str, compareTo) != NULL;
+}
+
+static inline bool ffStrContainsIgnCase(const char* str, const char* compareTo) {
+ return strcasestr(str, compareTo) != NULL;
+}
+
+static inline bool ffStrContainsC(const char* str, char compareTo) {
+ return strchr(str, compareTo) != NULL;
+}
+
+static inline bool ffCharIsEnglishAlphabet(char c) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
+}
+
+static inline bool ffCharIsDigit(char c) {
+ return '0' <= c && c <= '9';
+}
+
+// Parse one UTF-8 character, returning consumed byte count and display width.
+// Invalid / incomplete sequence falls back to one-byte width=1.
+// If the Unicode codepoint is non-printable, width becomes 0.
+uint8_t ffUtf8CharLenWidth(const char* str, uint32_t length, uint8_t* width);
+
+uint32_t ffUtf8StrWidth(const char* str, uint32_t length);
+
+static inline bool ffCharIsHexDigit(char c) {
+ return ffCharIsDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
+}
+
+static inline int8_t ffHexCharToInt(char c) {
+ if (ffCharIsDigit(c)) {
+ return (int8_t) (c - '0');
+ } else if ('a' <= c && c <= 'f') {
+ return (int8_t) (c - 'a' + 10);
+ } else if ('A' <= c && c <= 'F') {
+ return (int8_t) (c - 'A' + 10);
+ } else {
+ return -1;
+ }
+}
+
+// Copies at most (dstBufSiz - 1) bytes from src to dst; dst is always null-terminated
+static inline char* ffStrCopy(char* __restrict__ dst, const char* __restrict__ src, size_t dstBufSiz) {
+ if (__builtin_expect(dst == NULL, false) || dstBufSiz == 0) {
+ return dst;
+ }
+
+ size_t len = strnlen(src, dstBufSiz - 1);
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+ return dst + len;
+}