//////////////////////////////// // Functions: Command Line Parsing MR4TH_SYMBOL MR4TH_READ_ONLY CMDLN_Params cmdln__params_nil = {0}; #define cmdln_params_nil (CMDLN_Params*)(&cmdln__params_nil) MR4TH_SYMBOL CMDLN* cmdln_from_args(Arena *arena, String8List *args){ CMDLN *cmdln = push_array(arena, CMDLN, 1); cmdln->raw = str8_list_copy(arena, args); String8Node *node = cmdln->raw.first; // first string is 'program' if (node != 0){ cmdln->program = node->string; node = node->next; } // consume string nodes B32 forced_input = 0; for (;node != 0;){ String8 string = str8_skip_chop_whitespace(node->string); node = node->next; // check if argument is a flag B32 is_flag = 0; if (!forced_input){ is_flag = (string.size != 0 && string.str[0] == '-'); } // parse flag if (is_flag){ // long flag B32 is_long_flag = (string.size > 1 && string.str[1] == '-'); if (is_long_flag){ // end 'normal' mode B32 double_dash = (string.size == 2); if (double_dash){ forced_input = 1; } // parse long flag if (!double_dash){ String8 flag_whole = str8_skip(string, 2); // parameter delimter U64 delim = flag_whole.size; for (U8 *ptr = flag_whole.str, *opl = flag_whole.str + flag_whole.size; ptr < opl; ptr += 1){ if (*ptr == '=' || *ptr == ':'){ delim = (U64)(ptr - flag_whole.str); break; } } // split flag at delimiter String8 flag_name = str8_prefix(flag_whole, delim); String8 flag_param = str8_skip(flag_whole, delim + 1); // if have a param delimiter at end // then use the next argument as the flag_param if (delim == flag_whole.size - 1){ if (node != 0){ flag_param = node->string; node = node->next; } } // parse parameters CMDLN_Params *params = cmdln_params_from_string(arena, flag_param); // store flag node { CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1); SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode); cmdlnnode->string = flag_name; cmdlnnode->params = params; cmdln->flag_count += 1; } } } // short flags if (!is_long_flag){ String8 short_flags = str8_skip(string, 1); U8 *flag = short_flags.str; U8 *flagopl = short_flags.str + short_flags.size; for (; flag < flagopl; flag += 1){ U8 *flagptr = flag; // check for parameters String8 flag_param = {0}; if (flag + 1 < flagopl && (flag[1] == '=' || flag[1] == ':')){ flag_param = str8_range(flag + 2, flagopl); if (flag_param.size == 0){ if (node != 0){ flag_param = node->string; node = node->next; } } // kill the flag loop after finding parameters flag = flagopl; } // parse parameters CMDLN_Params *params = cmdln_params_from_string(arena, flag_param); // store flag node { CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1); SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode); cmdlnnode->string = CLiteral(String8){flagptr, 1}; cmdlnnode->params = params; cmdln->flag_count += 1; } } } } // parse input if (!is_flag){ String8 input_string = string; // store input node { CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1); SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode); cmdlnnode->string = input_string; cmdln->input_count += 1; } } } // pointer arrays cmdln->inputs = push_array(arena, CMDLN_Node*, cmdln->input_count); cmdln->flags = push_array(arena, CMDLN_Node*, cmdln->flag_count); { CMDLN_Node **inputptr = cmdln->inputs; CMDLN_Node **flagptr = cmdln->flags; for (CMDLN_Node *node = cmdln->first; node != 0; node = node->next){ if (node->params == 0){ *inputptr = node; inputptr += 1; } else{ *flagptr = node; flagptr += 1; } } } return(cmdln); } MR4TH_SYMBOL CMDLN_Params* cmdln_params_from_string(Arena *arena, String8 flag_param){ CMDLN_Params *params = cmdln_params_nil; if (flag_param.size > 0){ params = push_array(arena, CMDLN_Params, 1); params->raw = flag_param; params->list = str8_split(arena, flag_param, (U8*)",", 1); } return(params); } MR4TH_SYMBOL U64 cmdln_input_count(CMDLN *cmdln){ return(cmdln->input_count); } MR4TH_SYMBOL String8 cmdln_input_from_idx(CMDLN *cmdln, U64 idx){ String8 result = {0}; if (idx < cmdln->input_count){ result = cmdln->inputs[idx]->string; } return(result); } MR4TH_SYMBOL U64 cmdln_flag_count(CMDLN *cmdln){ return(cmdln->flag_count); } MR4TH_SYMBOL CMDLN_Flag* cmdln_flag_from_idx(CMDLN *cmdln, U64 idx){ CMDLN_Flag *result = 0; if (idx < cmdln->flag_count){ result = cmdln->flags[idx]; } return(result); } MR4TH_SYMBOL CMDLN_Params* cmdln_get_params(CMDLN *cmdln, String8 flagstr, char abbrev){ CMDLN_Params *result = 0; for (CMDLN_Node *node = cmdln->first; node != 0; node = node->next){ if (node->params != 0){ if (str8_match(flagstr, node->string, 0) || (node->string.size == 1 && node->string.str[0] == (U8)abbrev)){ result = node->params; break; } } } return(result); } MR4TH_SYMBOL B32 cmdln_has_flag(CMDLN *cmdln, String8 flagstr, char abbrev){ CMDLN_Params *params = cmdln_get_params(cmdln, flagstr, abbrev); B32 result = (params != 0); return(result); } MR4TH_SYMBOL String8 cmdln_get_str8(CMDLN *cmdln, String8 flagstr, char abbrev){ CMDLN_Params *params = cmdln_get_params(cmdln, flagstr, abbrev); String8 result = {0}; if (params != 0){ result = params->raw; } return(result); } MR4TH_SYMBOL S64 cmdln_get_s64(CMDLN *cmdln, String8 flagstr, char abbrev){ String8 str = cmdln_get_str8(cmdln, flagstr, abbrev); S64 result = cmdln_s64_from_str8(str); return(result); } MR4TH_SYMBOL F64 cmdln_get_f64(CMDLN *cmdln, String8 flagstr, char abbrev){ String8 str = cmdln_get_str8(cmdln, flagstr, abbrev); F64 result = cmdln_f64_from_str8(str); return(result); } MR4TH_SYMBOL S64 cmdln_s64_from_str8(String8 valstr){ S64 result = s64_from_str8_c_syntax(valstr); return(result); } MR4TH_SYMBOL F64 cmdln_f64_from_str8(String8 valstr){ F64 result = f64_from_str8(valstr); return(result); } MR4TH_SYMBOL void cmdln_dump(Arena *arena, String8List *out, CMDLN *cmdln, U32 indent){ // raw str8_list_pushf(arena, out, "%Nraw:\n", indent); for (String8Node *node = cmdln->raw.first; node != 0; node = node->next){ str8_list_pushf(arena, out, "%N%S\n", indent + 1, node->string); } // program str8_list_pushf(arena, out, "%Nprogram: %S\n", indent, cmdln->program); // input nodes str8_list_pushf(arena, out, "%Nnodes:\n", indent); for (CMDLN_Node *cmdlnnode = cmdln->first; cmdlnnode != 0; cmdlnnode = cmdlnnode->next){ if (cmdlnnode->params == 0){ str8_list_pushf(arena, out, "%N[input] %S\n", indent + 1, cmdlnnode->string); } else{ str8_list_pushf(arena, out, "%N[flag ] %S\n", indent + 1, cmdlnnode->string); for (String8Node *node = cmdlnnode->params->list.first; node != 0; node = node->next){ str8_list_pushf(arena, out, "%N[param] %S\n", indent + 2, node->string); } } } }