diff --git a/colla.c b/colla.c index b8dee09..abf3ce4 100644 --- a/colla.c +++ b/colla.c @@ -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 ======================================================== diff --git a/colla_win32.c b/colla_win32.c index 129d664..8e9cf53 100644 --- a/colla_win32.c +++ b/colla_win32.c @@ -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*)®value, + &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 +