Files
raddebugger/src/linker/lnk_cmd_line.c
T
2025-01-18 17:39:11 -08:00

275 lines
8.1 KiB
C

// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal String8List
lnk_arg_list_parse_windows_rules(Arena *arena, String8 string)
{
Temp scratch = scratch_begin(&arena, 1);
String8List list = {0};
U8 *ptr = string.str;
U8 *opl = string.str + string.size;
while (ptr < opl) {
// skip white space and new lines
for (;;) {
U64 size = (U64)(opl - ptr);
UnicodeDecode uni = utf8_decode(ptr, size);
if (uni.codepoint != ' ' && uni.codepoint != '\n' && uni.codepoint != '\r') {
break;
}
ptr += uni.inc;
}
if (*ptr == '\0') {
break;
}
String8List token_builder = {0};
U8 *anchor = ptr;
while (ptr < opl) {
UnicodeDecode uni;
uni = utf8_decode(ptr, (U64)(opl-ptr));
if (uni.codepoint == '\0' || uni.codepoint == '\n' || uni.codepoint == '\r' || uni.codepoint == ' ') {
break;
}
// handle string and strip quotes
if (uni.codepoint == '"') {
String8 text_before_quote = str8(anchor, (U64)(ptr - anchor));
str8_list_push(scratch.arena, &token_builder, text_before_quote);
// advance past starting quote
ptr += uni.inc;
anchor = ptr;
U8 *quote_end = ptr;
while (ptr < opl) {
uni = utf8_decode(ptr, (U64)(opl - ptr));
ptr += uni.inc;
// skip escape char
if (uni.codepoint == '\\') {
uni = utf8_decode(ptr, (U64)(opl - ptr));
ptr += uni.inc;
} else if (uni.codepoint == '"' || uni.codepoint == '\0') {
break; // found matching quote char
}
quote_end = ptr;
}
String8 text_inside_quotes = str8(anchor, (U64)(quote_end - anchor));
str8_list_push(scratch.arena, &token_builder, text_inside_quotes);
anchor = ptr;
} else {
ptr += uni.inc;
}
}
// push remaining text
String8 text = str8(anchor, (U64)(ptr - anchor));
str8_list_push(scratch.arena, &token_builder, text);
// push token
String8 token = str8_list_join(arena, &token_builder, NULL);
if (token.size) {
str8_list_push(arena, &list, token);
}
}
scratch_end(scratch);
return list;
}
internal void
lnk_cmd_line_push_option_node(LNK_CmdLine *cmd_line, LNK_CmdOption *opt)
{
SLLQueuePush(cmd_line->first_option, cmd_line->last_option, opt);
cmd_line->option_count += 1;
}
internal LNK_CmdOption *
lnk_cmd_line_push_option_list(Arena *arena, LNK_CmdLine *cmd_line, String8 string, String8List value_strings)
{
// fill out node
LNK_CmdOption *opt = push_array_no_zero(arena, LNK_CmdOption, 1);
opt->next = 0;
opt->string = string;
opt->value_strings = value_strings;
// push node
lnk_cmd_line_push_option_node(cmd_line, opt);
return opt;
}
internal LNK_CmdOption *
lnk_cmd_line_push_option_string(Arena *arena, LNK_CmdLine *cmd_line, String8 string, String8 value)
{
String8List value_list = str8_split_by_string_chars(arena, value, str8_lit(","), StringSplitFlag_KeepEmpties);
LNK_CmdOption *opt = lnk_cmd_line_push_option_list(arena, cmd_line, string, value_list);
return opt;
}
internal LNK_CmdOption *
lnk_cmd_line_push_option(Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value)
{
return lnk_cmd_line_push_option_string(arena, cmd_line, str8_cstring(string), str8_cstring(value));
}
internal LNK_CmdOption *
lnk_cmd_line_push_option_if_not_present(Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value)
{
if (!lnk_cmd_line_has_option(*cmd_line, string)) {
return lnk_cmd_line_push_option(arena, cmd_line, string, value);
}
return 0;
}
internal LNK_CmdLine
lnk_cmd_line_parse_windows_rules(Arena *arena, String8List arg_list)
{
Temp scratch = scratch_begin(&arena, 1);
LNK_CmdLine cmd_line = {0};
for (String8Node *arg_node = arg_list.first; arg_node != 0; arg_node = arg_node->next) {
String8 arg = arg_node->string;
B32 is_option = str8_match_lit("/", arg, StringMatchFlag_RightSideSloppy) ||
str8_match_lit("-", arg, StringMatchFlag_RightSideSloppy);
if (is_option) {
U64 param_start_pos = str8_find_needle(arg, 0, str8_lit(":"), 0);
String8 option_name = str8_chop(arg, arg.size - param_start_pos);
// remove '/' or '-' from option name
option_name = str8_skip(option_name, 1);
// skip ':'
String8 value_string = str8_skip(arg, param_start_pos + 1);
// make value list
String8List value_list = str8_split_by_string_chars(arena, value_string, str8_lit(","), 0);
// push command
lnk_cmd_line_push_option_list(arena, &cmd_line, option_name, value_list);
} else {
str8_list_push(arena, &cmd_line.input_list, arg);
}
}
scratch_end(scratch);
return cmd_line;
}
internal LNK_CmdOption *
lnk_cmd_line_option_from_string(LNK_CmdLine cmd_line, String8 string)
{
LNK_CmdOption *opt;
for (opt = cmd_line.first_option; opt != NULL; opt = opt->next) {
if (str8_match(string, opt->string, StringMatchFlag_CaseInsensitive)) {
break;
}
}
return opt;
}
internal B32
lnk_cmd_line_has_option_string(LNK_CmdLine cmd_line, String8 string)
{
LNK_CmdOption *opt = lnk_cmd_line_option_from_string(cmd_line, string);
B32 has_option = (opt != 0);
return has_option;
}
internal B32
lnk_cmd_line_has_option(LNK_CmdLine cmd_line, char *string)
{
return lnk_cmd_line_has_option_string(cmd_line, str8_cstring(string));
}
internal String8List
lnk_unwrap_rsp(Arena *arena, String8List arg_list)
{
Temp scratch = scratch_begin(&arena, 1);
String8List result = {0};
for (String8Node *curr = arg_list.first; curr != 0; curr = curr->next) {
B32 is_rsp = str8_match_lit("@", curr->string, StringMatchFlag_RightSideSloppy);
if (is_rsp) {
// remove "@"
String8 name = str8_skip(curr->string, 1);
if (os_file_path_exists(name)) {
// read rsp from disk
String8 file = lnk_read_data_from_file_path(scratch.arena, name);
// parse rsp
String8List rsp_args = lnk_arg_list_parse_windows_rules(scratch.arena, file);
// handle case where rsp references another rsp
String8List list = lnk_unwrap_rsp(arena, rsp_args);
// push arguments from rsp
list = str8_list_copy(arena, &list);
str8_list_concat_in_place(&result, &list);
} else {
lnk_error(LNK_Error_Cmdl, "unable to find rsp: %S", name);
}
} else {
// push regular argument
String8 str = push_str8_copy(arena, curr->string);
str8_list_push(arena, &result, str);
}
}
scratch_end(scratch);
return result;
}
internal String8List
lnk_data_from_cmd_line(Arena *arena, LNK_CmdLine cmd_line)
{
String8List result = {0};
for (LNK_CmdOption *opt = cmd_line.first_option; opt != 0; opt = opt->next) {
// separate directives
if (opt != cmd_line.first_option) {
str8_list_pushf(arena, &result, " ");
}
// push new directive
str8_list_pushf(arena, &result, "/%.*s", str8_varg(opt->string));
// do we have arguments?
if (opt->value_strings.node_count > 0) {
str8_list_pushf(arena, &result, ":");
for (String8Node *value_node = opt->value_strings.first; value_node != 0; value_node = value_node->next) {
// separate arguments
if (value_node != opt->value_strings.first) {
str8_list_pushf(arena, &result, ",");
}
// push argument
B32 has_spaces = str8_find_needle(value_node->string, 0, str8_lit(" "), StringMatchFlag_CaseInsensitive) < value_node->string.size;
if (has_spaces) {
str8_list_pushf(arena, &result, "\"%.*s\"", str8_varg(value_node->string));
} else {
str8_list_pushf(arena, &result, "%.*s", str8_varg(value_node->string));
}
}
}
}
// append inputs
for (String8Node *input_node = cmd_line.input_list.first; input_node != 0; input_node = input_node->next) {
if (input_node != cmd_line.input_list.first) {
str8_list_pushf(arena, &result, " ");
}
str8_list_pushf(arena, &result, "\"%.*s\"", str8_varg(input_node->string));
}
return result;
}