From 5b7c366234967d61f705e5d6dbb2327b25b2e110 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Wed, 8 Jan 2025 14:54:11 -0800 Subject: [PATCH] cfg data structure first pass, serialization/deserialization, & building, to prep for all rd_entity code deletion --- src/raddbg/raddbg_core.c | 526 +++++++++++++++++++++++++++++++++++++++ src/raddbg/raddbg_core.h | 71 +++++- src/raddbg/raddbg_main.c | 57 +++++ 3 files changed, 653 insertions(+), 1 deletion(-) diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 5eb5ee6e0..31fdd38e1 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -1031,6 +1031,327 @@ rd_name_release(String8 string) SLLStackPush(rd_state->free_name_chunks[bucket_idx], node); } +//////////////////////////////// +//~ rjf: New Config/Entity Data Structure Functions + +internal RD_Cfg * +rd_cfg_alloc(void) +{ + RD_Cfg *result = rd_state->free_cfg; + if(result) + { + SLLStackPop(rd_state->free_cfg); + } + else + { + result = push_array_no_zero(rd_state->arena, RD_Cfg, 1); + } + U64 old_gen = result->gen; + MemoryZeroStruct(result); + result->first = result->last = result->next = result->prev = result->parent = &rd_nil_cfg; + result->gen = old_gen + 1; + return result; +} + +internal void +rd_cfg_release(RD_Cfg *cfg) +{ + Temp scratch = scratch_begin(0, 0); + rd_cfg_unhook(cfg->parent, cfg); + RD_CfgList nodes = {0}; + for(RD_Cfg *c = cfg; c != &rd_nil_cfg; c = rd_cfg_rec__depth_first(cfg, c).next) + { + rd_cfg_list_push(scratch.arena, &nodes, c); + } + for(RD_CfgNode *n = nodes.first; n != 0; n = n->next) + { + RD_Cfg *c = n->v; + c->gen += 1; + rd_name_release(c->string); + SLLStackPush(rd_state->free_cfg, c); + } + scratch_end(scratch); +} + +internal RD_Cfg * +rd_cfg_new(RD_Cfg *parent, String8 string) +{ + RD_Cfg *cfg = rd_cfg_alloc(); + rd_cfg_insert_child(parent, parent->last, cfg); + rd_cfg_equip_string(cfg, string); + return cfg; +} + +internal RD_Cfg * +rd_cfg_newf(RD_Cfg *parent, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + RD_Cfg *result = rd_cfg_new(parent, string); + va_end(args); + scratch_end(scratch); + return result; +} + +internal void +rd_cfg_equip_string(RD_Cfg *cfg, String8 string) +{ + if(cfg->string.size != 0) + { + rd_name_release(cfg->string); + } + cfg->string = rd_name_alloc(string); +} + +internal void +rd_cfg_insert_child(RD_Cfg *parent, RD_Cfg *prev_child, RD_Cfg *new_child) +{ + DLLInsert_NPZ(&rd_nil_cfg, parent->first, parent->last, prev_child, new_child, next, prev); + new_child->parent = parent; +} + +internal void +rd_cfg_unhook(RD_Cfg *parent, RD_Cfg *child) +{ + if(parent == child->parent && parent != &rd_nil_cfg) + { + DLLRemove_NPZ(&rd_nil_cfg, parent->first, parent->last, child, next, prev); + child->parent = &rd_nil_cfg; + } +} + +internal RD_Cfg * +rd_cfg_child_from_string(RD_Cfg *parent, String8 string) +{ + RD_Cfg *child = &rd_nil_cfg; + for(RD_Cfg *c = parent->first; c != &rd_nil_cfg; c = c->next) + { + if(str8_match(c->string, string, 0)) + { + child = c; + break; + } + } + return child; +} + +internal RD_CfgList +rd_cfg_child_list_from_string(Arena *arena, RD_Cfg *parent, String8 string) +{ + RD_CfgList result = {0}; + for(RD_Cfg *child = parent->first; child != &rd_nil_cfg; child = child->next) + { + if(str8_match(child->string, string, 0)) + { + rd_cfg_list_push(arena, &result, child); + } + } + return result; +} + +internal RD_CfgList +rd_cfg_top_level_list_from_string(Arena *arena, String8 string) +{ + RD_CfgList result = {0}; + for(RD_Cfg *bucket = rd_state->root_cfg->first; bucket != &rd_nil_cfg; bucket = bucket->next) + { + for(RD_Cfg *tln = bucket->first; tln != &rd_nil_cfg; tln = tln->next) + { + if(str8_match(tln->string, string, 0)) + { + rd_cfg_list_push(arena, &result, tln); + } + } + } + return result; +} + +internal RD_CfgList +rd_cfg_tree_list_from_string(Arena *arena, String8 string) +{ + RD_CfgList result = {0}; + Temp scratch = scratch_begin(&arena, 1); + MD_Node *root = md_tree_from_string(scratch.arena, string); + for MD_EachNode(tln, root->first) + { + RD_Cfg *dst_root_n = &rd_nil_cfg; + RD_Cfg *dst_active_parent_n = &rd_nil_cfg; + MD_NodeRec rec = {0}; + for(MD_Node *src_n = tln; !md_node_is_nil(src_n); src_n = rec.next) + { + RD_Cfg *dst_n = rd_cfg_alloc(); + rd_cfg_equip_string(dst_n, src_n->string); + if(dst_active_parent_n != &rd_nil_cfg) + { + rd_cfg_insert_child(dst_active_parent_n, dst_active_parent_n->last, dst_n); + } + rec = md_node_rec_depth_first_pre(src_n, tln); + if(rec.push_count > 0) + { + if(dst_active_parent_n == &rd_nil_cfg) + { + dst_root_n = dst_n; + } + dst_active_parent_n = dst_n; + } + else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1) + { + dst_active_parent_n = dst_active_parent_n->parent; + } + } + rd_cfg_list_push(arena, &result, dst_root_n); + } + scratch_end(scratch); + return result; +} + +internal String8 +rd_string_from_cfg_tree(Arena *arena, RD_Cfg *cfg) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List strings = {0}; + { + typedef struct NestTask NestTask; + struct NestTask + { + NestTask *next; + RD_Cfg *cfg; + B32 is_simple; + }; + NestTask *top_nest_task = 0; + RD_CfgRec rec = {0}; + for(RD_Cfg *c = cfg; c != &rd_nil_cfg; c = rec.next) + { + // rjf: push name of this node + if(c->string.size != 0 || c->first == &rd_nil_cfg) + { + String8List c_name_strings = {0}; + B32 name_can_be_pushed_standalone = 0; + { + Temp temp = temp_begin(scratch.arena); + MD_TokenizeResult c_name_tokenize = md_tokenize_from_text(temp.arena, c->string); + name_can_be_pushed_standalone = (c_name_tokenize.tokens.count == 1 && c_name_tokenize.tokens.v[0].flags & (MD_TokenFlag_Identifier| + MD_TokenFlag_Numeric| + MD_TokenFlag_StringLiteral| + MD_TokenFlag_Symbol)); + temp_end(temp); + } + if(name_can_be_pushed_standalone) + { + str8_list_push(scratch.arena, &c_name_strings, c->string); + } + else + { + String8 c_name_escaped = escaped_from_raw_str8(scratch.arena, c->string); + str8_list_push(scratch.arena, &c_name_strings, str8_lit("\"")); + str8_list_push(scratch.arena, &c_name_strings, c_name_escaped); + str8_list_push(scratch.arena, &c_name_strings, str8_lit("\"")); + } + if(top_nest_task != 0 && top_nest_task->is_simple) + { + str8_list_push(scratch.arena, &strings, str8_lit(" ")); + } + str8_list_concat_in_place(&strings, &c_name_strings); + } + + // rjf: grab next recursion + rec = rd_cfg_rec__depth_first(cfg, c); + + // rjf: determine if this node is simple, and can be encoded on a single line - + // if so, push a new nesting task onto the stack + if(c->first != &rd_nil_cfg) + { + B32 is_simple_children_list = 1; + for(RD_Cfg *child = c->first; child != &rd_nil_cfg; child = child->next) + { + if(child->first != &rd_nil_cfg && child != c->last) + { + is_simple_children_list = 0; + break; + } + } + NestTask *task = push_array(scratch.arena, NestTask, 1); + task->cfg = c; + task->is_simple = is_simple_children_list; + SLLStackPush(top_nest_task, task); + } + + // rjf: tree navigations -> encode hierarchy + if(rec.push_count > 0) + { + if(top_nest_task->is_simple && c->string.size != 0) + { + str8_list_push(scratch.arena, &strings, str8_lit(":")); + } + else + { + if(c->string.size != 0) + { + str8_list_push(scratch.arena, &strings, str8_lit(":\n")); + } + str8_list_push(scratch.arena, &strings, str8_lit("{")); + } + } + else + { + for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1, SLLStackPop(top_nest_task)) + { + if(top_nest_task->is_simple) + { + if(top_nest_task->cfg->string.size == 0) + { + str8_list_push(scratch.arena, &strings, str8_lit(" }")); + } + } + else + { + str8_list_push(scratch.arena, &strings, str8_lit("\n}")); + } + } + } + if(!top_nest_task || top_nest_task->is_simple == 0) + { + str8_list_push(scratch.arena, &strings, str8_lit("\n")); + } + } + } + String8 result_unindented = str8_list_join(scratch.arena, &strings, 0); + String8 result = indented_from_string(arena, result_unindented); + scratch_end(scratch); + return result; +} + +internal RD_CfgRec +rd_cfg_rec__depth_first(RD_Cfg *root, RD_Cfg *cfg) +{ + RD_CfgRec rec = {&rd_nil_cfg}; + if(cfg->first != &rd_nil_cfg) + { + rec.next = cfg->first; + rec.push_count = 1; + } + else for(RD_Cfg *p = cfg; p != root; p = p->parent, rec.pop_count += 1) + { + if(p->next != &rd_nil_cfg) + { + rec.next = p->next; + break; + } + } + return rec; +} + +internal void +rd_cfg_list_push(Arena *arena, RD_CfgList *list, RD_Cfg *cfg) +{ + RD_CfgNode *n = push_array(arena, RD_CfgNode, 1); + n->v = cfg; + SLLQueuePush(list->first, list->last, n); + list->count += 1; +} + //////////////////////////////// //~ rjf: Entity State Functions @@ -11169,6 +11490,15 @@ rd_init(CmdLine *cmdln) rd_state->top_regs = &rd_state->base_regs; rd_clear_bindings(); + // rjf: set up top-level config entity trees + { + rd_state->root_cfg = rd_cfg_alloc(); + RD_Cfg *user_tree = rd_cfg_new(rd_state->root_cfg, str8_lit("user")); + RD_Cfg *project_tree = rd_cfg_new(rd_state->root_cfg, str8_lit("project")); + RD_Cfg *command_line_tree = rd_cfg_new(rd_state->root_cfg, str8_lit("command_line")); + RD_Cfg *transient = rd_cfg_new(rd_state->root_cfg, str8_lit("transient")); + } + // rjf: set up user / project paths { Temp scratch = scratch_begin(0, 0); @@ -11332,6 +11662,12 @@ rd_frame(void) local_persist S32 depth = 0; log_scope_begin(); + //- TODO(rjf): @cfg debugging: stringify the current cfg tree + { + String8 string = rd_string_from_cfg_tree(scratch.arena, rd_state->root_cfg); + int x = 0; + } + ////////////////////////////// //- rjf: do per-frame resets // @@ -12147,6 +12483,32 @@ rd_frame(void) case RD_CmdKind_OpenUser: case RD_CmdKind_OpenProject: { + //- TODO(rjf): @cfg load & apply user/project data to the cfg data structure + { + String8 file_root_key = (kind == RD_CmdKind_OpenUser ? str8_lit("user") : str8_lit("project")); + RD_Cfg *file_root = rd_cfg_child_from_string(rd_state->root_cfg, file_root_key); + + //- rjf: eliminate all old state under this file tree + for(RD_Cfg *tln = file_root->first, *next = &rd_nil_cfg; + tln != &rd_nil_cfg; + tln = next) + { + next = tln->next; + rd_cfg_release(tln); + } + + //- rjf: load & parse the new file, generate cfg entities for it + String8 file_path = rd_regs()->file_path; + String8 file_data = os_data_from_file_path(scratch.arena, file_path); + RD_CfgList file_cfg_list = rd_cfg_tree_list_from_string(scratch.arena, file_data); + + //- rjf: insert the new cfg entities into this file tree + for(RD_CfgNode *n = file_cfg_list.first; n != 0; n = n->next) + { + rd_cfg_insert_child(file_root, file_root->last, n->v); + } + } + // TODO(rjf): dear lord this is so overcomplicated, this needs to be collapsed down & simplified ASAP B32 load_cfg[RD_CfgSrc_COUNT] = {0}; @@ -12320,6 +12682,32 @@ rd_frame(void) //- rjf: keep track of recent projects if(src == RD_CfgSrc_Project) { + //- TODO(rjf): @cfg keep track of recent projects + { + RD_Cfg *user = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("user")); + RD_CfgList recent_projects = rd_cfg_child_list_from_string(scratch.arena, user, str8_lit("recent_project")); + RD_Cfg *recent_project = &rd_nil_cfg; + for(RD_CfgNode *n = recent_projects.first; n != 0; n = n->next) + { + if(path_match_normalized(n->v->string, cfg_path)) + { + recent_project = n->v; + break; + } + } + if(recent_project == &rd_nil_cfg) + { + recent_project = rd_cfg_new(user, str8_lit("recent_project")); + rd_cfg_new(recent_project, path_normalized_from_string(scratch.arena, cfg_path)); + } + rd_cfg_unhook(user, recent_project); + rd_cfg_insert_child(user, &rd_nil_cfg, recent_project); + recent_projects = rd_cfg_child_list_from_string(scratch.arena, user, str8_lit("recent_project")); + if(recent_projects.count > 32) + { + rd_cfg_release(recent_projects.last->v); + } + } RD_EntityList recent_projects = rd_query_cached_entity_list_with_kind(RD_EntityKind_RecentProject); RD_Entity *recent_project = &rd_nil_entity; for(RD_EntityNode *n = recent_projects.first; n != 0; n = n->next) @@ -13548,6 +13936,15 @@ rd_frame(void) String8 map_dst = str8_list_join(scratch.arena, &map_dst_parts, &map_join); //- rjf: store as file path map entity + //- TODO(rjf): @cfg store as file path map entity + { + RD_Cfg *user = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("user")); + RD_Cfg *map = rd_cfg_new(user, str8_lit("file_path_map")); + RD_Cfg *src = rd_cfg_new(map, str8_lit("source")); + RD_Cfg *dst = rd_cfg_new(map, str8_lit("dest")); + rd_cfg_new(src, map_src); + rd_cfg_new(dst, map_dst); + } RD_Entity *map = rd_entity_alloc(rd_entity_root(), RD_EntityKind_FilePathMap); RD_Entity *src = rd_entity_alloc(map, RD_EntityKind_Source); RD_Entity *dst = rd_entity_alloc(map, RD_EntityKind_Dest); @@ -13880,6 +14277,34 @@ rd_frame(void) if(rd_regs()->file_path.size != 0) { String8 path = path_normalized_from_string(scratch.arena, rd_regs()->file_path); + + //- TODO(rjf): @cfg record file in project + { + RD_Cfg *project = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("project")); + RD_CfgList recent_files = rd_cfg_child_list_from_string(scratch.arena, project, str8_lit("recent_files")); + RD_Cfg *recent_file = &rd_nil_cfg; + for(RD_CfgNode *n = recent_files.first; n != 0; n = n->next) + { + if(path_match_normalized(n->v->string, path)) + { + recent_file = n->v; + break; + } + } + if(recent_file == &rd_nil_cfg) + { + recent_file = rd_cfg_new(project, str8_lit("recent_file")); + rd_cfg_new(recent_file, path); + } + rd_cfg_unhook(project, recent_file); + rd_cfg_insert_child(project, &rd_nil_cfg, recent_file); + recent_files = rd_cfg_child_list_from_string(scratch.arena, project, str8_lit("recent_files")); + if(recent_files.count > 256) + { + rd_cfg_release(recent_files.last->v); + } + } + RD_EntityList recent_files = rd_query_cached_entity_list_with_kind(RD_EntityKind_RecentFile); if(recent_files.count >= 256) { @@ -15075,6 +15500,49 @@ rd_frame(void) U64 vaddr = rd_regs()->vaddr; if(file_path.size != 0 || string.size != 0 || vaddr != 0) { + //- TODO(rjf): @cfg add/toggle breakpoint + { + B32 removed_already_existing = 0; + if(kind == RD_CmdKind_ToggleBreakpoint) + { + RD_CfgList bps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("breakpoint")); + for(RD_CfgNode *n = bps.first; n != 0; n = n->next) + { + RD_Cfg *bp = n->v; + RD_Cfg *loc = rd_cfg_child_from_string(bp, str8_lit("location")); + S64 loc_line = 0; + U64 loc_vaddr = 0; + B32 loc_matches_file_pt = (file_path.size != 0 && path_match_normalized(loc->first->string, file_path) && try_s64_from_str8_c_rules(loc->first->first->string, &loc_line) && loc_line == pt.line); + B32 loc_matches_string = (string.size != 0 && str8_match(loc->first->string, string, 0)); + B32 loc_matches_vaddr = (vaddr != 0 && try_u64_from_str8_c_rules(loc->first->string, &loc_vaddr) && loc_vaddr == vaddr); + if(loc_matches_file_pt || loc_matches_string || loc_matches_vaddr) + { + rd_cfg_release(bp); + removed_already_existing = 1; + break; + } + } + } + if(!removed_already_existing) + { + RD_Cfg *project = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("project")); + RD_Cfg *bp = rd_cfg_new(project, str8_lit("breakpoint")); + RD_Cfg *loc = rd_cfg_new(bp, str8_lit("location")); + if(vaddr != 0) + { + rd_cfg_newf(loc, "0x%I64x", vaddr); + } + else if(string.size != 0) + { + rd_cfg_new(loc, string); + } + else if(file_path.size != 0) + { + RD_Cfg *file_path_cfg = rd_cfg_new(loc, file_path); + rd_cfg_newf(file_path_cfg, "%I64d", pt.line); + } + } + } B32 removed_already_existing = 0; if(kind == RD_CmdKind_ToggleBreakpoint) { @@ -15131,6 +15599,48 @@ rd_frame(void) TxtPt pt = rd_regs()->cursor; String8 string = rd_regs()->string; U64 vaddr = rd_regs()->vaddr; + //- TODO(rjf): @cfg add/toggle watch pin + { + B32 removed_already_existing = 0; + if(kind == RD_CmdKind_ToggleWatchPin) + { + RD_CfgList wps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("watch_pin")); + for(RD_CfgNode *n = wps.first; n != 0; n = n->next) + { + RD_Cfg *wp = n->v; + RD_Cfg *name = rd_cfg_child_from_string(wp, str8_lit("name")); + RD_Cfg *loc = rd_cfg_child_from_string(wp, str8_lit("location")); + S64 loc_line = 0; + U64 loc_vaddr = 0; + B32 loc_matches_file_pt = (file_path.size != 0 && path_match_normalized(loc->first->string, file_path) && try_s64_from_str8_c_rules(loc->first->first->string, &loc_line) && loc_line == pt.line); + B32 loc_matches_vaddr = (vaddr != 0 && try_u64_from_str8_c_rules(loc->first->string, &loc_vaddr) && loc_vaddr == vaddr); + B32 loc_matches_expr = (string.size != 0 && str8_match(name->first->string, string, 0)); + if(loc_matches_expr && (loc_matches_file_pt || loc_matches_vaddr)) + { + rd_cfg_release(wp); + removed_already_existing = 1; + break; + } + } + } + if(!removed_already_existing) + { + RD_Cfg *project = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("project")); + RD_Cfg *wp = rd_cfg_new(project, str8_lit("watch_pin")); + RD_Cfg *name = rd_cfg_new(wp, str8_lit("name")); + RD_Cfg *loc = rd_cfg_new(wp, str8_lit("location")); + rd_cfg_new(name, string); + if(vaddr != 0) + { + rd_cfg_newf(loc, "0x%I64x", vaddr); + } + else if(file_path.size != 0) + { + RD_Cfg *file_path_cfg = rd_cfg_new(loc, file_path); + rd_cfg_newf(file_path_cfg, "%I64d", pt.line); + } + } + } B32 removed_already_existing = 0; if(kind == RD_CmdKind_ToggleWatchPin) { @@ -15251,6 +15761,22 @@ rd_frame(void) //- rjf: targets case RD_CmdKind_AddTarget: { + //- TODO(rjf): @cfg add new target + { + String8 file_path = rd_regs()->file_path; + RD_Cfg *project = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("project")); + RD_Cfg *target = rd_cfg_new(project, str8_lit("target")); + RD_Cfg *exe = rd_cfg_new(target, str8_lit("executable")); + rd_cfg_new(exe, file_path); + String8 working_directory = str8_chop_last_slash(file_path); + if(working_directory.size != 0) + { + RD_Cfg *wdir = rd_cfg_new(target, str8_lit("working_directory")); + rd_cfg_newf(wdir, "%S/", working_directory); + } + // TODO(rjf): (select target here) + } + // rjf: build target RD_Entity *entity = &rd_nil_entity; entity = rd_entity_alloc(rd_entity_root(), RD_EntityKind_Target); diff --git a/src/raddbg/raddbg_core.h b/src/raddbg/raddbg_core.h index d625c3c78..196a63289 100644 --- a/src/raddbg/raddbg_core.h +++ b/src/raddbg/raddbg_core.h @@ -376,6 +376,44 @@ struct RD_CfgTable RD_CfgVal *last_val; }; +//////////////////////////////// +//~ rjf: New Config/Entity Data Structure + +typedef struct RD_Cfg RD_Cfg; +struct RD_Cfg +{ + RD_Cfg *first; + RD_Cfg *last; + RD_Cfg *next; + RD_Cfg *prev; + RD_Cfg *parent; + U64 gen; + String8 string; +}; + +typedef struct RD_CfgNode RD_CfgNode; +struct RD_CfgNode +{ + RD_CfgNode *next; + RD_Cfg *v; +}; + +typedef struct RD_CfgList RD_CfgList; +struct RD_CfgList +{ + RD_CfgNode *first; + RD_CfgNode *last; + U64 count; +}; + +typedef struct RD_CfgRec RD_CfgRec; +struct RD_CfgRec +{ + RD_Cfg *next; + S32 push_count; + S32 pop_count; +}; + //////////////////////////////// //~ rjf: Entity Types @@ -836,12 +874,16 @@ struct RD_State RD_RegSlot drag_drop_regs_slot; RD_DragDropState drag_drop_state; + // rjf: cfg state + RD_NameChunkNode *free_name_chunks[8]; + RD_Cfg *free_cfg; + RD_Cfg *root_cfg; + //- // TODO(rjf): TO BE ELIMINATED OR REPLACED VVVVVVVVVVVVVVVV //- // rjf: entity state - RD_NameChunkNode *free_name_chunks[8]; Arena *entities_arena; RD_Entity *entities_base; U64 entities_count; @@ -914,6 +956,15 @@ struct RD_State read_only global RD_CfgTree d_nil_cfg_tree = {&d_nil_cfg_tree, RD_CfgSrc_User, &md_nil_node}; read_only global RD_CfgVal d_nil_cfg_val = {&d_nil_cfg_val, &d_nil_cfg_val, &d_nil_cfg_tree, &d_nil_cfg_tree}; +read_only global RD_Cfg rd_nil_cfg = +{ + &rd_nil_cfg, + &rd_nil_cfg, + &rd_nil_cfg, + &rd_nil_cfg, + &rd_nil_cfg, +}; + read_only global RD_Entity rd_nil_entity = { &rd_nil_entity, @@ -1097,6 +1148,24 @@ internal U64 rd_name_bucket_idx_from_string_size(U64 size); internal String8 rd_name_alloc(String8 string); internal void rd_name_release(String8 string); +//////////////////////////////// +//~ rjf: New Config/Entity Data Structure Functions + +internal RD_Cfg *rd_cfg_alloc(void); +internal void rd_cfg_release(RD_Cfg *cfg); +internal RD_Cfg *rd_cfg_new(RD_Cfg *parent, String8 string); +internal RD_Cfg *rd_cfg_newf(RD_Cfg *parent, char *fmt, ...); +internal void rd_cfg_equip_string(RD_Cfg *cfg, String8 string); +internal void rd_cfg_insert_child(RD_Cfg *parent, RD_Cfg *prev_child, RD_Cfg *new_child); +internal void rd_cfg_unhook(RD_Cfg *parent, RD_Cfg *child); +internal RD_Cfg *rd_cfg_child_from_string(RD_Cfg *parent, String8 string); +internal RD_CfgList rd_cfg_child_list_from_string(Arena *arena, RD_Cfg *parent, String8 string); +internal RD_CfgList rd_cfg_top_level_list_from_string(Arena *arena, String8 string); +internal RD_CfgList rd_cfg_tree_list_from_string(Arena *arena, String8 string); +internal String8 rd_string_from_cfg_tree(Arena *arena, RD_Cfg *cfg); +internal RD_CfgRec rd_cfg_rec__depth_first(RD_Cfg *root, RD_Cfg *cfg); +internal void rd_cfg_list_push(Arena *arena, RD_CfgList *list, RD_Cfg *cfg); + //////////////////////////////// //~ rjf: Entity Stateful Functions diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index 985af73ff..f0f97e8aa 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -599,6 +599,63 @@ entry_point(CmdLine *cmd_line) String8List args = cmd_line->inputs; if(args.node_count > 0 && args.first->string.size != 0) { + //- TODO(rjf): @cfg setup initial target from command line arguments + { + Temp scratch = scratch_begin(0, 0); + + //- rjf: unpack command line inputs + String8 executable_name_string = {0}; + String8 arguments_string = {0}; + String8 working_directory_string = {0}; + { + // rjf: unpack full executable path + if(args.first->string.size != 0) + { + String8 current_path = os_get_current_path(scratch.arena); + String8 exe_name = args.first->string; + PathStyle style = path_style_from_str8(exe_name); + if(style == PathStyle_Relative) + { + exe_name = push_str8f(scratch.arena, "%S/%S", current_path, exe_name); + exe_name = path_normalized_from_string(scratch.arena, exe_name); + } + executable_name_string = exe_name; + } + + // rjf: unpack working directory + if(args.first->string.size != 0) + { + String8 path_part_of_arg = str8_chop_last_slash(args.first->string); + if(path_part_of_arg.size != 0) + { + String8 path = push_str8f(scratch.arena, "%S/", path_part_of_arg); + working_directory_string = path; + } + } + + // rjf: unpack arguments + String8List passthrough_args_list = {0}; + for(String8Node *n = args.first->next; n != 0; n = n->next) + { + str8_list_push(scratch.arena, &passthrough_args_list, n->string); + } + StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")}; + arguments_string = str8_list_join(scratch.arena, &passthrough_args_list, &join); + } + + //- rjf: build config tree + RD_Cfg *command_line_root = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("command_line")); + RD_Cfg *target = rd_cfg_new(command_line_root, str8_lit("target")); + RD_Cfg *exe = rd_cfg_new(target, str8_lit("executable")); + RD_Cfg *args = rd_cfg_new(target, str8_lit("arguments")); + RD_Cfg *wdir = rd_cfg_new(target, str8_lit("working_directory")); + rd_cfg_new(exe, executable_name_string); + rd_cfg_new(args, arguments_string); + rd_cfg_new(wdir, working_directory_string); + + scratch_end(scratch); + } + Temp scratch = scratch_begin(0, 0); RD_Entity *target = rd_entity_alloc(rd_entity_root(), RD_EntityKind_Target); rd_entity_equip_cfg_src(target, RD_CfgSrc_CommandLine);