master ohmu/fastfetch-openbsd / src / common / strutil.h
ohmu  ·  2026-10-15
  1#pragma once
  2
  3#include <stdbool.h>
  4#include <stdint.h>
  5#include <string.h>
  6#include <ctype.h>
  7
  8#ifdef _WIN32
  9// #include <shlwapi.h>
 10__stdcall char* StrStrIA(const char* lpFirst, const char* lpSrch);
 11    #define strcasestr StrStrIA
 12#endif
 13
 14#define FF_STR_INDIR(x) #x
 15#define FF_STR(x) FF_STR_INDIR(x)
 16
 17// Everything below is a leaf predicate over the bytes it is given: it reads memory through its
 18// arguments and nothing else, so `gnu::pure` is accurate. `gnu::const` is deliberately not used --
 19// these are already `always_inline`, so it would buy no optimization, and it would become a lie the
 20// day one of them is rewritten around a lookup table.
 21//
 22// The `gnu::nonnull` indexes name the arguments that are dereferenced unconditionally. `ffStrSet`
 23// and `ffStrCopy` are not listed: both handle a null pointer explicitly.
 24
 25[[nodiscard, gnu::pure]]
 26static inline bool ffStrSet(const char* str) {
 27    if (str == nullptr) {
 28        return false;
 29    }
 30
 31    while (isspace(*str)) {
 32        str++;
 33    }
 34
 35    return *str != '\0';
 36}
 37
 38[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
 39static inline bool ffStrStartsWithIgnCase(const char* str, const char* compareTo) {
 40    return strncasecmp(str, compareTo, strlen(compareTo)) == 0;
 41}
 42
 43[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
 44static inline bool ffStrEqualsIgnCase(const char* str, const char* compareTo) {
 45    return strcasecmp(str, compareTo) == 0;
 46}
 47
 48[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
 49static inline bool ffStrStartsWith(const char* str, const char* compareTo) {
 50    return strncmp(str, compareTo, strlen(compareTo)) == 0;
 51}
 52
 53[[gnu::nonnull(1, 2), gnu::pure, nodiscard]]
 54static inline bool ffStrEndsWith(const char* str, const char* compareTo) {
 55    size_t strLength = strlen(str);
 56    size_t compareToLength = strlen(compareTo);
 57    if (strLength < compareToLength) {
 58        return false;
 59    }
 60    return memcmp(str + strLength - compareToLength, compareTo, compareToLength) == 0;
 61}
 62
 63[[gnu::nonnull(1, 2), gnu::pure, nodiscard]]
 64static inline bool ffStrEndsWithIgnCase(const char* str, const char* compareTo) {
 65    size_t strLength = strlen(str);
 66    size_t compareToLength = strlen(compareTo);
 67    if (strLength < compareToLength) {
 68        return false;
 69    }
 70    return strncasecmp(str + strLength - compareToLength, compareTo, compareToLength) == 0;
 71}
 72
 73[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
 74static inline bool ffStrEquals(const char* str, const char* compareTo) {
 75    return strcmp(str, compareTo) == 0;
 76}
 77
 78[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
 79static inline bool ffStrContains(const char* str, const char* compareTo) {
 80    return strstr(str, compareTo) != nullptr;
 81}
 82
 83[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
 84static inline bool ffStrContainsIgnCase(const char* str, const char* compareTo) {
 85    return strcasestr(str, compareTo) != nullptr;
 86}
 87
 88[[gnu::always_inline, gnu::nonnull(1), gnu::pure, nodiscard]]
 89static inline bool ffStrContainsC(const char* str, char compareTo) {
 90    return strchr(str, compareTo) != nullptr;
 91}
 92
 93[[gnu::always_inline, gnu::pure, nodiscard]]
 94static inline bool ffCharIsEnglishAlphabet(char c) {
 95    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
 96}
 97
 98[[gnu::always_inline, gnu::pure, nodiscard]]
 99static inline bool ffCharIsDigit(char c) {
100    return '0' <= c && c <= '9';
101}
102
103// Parse one UTF-8 character, returning consumed byte count and display width.
104// Invalid / incomplete sequence falls back to one-byte width=1.
105// If the Unicode codepoint is non-printable, width becomes 0.
106// Not `pure`: it stores the width through `width` when that is not null.
107[[gnu::nonnull(1)]]
108uint8_t ffUtf8CharLenWidth(const char* str, uint32_t length, uint8_t* width);
109
110[[gnu::nonnull(1), gnu::pure, nodiscard]]
111uint32_t ffUtf8StrWidth(const char* str, uint32_t length);
112
113[[gnu::always_inline, gnu::pure, nodiscard]]
114static inline bool ffCharIsHexDigit(char c) {
115    return ffCharIsDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
116}
117
118[[gnu::always_inline, gnu::pure, nodiscard]]
119static inline int8_t ffHexCharToInt(char c) {
120    if (ffCharIsDigit(c)) {
121        return (int8_t) (c - '0');
122    } else if ('a' <= c && c <= 'f') {
123        return (int8_t) (c - 'a' + 10);
124    } else if ('A' <= c && c <= 'F') {
125        return (int8_t) (c - 'A' + 10);
126    } else {
127        return -1;
128    }
129}
130
131// Copies at most (dstBufSiz - 1) bytes from src to dst; dst is always null-terminated
132// Returns a pointer to the end of the copy, which callers are free to ignore, so not `nodiscard`.
133// Not `pure`: it writes through `dst`. `dst` may be null, `src` may not.
134[[gnu::nonnull(2)]]
135static inline char* ffStrCopy(char* __restrict__ dst, const char* __restrict__ src, size_t dstBufSiz) {
136    if (__builtin_expect(dst == nullptr, false) || dstBufSiz == 0) {
137        return dst;
138    }
139
140    size_t len = strnlen(src, dstBufSiz - 1);
141    memcpy(dst, src, len);
142    dst[len] = '\0';
143    return dst + len;
144}