pass over base layer command line parsing. do not assume debugger-style 'treat all after first non-option input as passthrough options'. most things do not want that - the debugger can still get it by doing a quick secondary parse

This commit is contained in:
Ryan Fleury
2025-06-10 09:46:46 -07:00
parent 49de09883e
commit 7205b7d1ab
8 changed files with 96 additions and 81 deletions
+37 -59
View File
@@ -2,18 +2,7 @@
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ NOTE(rjf): Command Line Option Parsing
internal U64
cmd_line_hash_from_string(String8 string)
{
U64 result = 5381;
for(U64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + string.str[i];
}
return result;
}
//~ rjf: Command Line Parsing Functions
internal CmdLineOpt **
cmd_line_slot_from_string(CmdLine *cmd_line, String8 string)
@@ -21,7 +10,7 @@ cmd_line_slot_from_string(CmdLine *cmd_line, String8 string)
CmdLineOpt **slot = 0;
if(cmd_line->option_table_size != 0)
{
U64 hash = cmd_line_hash_from_string(string);
U64 hash = u64_hash_from_str8(string);
U64 bucket = hash % cmd_line->option_table_size;
slot = &cmd_line->option_table[bucket];
}
@@ -64,7 +53,7 @@ cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List
{
var = push_array(arena, CmdLineOpt, 1);
var->hash_next = *slot;
var->hash = cmd_line_hash_from_string(string);
var->hash = u64_hash_from_str8(string);
var->string = push_str8_copy(arena, string);
var->value_strings = values;
StringJoin join = {0};
@@ -83,28 +72,25 @@ cmd_line_from_string_list(Arena *arena, String8List command_line)
{
CmdLine parsed = {0};
parsed.exe_name = command_line.first->string;
parsed.option_table_size = 64;
parsed.option_table = push_array(arena, CmdLineOpt *, parsed.option_table_size);
// NOTE(rjf): Set up config option table.
{
parsed.option_table_size = 4096;
parsed.option_table = push_array(arena, CmdLineOpt *, parsed.option_table_size);
}
// NOTE(rjf): Parse command line.
//- rjf: parse options / inputs
B32 after_passthrough_option = 0;
B32 first_passthrough = 1;
for(String8Node *node = command_line.first->next, *next = 0; node != 0; node = next)
{
next = node->next;
String8 option_name = node->string;
// NOTE(rjf): Look at --, -, or / (only on Windows) at the start of an
// argument to determine if it's a flag option. All arguments after a
//- rjf: look at --, -, or / (only on Windows) at the start of an
// argument to determine if it's a flag option. all arguments after a
// single "--" (with no trailing string on the command line will be
// considered as input files.
B32 is_option = 1;
if(after_passthrough_option == 0)
// considered as passthrough input strings.
B32 is_option = 0;
String8 option_name = node->string;
if(!after_passthrough_option)
{
is_option = 1;
if(str8_match(node->string, str8_lit("--"), 0))
{
after_passthrough_option = 1;
@@ -128,69 +114,61 @@ cmd_line_from_string_list(Arena *arena, String8List command_line)
is_option = 0;
}
}
else
{
is_option = 0;
}
// NOTE(rjf): This string is an option.
//- rjf: string is an option
if(is_option)
{
B32 has_arguments = 0;
U64 arg_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0);
U64 arg_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0);
U64 arg_signifier_position = Min(arg_signifier_position1, arg_signifier_position2);
String8 arg_portion_this_string = str8_skip(option_name, arg_signifier_position+1);
if(arg_signifier_position < option_name.size)
// rjf: unpack option prefix
B32 has_values = 0;
U64 value_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0);
U64 value_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0);
U64 value_signifier_position = Min(value_signifier_position1, value_signifier_position2);
String8 value_portion_this_string = str8_skip(option_name, value_signifier_position+1);
if(value_signifier_position < option_name.size)
{
has_arguments = 1;
has_values = 1;
}
option_name = str8_prefix(option_name, arg_signifier_position);
option_name = str8_prefix(option_name, value_signifier_position);
String8List arguments = {0};
// NOTE(rjf): Parse arguments.
if(has_arguments)
// rjf: parse option's values
String8List values = {0};
if(has_values)
{
for(String8Node *n = node; n; n = n->next)
{
next = n->next;
String8 string = n->string;
if(n == node)
{
string = arg_portion_this_string;
string = value_portion_this_string;
}
U8 splits[] = { ',' };
String8List args_in_this_string = str8_split(arena, string, splits, ArrayCount(splits), 0);
for(String8Node *sub_arg = args_in_this_string.first; sub_arg; sub_arg = sub_arg->next)
String8List values_in_this_string = str8_split(arena, string, splits, ArrayCount(splits), 0);
for(String8Node *sub_val = values_in_this_string.first; sub_val; sub_val = sub_val->next)
{
str8_list_push(arena, &arguments, sub_arg->string);
str8_list_push(arena, &values, sub_val->string);
}
if(!str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) &&
(n != node || arg_portion_this_string.size != 0))
(n != node || value_portion_this_string.size != 0))
{
break;
}
}
}
// NOTE(rjf): Register config variable.
cmd_line_insert_opt(arena, &parsed, option_name, arguments);
// rjf: store
cmd_line_insert_opt(arena, &parsed, option_name, values);
}
// NOTE(rjf): Default path, treat as a passthrough config option to be
// handled by tool-specific code.
//- rjf: default path - treat as a passthrough input
else if(!str8_match(node->string, str8_lit("--"), 0) || !first_passthrough)
{
str8_list_push(arena, &parsed.inputs, node->string);
after_passthrough_option = 1;
first_passthrough = 0;
}
}
// rjf: fill argc/argv
//- rjf: fill argc/argv
parsed.argc = command_line.node_count;
parsed.argv = push_array(arena, char *, parsed.argc);
{
@@ -239,12 +217,12 @@ internal B32
cmd_line_has_flag(CmdLine *cmd_line, String8 name)
{
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
return(var != 0);
return (var != 0);
}
internal B32
cmd_line_has_argument(CmdLine *cmd_line, String8 name)
{
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
return(var != 0 && var->value_strings.node_count > 0);
return (var != 0 && var->value_strings.node_count > 0);
}