#include "nm.h" #include "mem.h" #include "str.h" #include #include #include int tobi_nm_is_lead(unsigned char c) { return (c >= 'A' && c <= 'Z') || c == '_'; } int tobi_nm_is_tail(unsigned char c) { return tobi_nm_is_lead(c) || (c >= '0' && c <= '9'); } int tobi_nm_nameseg(tobi_rd *rd, char **out) { size_t start = rd->pos; uint8_t b[4]; if (!tobi_rd_bytes(rd, b, sizeof(b))) { rd->pos = start; return 0; } if (!tobi_nm_is_lead(b[0])) { rd->pos = start; rd->failed = 1; return 0; } for (size_t i = 1; i < 4; i++) { if (!tobi_nm_is_tail(b[i])) { rd->pos = start; rd->failed = 1; return 0; } } *out = tobi_xstrndup((const char *)b, 4); return 1; } static void add_seg(tobi_sb *sb, const char *seg, int *first) { if (!*first && sb->len > 0 && sb->buf[sb->len - 1] != '\\' && sb->buf[sb->len - 1] != '^') { tobi_sb_ch(sb, '.'); } tobi_sb_add(sb, seg); *first = 0; } int tobi_nm_namestring(tobi_rd *rd, char **out) { size_t start = rd->pos; tobi_sb sb; tobi_sb_init(&sb); uint8_t b = 0; int first = 1; if (!tobi_rd_peek(rd, &b)) { tobi_sb_free(&sb); return 0; } if (b == '\\') { (void)tobi_rd_u8(rd, &b); tobi_sb_ch(&sb, '\\'); } else { while (tobi_rd_peek(rd, &b) && b == '^') { (void)tobi_rd_u8(rd, &b); tobi_sb_ch(&sb, '^'); } } if (!tobi_rd_peek(rd, &b)) { tobi_sb_free(&sb); rd->pos = start; return 0; } if (b == 0x00) { (void)tobi_rd_u8(rd, &b); if (sb.len == 0) { tobi_sb_add(&sb, ""); } *out = tobi_sb_take(&sb); return 1; } if (b == 0x2e) { (void)tobi_rd_u8(rd, &b); for (int i = 0; i < 2; i++) { char *seg = NULL; if (!tobi_nm_nameseg(rd, &seg)) { tobi_sb_free(&sb); rd->pos = start; return 0; } add_seg(&sb, seg, &first); free(seg); } *out = tobi_sb_take(&sb); return 1; } if (b == 0x2f) { (void)tobi_rd_u8(rd, &b); uint8_t count = 0; if (!tobi_rd_u8(rd, &count) || count == 0) { tobi_sb_free(&sb); rd->pos = start; rd->failed = 1; return 0; } for (uint8_t i = 0; i < count; i++) { char *seg = NULL; if (!tobi_nm_nameseg(rd, &seg)) { tobi_sb_free(&sb); rd->pos = start; return 0; } add_seg(&sb, seg, &first); free(seg); } *out = tobi_sb_take(&sb); return 1; } char *seg = NULL; if (!tobi_nm_nameseg(rd, &seg)) { tobi_sb_free(&sb); rd->pos = start; return 0; } add_seg(&sb, seg, &first); free(seg); *out = tobi_sb_take(&sb); return 1; } static char *pop_scope(const char *scope) { if (!scope || strcmp(scope, "\\") == 0 || scope[0] == '\0') { return tobi_xstrdup("\\"); } const char *last = strrchr(scope, '.'); if (!last) { return tobi_xstrdup("\\"); } return tobi_xstrndup(scope, (size_t)(last - scope)); } char *tobi_nm_resolve(const char *scope, const char *name) { if (!name || name[0] == '\0') { return tobi_xstrdup(scope && scope[0] ? scope : "\\"); } if (name[0] == '\\') { return tobi_xstrdup(name); } char *cur = tobi_xstrdup(scope && scope[0] ? scope : "\\"); const char *rest = name; while (*rest == '^') { char *p = pop_scope(cur); free(cur); cur = p; rest++; } if (*rest == '\0' || strcmp(rest, "") == 0) { return cur; } tobi_sb sb; tobi_sb_init(&sb); if (strcmp(cur, "\\") == 0) { tobi_sb_add(&sb, "\\"); tobi_sb_add(&sb, rest); } else { tobi_sb_add(&sb, cur); tobi_sb_ch(&sb, '.'); tobi_sb_add(&sb, rest); } free(cur); return tobi_sb_take(&sb); } void tobi_ns_init(tobi_ns *ns) { ns->items = NULL; ns->len = 0; ns->cap = 0; } static void ns_ent_free(tobi_ns_ent *e) { free(e->path); free(e->name); free(e->owner); free(e->target); free(e->source); } void tobi_ns_free(tobi_ns *ns) { if (!ns) { return; } for (size_t i = 0; i < ns->len; i++) { ns_ent_free(&ns->items[i]); } free(ns->items); ns->items = NULL; ns->len = 0; ns->cap = 0; } const char *tobi_ns_kind_name(tobi_ns_kind kind) { switch (kind) { case TOBI_NS_UNKNOWN: return "unknown"; case TOBI_NS_SCOPE: return "scope"; case TOBI_NS_DEVICE: return "device"; case TOBI_NS_METHOD: return "method"; case TOBI_NS_PROCESSOR: return "processor"; case TOBI_NS_NAME: return "name"; case TOBI_NS_ALIAS: return "alias"; case TOBI_NS_OPREGION: return "opregion"; case TOBI_NS_FIELD: return "field"; case TOBI_NS_MUTEX: return "mutex"; case TOBI_NS_EVENT: return "event"; case TOBI_NS_EXTERNAL: return "external"; } return "invalid"; } const char *tobi_ns_leaf(const char *path) { if (!path || path[0] == '\0') { return ""; } const char *dot = strrchr(path, '.'); if (dot && dot[1]) { return dot + 1; } if (path[0] == '\\') { return path + 1; } return path; } tobi_ns_ent *tobi_ns_find(tobi_ns *ns, const char *path) { if (!ns || !path) { return NULL; } for (size_t i = 0; i < ns->len; i++) { if (strcmp(ns->items[i].path, path) == 0) { return &ns->items[i]; } } return NULL; } const tobi_ns_ent *tobi_ns_find_const(const tobi_ns *ns, const char *path) { if (!ns || !path) { return NULL; } for (size_t i = 0; i < ns->len; i++) { if (strcmp(ns->items[i].path, path) == 0) { return &ns->items[i]; } } return NULL; } const tobi_ns_ent *tobi_ns_find_method(const tobi_ns *ns, const char *name) { if (!ns || !name) { return NULL; } for (size_t i = 0; i < ns->len; i++) { const tobi_ns_ent *e = &ns->items[i]; if (e->kind == TOBI_NS_METHOD && strcmp(e->path, name) == 0) { return e; } } for (size_t i = 0; i < ns->len; i++) { const tobi_ns_ent *e = &ns->items[i]; if (e->kind == TOBI_NS_METHOD && strcmp(e->name, name) == 0) { return e; } } const char *leaf = strrchr(name, '.'); leaf = leaf ? leaf + 1 : name; for (size_t i = 0; i < ns->len; i++) { const tobi_ns_ent *e = &ns->items[i]; if (e->kind == TOBI_NS_METHOD && strcmp(e->name, leaf) == 0) { return e; } } return NULL; } static char *ns_parent(const char *scope) { if (!scope || strcmp(scope, "\\") == 0 || scope[0] == '\0') { return NULL; } const char *dot = strrchr(scope, '.'); if (!dot) { return tobi_xstrdup("\\"); } if (dot == scope || (scope[0] == '\\' && dot == scope + 1)) { return tobi_xstrdup("\\"); } return tobi_xstrndup(scope, (size_t)(dot - scope)); } static char *ns_join(const char *scope, const char *name) { tobi_sb sb; tobi_sb_init(&sb); if (!scope || scope[0] == '\0' || strcmp(scope, "\\") == 0) { tobi_sb_add(&sb, "\\"); tobi_sb_add(&sb, name); } else { tobi_sb_add(&sb, scope); tobi_sb_ch(&sb, '.'); tobi_sb_add(&sb, name); } return tobi_sb_take(&sb); } static const tobi_ns_ent *ns_follow_alias(const tobi_ns *ns, const tobi_ns_ent *ent) { const tobi_ns_ent *cur = ent; for (unsigned i = 0; cur && cur->kind == TOBI_NS_ALIAS && cur->target && i < 16; i++) { const tobi_ns_ent *next = tobi_ns_find_const(ns, cur->target); if (!next || next == cur) { break; } cur = next; } return cur; } const tobi_ns_ent *tobi_ns_lookup(const tobi_ns *ns, const char *scope, const char *name) { if (!ns || !name || name[0] == '\0') { return NULL; } if (name[0] == '\\' || name[0] == '^' || strcmp(name, "") == 0) { char *path = tobi_nm_resolve(scope, name); const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, path)); free(path); return ent; } char *cur = tobi_xstrdup(scope && scope[0] ? scope : "\\"); for (;;) { char *candidate = ns_join(cur, name); const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, candidate)); free(candidate); if (ent) { free(cur); return ent; } char *parent = ns_parent(cur); free(cur); if (!parent) { break; } cur = parent; } char *root = ns_join("\\", name); const tobi_ns_ent *ent = ns_follow_alias(ns, tobi_ns_find_const(ns, root)); free(root); return ent; } char *tobi_ns_resolve_path(const tobi_ns *ns, const char *scope, const char *name) { const tobi_ns_ent *ent = tobi_ns_lookup(ns, scope, name); if (ent) { return tobi_xstrdup(ent->path); } return tobi_nm_resolve(scope, name); } tobi_ns_ent *tobi_ns_add(tobi_ns *ns, tobi_ns_kind kind, const char *path, const char *owner, size_t off, unsigned args, unsigned flags, int external, const char *target, int *duplicate) { if (duplicate) { *duplicate = 0; } if (!ns || !path || path[0] == '\0') { return NULL; } tobi_ns_ent *old = tobi_ns_find(ns, path); if (old) { int old_is_external = old->external || old->kind == TOBI_NS_EXTERNAL; int same_scope = old->kind == TOBI_NS_SCOPE && kind == TOBI_NS_SCOPE; int same_decl = old->kind == kind && old->off == off; if (duplicate && !same_decl && !same_scope && !(old_is_external && !external)) { *duplicate = 1; } if (old_is_external && !external) { old->kind = kind; old->external = 0; old->off = off; old->args = args; } else if (kind == TOBI_NS_METHOD && old->kind == TOBI_NS_METHOD) { old->args = args; } old->flags = flags; if (target) { free(old->target); old->target = tobi_xstrdup(target); } return old; } if (ns->len == ns->cap) { ns->cap = tobi_xgrow_cap(ns->cap, ns->len + 1u, 16u); ns->items = tobi_xrealloc(ns->items, tobi_xmul_size(ns->cap, sizeof(ns->items[0]))); } tobi_ns_ent *e = &ns->items[ns->len++]; memset(e, 0, sizeof(*e)); e->path = tobi_xstrdup(path); e->name = tobi_xstrdup(tobi_ns_leaf(path)); e->owner = tobi_xstrdup(owner && owner[0] ? owner : "\\"); e->target = target ? tobi_xstrdup(target) : NULL; e->kind = kind; e->off = off; e->args = args; e->flags = flags; e->external = external; return e; } void tobi_ns_ent_set_source(tobi_ns_ent *e, const char *source, size_t input_index) { if (!e || !source) { return; } free(e->source); e->source = tobi_xstrdup(source); e->input_index = input_index; e->has_source = 1; } void tobi_ns_set_source(tobi_ns *ns, const char *source, size_t input_index) { if (!ns || !source) { return; } for (size_t i = 0; i < ns->len; i++) { tobi_ns_ent_set_source(&ns->items[i], source, input_index); } }