This commit is contained in:
snarmph 2026-02-26 13:54:59 +01:00
parent c7291ead23
commit ff748bd3ff
2 changed files with 386 additions and 68 deletions

372
colla.c
View file

@ -473,7 +473,7 @@ usize strv_rfind(strview_t ctx, char c, usize from_right) {
if (ctx.len == 0) return STR_NONE;
if (from_right > ctx.len) from_right = ctx.len;
isize end = (isize)(ctx.len - from_right);
for (isize i = end; i >= 0; --i) {
for (isize i = end - 1; i >= 0; --i) {
if (ctx.buf[i] == c) {
return (usize)i;
}
@ -508,6 +508,11 @@ bool char_is_num(char c) {
return c >= '0' && c <= '9';
}
bool char_is_hex(char c) {
c = char_lower(c);
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
}
char char_lower(char c) {
return c >= 'A' && c <= 'Z' ? c + 32 : c;
}
@ -930,85 +935,318 @@ bool ibstr_get_i64(ibstream_t *ib, i64 *out) {
// == REGEX ========================================================
bool rg__match_impl(rg_match_t *ctx, instream_t *r, instream_t *t) {
bool match_any = false;
// adapted from rob pike regular expression matcher
usize beg = STR_END;
usize end = STR_END;
bool rg__match_here(instream_t r, instream_t t);
while (!istr_is_finished(r) && !istr_is_finished(t)) {
char rc = istr_peek(r);
char tc = istr_peek(t);
if (rc == '\\') {
if (istr_peek_next(r) != '*' && istr_peek_next(r) != '\\') {
warn("expected * or \\ after escape character");
return false;
}
istr_skip(r, 1);
continue;
bool rg__match_star(char c, instream_t r, instream_t t) {
do {
if (rg__match_here(r, t)) {
return true;
}
if (istr_peek_next(r) == '*' && rc == tc) {
match_any = true;
istr_skip(r, 2);
istr_skip(t, 1);
beg = istr_tell(t);
continue;
}
if (rc == '*') {
match_any = true;
istr_skip(r, 1);
istr_skip(t, 1);
beg = istr_tell(t);
continue;
}
if (rc == tc) {
if (match_any && istr_peek_next(r) == istr_peek_next(t)) {
end = istr_tell(t);
ctx->text[ctx->count++] = strv(t->beg + beg, end - beg);
beg = STR_END;
match_any = false;
}
istr_skip(r, 1);
istr_skip(t, 1);
continue;
}
if (match_any) {
istr_skip(t, 1);
continue;
}
return false;
}
if (match_any && istr_is_finished(r)) {
end = t->len;
ctx->text[ctx->count++] = strv(t->beg + beg, end - beg);
return true;
}
return ctx->count > 0 && istr_is_finished(r) && istr_is_finished(t);
} while (!istr_is_finished(&t) && (istr_get(&t) == c || c == '.'));
return false;
}
rg_match_t rg_match(strview_t rg, strview_t text) {
rg_match_t out = {0};
bool rg__match_here(instream_t r, instream_t t) {
char rc = istr_peek(&r);
char rcn = istr_peek_next(&r);
if (rc == '\0') {
return true;
}
if (rcn == '*') {
istr_skip(&r, 2);
return rg__match_star(rc, r, t);
}
if (rc == '$' && rcn == '\0') {
return istr_peek(&t) == '\0';
}
if (!istr_is_finished(&t) && (rc == '.' || rc == istr_peek(&t))) {
istr_skip(&r, 1);
istr_skip(&t, 1);
return rg__match_here(r, t);
}
return false;
}
bool rg__matches_impl(instream_t r, instream_t t) {
do {
if (rg__match_here(r, t)) {
return true;
}
} while (istr_get(&t) != '\0');
return false;
}
bool rg_matches(strview_t rg, strview_t text) {
if (strv_contains(rg, '*')) {
instream_t r = istr_init(rg);
instream_t t = istr_init(text);
out.matches = rg__match_impl(&out, &r, &t);
return rg__matches_impl(r, t);
}
else {
out.matches = strv_equals(rg, text);
return strv_equals(rg, text);
}
return out;
}
bool rg_match_easy(strview_t rg, strview_t text) {
return rg_match(rg, text).matches;
///////////////////////////////////////////////////
// glob has the following special characters:
// - * matches any string
// - ? matches any character
// - [ start a match group
// - cannot be empty, so this matches either
// ] or [:
// [][]
// []]
// - if theres a - between two characters, it
// matches a range:
// [A-Za-z0-9] is equal to
// [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789]
// - if there's a !, it negates the match,
// so [!abc] matches anything but a, b, or c
// - if there's a + after the last square bracket,
// it matches 1+ times
// g: abc*_d?f[0-9]+c
// t: abcdef_def901c
// ----------------
// g[0] == t[0] -> ++g, ++t
// g: bc*_d?f[0-9]+c
// t: bcdef_def901c
// ----------------
// g[0] == t[0] -> ++g, ++t
// g: c*_d?f[0-9]+c
// t: cdef_def901c
// ----------------
// g[0] == t[0] -> ++g, ++t
// g: *_d?f[0-9]+c
// t: def_def901c
// ----------------
// g[0] == * -> ++g, star match
// g: _d?f[0-9]+c
// t: def_def901c
// c = _
// ----------------
// g[0] != t[0]
// star: ++t
// g: _d?f[0-9]+c
// t: ef_def901c
// ----------------
// g[0] != t[0]
// star: ++t
// g: _d?f[0-9]+c
// t: f_def901c
// ----------------
// g[0] != t[0]
// star: ++t
// g: _d?f[0-9]+c
// t: _def901c
// ----------------
// g[0] == t[0]
// ++g, ++t
// g: d?f[0-9]+c
// t: def901c
// return true from star
// ----------------
// g[0] == t[0] -> ++g, ++t
// g: ?f[0-9]+c
// t: ef901c
// ----------------
// g[0] == ? -> ++g, ++t
// g: f[0-9]+c
// t: f901c
// ----------------
// g[0] == t[0] -> ++g, ++t
// g: [0-9]+c
// t: 901c
// ----------------
// g[0] == [
// begin square:
// -- grab pattern
// s = strv_empty
// while (s == strv_empty)
// s += g.getUntil(])
// matches_multi = g.peek() == '+'
// s: 0-9
// -- parse pattern
// type: match
// multi: matches_multi
// ranges[MAX_RANGES] = {
// { from: 0, to: 9 }
// }
// ----------------
// matched_once = false
// do {
// if (!s.inRange(t[0])) {
// break;
// }
// matched_once true
// ++t
// } while (s.multi);
// return matched_atleast_once
// ----------------
// t: 901c
// t[0] == 9 -> in range = true
// ++t
// t: 01c
// t[0] == 0 -> in rage
// ++t
// t: 1c
// t[0] == 1 -> in rage
// ++t
// t: c
// t[0] == 1 -> not in rage
// return true
// ----------------
// g: c
// t: c
// ----------------
// g[0] == t[0] -> ++g, ++t
// g:
// t:
// ----------------
// g is empty -> return true
//
bool glob__match_here(instream_t *g, instream_t *t);
bool glob__match_star(instream_t *g, instream_t *t) {
char c = istr_get(g);
char n = istr_peek(g);
do {
if (istr_get(t) == c) {
return true;
}
} while (!istr_is_finished(t));
return false;
}
#define GLOB_MAX_RANGES 128
typedef struct {
char from;
char to;
} glob_range_t;
typedef struct {
bool multi;
bool exclude;
glob_range_t ranges[GLOB_MAX_RANGES];
int range_count;
} glob_pat_t;
bool glob__pat_is_in_range(glob_pat_t *pat, char c) {
for (int i = 0; i < pat->range_count; ++i) {
if (c >= pat->ranges[i].from &&
c <= pat->ranges[i].to
) {
return true;
}
}
return false;
}
bool glob__match_pattern(instream_t *t, strview_t pat, bool multi) {
glob_pat_t p = {
.multi = multi,
.exclude = pat.buf[0] == '!',
};
for (usize i = 0; i < pat.len; ++i) {
char from = '\0', to = '\0';
if (i > 0 && pat.buf[i] == '-' && (i + 1) < pat.len) {
from = pat.buf[i - 1];
to = pat.buf[i + 1];
++i;
}
else if ((i + 1) >= pat.len || pat.buf[i+1] != '-') {
from = to = pat.buf[i];
}
if (from && to) {
p.ranges[p.range_count++] = (glob_range_t){ from, to };
}
}
bool matched_atleast_once = false;
do {
char c = istr_peek(t);
bool is_in_range = glob__pat_is_in_range(&p, c);
if ((!is_in_range && !p.exclude) || (is_in_range && p.exclude)) {
break;
}
matched_atleast_once = true;
istr_skip(t, 1);
} while (p.multi);
return matched_atleast_once;
}
bool glob__match_here(instream_t *g, instream_t *t) {
char gc = istr_peek(g);
if (gc == '*') {
istr_skip(g, 1);
if (istr_is_finished(g)) {
// set t (text) to empty, as the rest of the patter is sure to match
*t = istr_init(STRV_EMPTY);
return true;
}
return glob__match_star(g, t);
}
if (gc == '[') {
// skip [
istr_skip(g, 1);
strview_t pattern = istr_get_view(g, ']');
if (pattern.len == 0) {
istr_skip(g, 1);
pattern = istr_get_view(g, ']');
// add first ]
pattern.buf--;
pattern.len++;
}
// skip ]
istr_skip(g, 1);
bool multi = false;
if (istr_peek(g) == '+') {
istr_skip(g, 1);
multi = true;
}
return glob__match_pattern(t, pattern, multi);
}
if (!istr_is_finished(t) && (gc == '?' || gc == istr_peek(t))) {
istr_skip(g, 1);
istr_skip(t, 1);
return true;
}
return false;
}
bool glob__impl(instream_t *g, instream_t *t) {
while (!istr_is_finished(g) && !istr_is_finished(t)) {
if (!glob__match_here(g, t)) {
return false;
}
}
return istr_get(g) == '\0' && istr_get(t) == '\0';
}
bool glob_matches(strview_t glob, strview_t text) {
u8 tmpbuf[2048] = {0};
arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
// HACK: convert to a regex match as that works better for now :(
outstream_t rg = ostr_init(&scratch);
for (usize i = 0; i < glob.len; ++i) {
if (glob.buf[i] == '*') {
ostr_putc(&rg, '.');
}
ostr_putc(&rg, glob.buf[i]);
}
instream_t pattern = istr_init(ostr_as_view(&rg));
instream_t t = istr_init(text);
return rg__matches_impl(pattern, t);
// instream_t g = istr_init(glob);
// instream_t t = istr_init(text);
// return glob__impl(&g, &t);
}
// == ARENA ========================================================

View file

@ -15,6 +15,10 @@
#endif
#endif
#if COLLA_CMT_LIB
#pragma comment(lib, "Advapi32")
#endif
#ifndef PROCESSOR_ARCHITECTURE_ARM64
#define PROCESSOR_ARCHITECTURE_ARM64 12
#endif
@ -352,6 +356,80 @@ oshandle_t os_win_conin(void) {
return w32_data.hconin;
}
oshandle_t os_win_regopen(arena_t scratch, strview_t name) {
HKEY key = NULL;
str16_t key_name = strv_to_str16(&scratch, name);
LONG result = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
key_name.buf,
0,
KEY_READ,
&key
);
if (result != ERROR_SUCCESS) {
err("failed to open registry key %v", name);
return os_handle_zero();
}
return (oshandle_t){ .data = (uptr)key };
}
void os_win_regclose(oshandle_t key) {
RegCloseKey((HKEY)key.data);
}
str_t os_win_regread_str(arena_t *arena, oshandle_t key, strview_t value) {
u8 buf[KB(5)] = {0};
DWORD bufsize = sizeof(buf);
arena_t scratch = *arena;
str16_t value_str = strv_to_str16(&scratch, value);
DWORD type = 0;
LONG result = RegQueryValueExW(
(HKEY)key.data,
value_str.buf,
NULL,
&type,
buf,
&bufsize
);
if (result != ERROR_SUCCESS) {
err("failed to read registry key %v", value);
return STR_EMPTY;
}
return str_from_str16(arena, str16_init((char16_t*)buf, 0));
}
i64 os_win_regread_int(arena_t scratch, oshandle_t key, strview_t value) {
DWORD regvalue = 0;
DWORD type = 0;
DWORD bufsize = sizeof(regvalue);
str16_t value_str = strv_to_str16(&scratch, value);
LONG result = RegQueryValueExW(
(HKEY)key.data,
value_str.buf,
NULL,
&type,
(u8*)&regvalue,
&bufsize
);
if (result != ERROR_SUCCESS) {
err("failed to read registry key %v", value);
return 0;
}
return (i64)regvalue;
}
str_t os_win_regkey_str(arena_t *arena, strview_t key, strview_t value) {
oshandle_t hkey = os_win_regopen(*arena, key);
str_t out = os_win_regread_str(arena, hkey, value);
os_win_regclose(hkey);
return out;
}
// == FILE ======================================
#define OS_SMALL_SCRATCH() \
@ -690,6 +768,7 @@ oshandle_t os_run_cmd_async(arena_t scratch, os_cmd_t *cmd, os_cmd_options_t *op
HANDLE hstdout_write = NULL;
HANDLE hstderr_write = NULL;
HANDLE hstdin_read = NULL;
bool quiet = options ? options->quiet : false;
SECURITY_ATTRIBUTES sa_attr = {
.nLength = sizeof(SECURITY_ATTRIBUTES),
@ -777,7 +856,7 @@ oshandle_t os_run_cmd_async(arena_t scratch, os_cmd_t *cmd, os_cmd_options_t *op
options->env->data = NULL;
}
if (!success) {
if (!success && !quiet) {
err("couldn't create process (%v): %v", cmd_str, os_get_error_string(os_get_last_error()));
return os_handle_zero();
}
@ -1357,3 +1436,4 @@ void sk_destroy_event(oshandle_t handle) {
}
#endif