diff --git a/CMakeLists.txt b/CMakeLists.txt index 758825b..35571b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,17 @@ target_link_libraries(amduat_pel_run ) set_target_properties(amduat_pel_run PROPERTIES OUTPUT_NAME amduat-pel-run) +add_executable(amduat_pel_cli src/tools/amduat_pel_cli.c) +target_include_directories(amduat_pel_cli + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_pel_cli + PRIVATE amduat_pel amduat_asl_store_fs amduat_asl amduat_enc + amduat_hash_asl1 amduat_util +) +set_target_properties(amduat_pel_cli PROPERTIES OUTPUT_NAME amduat-pel) + enable_testing() add_executable(amduat_test_pel_program_dag tests/enc/test_pel_program_dag.c) diff --git a/src/tools/amduat_pel_cli.c b/src/tools/amduat_pel_cli.c new file mode 100644 index 0000000..870fde4 --- /dev/null +++ b/src/tools/amduat_pel_cli.c @@ -0,0 +1,3633 @@ +#include "amduat/asl/asl_store_fs.h" +#include "amduat/asl/asl_store_fs_meta.h" +#include "amduat/asl/io.h" +#include "amduat/asl/parse.h" +#include "amduat/asl/ref_text.h" +#include "amduat/asl/store.h" +#include "amduat/enc/asl1_core_codec.h" +#include "amduat/enc/pel1_result.h" +#include "amduat/enc/pel_program_dag.h" +#include "amduat/enc/pel_trace_dag.h" +#include "amduat/hash/asl1.h" +#include "amduat/pel/opreg_kernel.h" +#include "amduat/pel/opreg_kernel_params.h" +#include "amduat/pel/program_dag_desc.h" +#include "amduat/pel/surf.h" +#include "amduat/util/hex.h" + +#include +#include +#include +#include +#include +#include +#include + +enum { + AMDUAT_PEL_CLI_EXIT_OK = 0, + AMDUAT_PEL_CLI_EXIT_USAGE = 2, + AMDUAT_PEL_CLI_EXIT_IO = 3, + AMDUAT_PEL_CLI_EXIT_NOT_FOUND = 4, + AMDUAT_PEL_CLI_EXIT_STORE = 5, + AMDUAT_PEL_CLI_EXIT_UNSUPPORTED = 6, + AMDUAT_PEL_CLI_EXIT_CODEC = 7, + AMDUAT_PEL_CLI_EXIT_CONFIG = 8 +}; + +static const char *const AMDUAT_PEL_CLI_DEFAULT_ROOT = ".amduat-asl"; + +typedef enum { + AMDUAT_PEL_CLI_REF_HEX = 0, + AMDUAT_PEL_CLI_REF_BYTES = 1 +} amduat_pel_cli_ref_format_t; + +typedef enum { + AMDUAT_PEL_CLI_FORMAT_TEXT = 0, + AMDUAT_PEL_CLI_FORMAT_JSON = 1 +} amduat_pel_cli_output_format_t; + +typedef enum { + AMDUAT_PEL_CLI_IO_RAW = 0, + AMDUAT_PEL_CLI_IO_ARTIFACT = 1 +} amduat_pel_cli_io_format_t; + +typedef struct { + const char *root; + amduat_pel_cli_ref_format_t ref_format; + amduat_pel_cli_output_format_t format; + bool quiet; +} amduat_pel_cli_global_opts_t; + +static void amduat_pel_cli_print_usage(FILE *stream) { + fprintf(stream, + "usage:\n" + " amduat-pel [--root PATH] [--ref-format hex|bytes] [--format text|json]\n" + " [--quiet] [args]\n" + "\n" + "commands:\n" + " run Execute a PEL/PROGRAM-DAG/1 program via PEL/1-SURF (store-backed).\n" + " exec Execute a PEL/PROGRAM-DAG/1 program in-memory (no store writes).\n" + " validate Validate a Program artifact (decode + DAG checks + params).\n" + " program Program DAG tools (decode, normalize).\n" + " trace Trace DAG tools (decode, from-result).\n" + " result Surface result tools (decode).\n" + " op Kernel op registry tools.\n" + " scheme Scheme refs, type tags, profile IDs.\n" + " help Show help for a command.\n" + "\n" + "global options:\n" + " --root PATH ASL store root (default .amduat-asl)\n" + " --ref-format hex|bytes Reference I/O format (default hex)\n" + " --format text|json Structured output format (default text)\n" + " --quiet Only print machine output\n" + "\n" + "run:\n" + " amduat-pel run --program-ref REF [--scheme-ref REF|dag]\n" + " [--input-ref REF ...] [--inputs-file PATH]\n" + " [--params-ref REF]\n" + " [--format text|json] [--ref-format hex|bytes]\n" + " Output: result_ref, trace_ref, output_refs (formatted per --format).\n" + "\n" + "exec:\n" + " amduat-pel exec --program PATH|-\n" + " [--input PATH ...] [--input-format raw|artifact]\n" + " [--output-dir DIR] [--output-format raw|artifact]\n" + " [--result-out PATH|-] [--format text|json]\n" + "\n" + "validate:\n" + " amduat-pel validate [--input PATH|- | --program-ref REF]\n" + " [--input-format raw|artifact]\n" + " [--expect-type-tag TAG]\n" + " [--format text|json]\n" + "\n" + "program:\n" + " amduat-pel program decode [--input PATH|- | --program-ref REF]\n" + " [--input-format raw|artifact]\n" + " [--expect-type-tag TAG]\n" + " [--format text|json]\n" + " amduat-pel program normalize --input PATH|- [--input-format raw|artifact]\n" + " [--output PATH|-] [--output-format raw|artifact]\n" + " [--type-tag TAG]\n" + "\n" + "trace:\n" + " amduat-pel trace decode [--input PATH|- | --trace-ref REF]\n" + " [--input-format raw|artifact]\n" + " [--expect-type-tag TAG]\n" + " [--format text|json]\n" + " amduat-pel trace from-result --result-ref REF [--root PATH]\n" + " [--format text|json]\n" + "\n" + "result:\n" + " amduat-pel result decode [--input PATH|- | --result-ref REF]\n" + " [--input-format raw|artifact]\n" + " [--expect-type-tag TAG]\n" + " [--format text|json]\n" + "\n" + "op:\n" + " amduat-pel op list [--format text|json]\n" + " amduat-pel op describe NAME[=VERSION] [--format text|json]\n" + " amduat-pel op params-decode --op NAME[=VERSION]\n" + " --input PATH|- [--format text|json]\n" + "\n" + "scheme:\n" + " amduat-pel scheme show [--format text|json]\n" + " amduat-pel scheme dag-ref [--format hex|bytes]\n" + "\n" + "defaults:\n" + " --root .amduat-asl\n" + " --ref-format hex\n" + " --input -\n" + " --output -\n" + " --input-format raw\n" + " --output-format raw\n" + " --scheme-ref dag\n" + "\n" + "notes:\n" + " - Only PEL/PROGRAM-DAG/1 is supported; other scheme refs yield SCHEME_UNSUPPORTED.\n" + " - Expected type tags: program 0x00000101, trace 0x00000102, result 0x00000103.\n" + " - Encoding profile IDs: program 0x0101, trace 0x0102, result 0x0103.\n" + " - Kernel ops: pel.bytes.concat, pel.bytes.slice, pel.bytes.const, pel.bytes.hash.asl1.\n" + " - --ref-format bytes expects --ref to be a path or '-' (raw reference bytes).\n"); +} + +static bool amduat_pel_cli_parse_ref_format( + const char *text, + amduat_pel_cli_ref_format_t *out_fmt) { + if (text == NULL || out_fmt == NULL) { + return false; + } + if (strcmp(text, "hex") == 0) { + *out_fmt = AMDUAT_PEL_CLI_REF_HEX; + return true; + } + if (strcmp(text, "bytes") == 0) { + *out_fmt = AMDUAT_PEL_CLI_REF_BYTES; + return true; + } + return false; +} + +static bool amduat_pel_cli_parse_output_format( + const char *text, + amduat_pel_cli_output_format_t *out_fmt) { + if (text == NULL || out_fmt == NULL) { + return false; + } + if (strcmp(text, "text") == 0) { + *out_fmt = AMDUAT_PEL_CLI_FORMAT_TEXT; + return true; + } + if (strcmp(text, "json") == 0) { + *out_fmt = AMDUAT_PEL_CLI_FORMAT_JSON; + return true; + } + return false; +} + +static bool amduat_pel_cli_parse_io_format( + const char *text, + amduat_pel_cli_io_format_t *out_fmt) { + if (text == NULL || out_fmt == NULL) { + return false; + } + if (strcmp(text, "raw") == 0) { + *out_fmt = AMDUAT_PEL_CLI_IO_RAW; + return true; + } + if (strcmp(text, "artifact") == 0) { + *out_fmt = AMDUAT_PEL_CLI_IO_ARTIFACT; + return true; + } + return false; +} + +static const char *amduat_pel_cli_status_name( + amduat_pel_execution_status_t status) { + switch (status) { + case AMDUAT_PEL_EXEC_STATUS_OK: + return "OK"; + case AMDUAT_PEL_EXEC_STATUS_SCHEME_UNSUPPORTED: + return "SCHEME_UNSUPPORTED"; + case AMDUAT_PEL_EXEC_STATUS_INVALID_PROGRAM: + return "INVALID_PROGRAM"; + case AMDUAT_PEL_EXEC_STATUS_INVALID_INPUTS: + return "INVALID_INPUTS"; + case AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED: + return "RUNTIME_FAILED"; + default: + return "UNKNOWN"; + } +} + +static const char *amduat_pel_cli_error_kind_name( + amduat_pel_execution_error_kind_t kind) { + switch (kind) { + case AMDUAT_PEL_EXEC_ERROR_NONE: + return "NONE"; + case AMDUAT_PEL_EXEC_ERROR_SCHEME: + return "SCHEME"; + case AMDUAT_PEL_EXEC_ERROR_PROGRAM: + return "PROGRAM"; + case AMDUAT_PEL_EXEC_ERROR_INPUTS: + return "INPUTS"; + case AMDUAT_PEL_EXEC_ERROR_RUNTIME: + return "RUNTIME"; + default: + return "UNKNOWN"; + } +} + +static const char *amduat_pel_cli_node_status_name( + amduat_pel_node_trace_status_t status) { + switch (status) { + case AMDUAT_PEL_NODE_TRACE_OK: + return "NODE_OK"; + case AMDUAT_PEL_NODE_TRACE_FAILED: + return "NODE_FAILED"; + case AMDUAT_PEL_NODE_TRACE_SKIPPED: + return "NODE_SKIPPED"; + default: + return "NODE_UNKNOWN"; + } +} + +static const char *amduat_pel_cli_store_phase_name( + amduat_pel_store_failure_phase_t phase) { + switch (phase) { + case AMDUAT_PEL_STORE_FAILURE_PROGRAM: + return "PROGRAM"; + case AMDUAT_PEL_STORE_FAILURE_INPUT: + return "INPUT"; + default: + return "UNKNOWN"; + } +} + +static const char *amduat_pel_cli_store_error_name( + amduat_pel_store_error_code_t code) { + switch (code) { + case AMDUAT_PEL_STORE_ERROR_NOT_FOUND: + return "NOT_FOUND"; + case AMDUAT_PEL_STORE_ERROR_INTEGRITY: + return "INTEGRITY"; + case AMDUAT_PEL_STORE_ERROR_UNSUPPORTED: + return "UNSUPPORTED"; + default: + return "UNKNOWN"; + } +} + +static const char *amduat_pel_cli_store_error_str( + amduat_asl_store_error_t err) { + switch (err) { + case AMDUAT_ASL_STORE_ERR_NOT_FOUND: + return "not found"; + case AMDUAT_ASL_STORE_ERR_UNSUPPORTED: + return "unsupported"; + case AMDUAT_ASL_STORE_ERR_INTEGRITY: + return "integrity"; + case AMDUAT_ASL_STORE_OK: + return "ok"; + default: + return "unknown"; + } +} + +static int amduat_pel_cli_map_store_error(amduat_asl_store_error_t err) { + switch (err) { + case AMDUAT_ASL_STORE_ERR_NOT_FOUND: + return AMDUAT_PEL_CLI_EXIT_NOT_FOUND; + case AMDUAT_ASL_STORE_ERR_UNSUPPORTED: + return AMDUAT_PEL_CLI_EXIT_UNSUPPORTED; + case AMDUAT_ASL_STORE_ERR_INTEGRITY: + return AMDUAT_PEL_CLI_EXIT_STORE; + case AMDUAT_ASL_STORE_OK: + default: + return AMDUAT_PEL_CLI_EXIT_STORE; + } +} + +static void amduat_pel_cli_free_reference(amduat_reference_t *ref) { + if (ref == NULL) { + return; + } + free((void *)ref->digest.data); + ref->digest.data = NULL; + ref->digest.len = 0u; +} + +static void amduat_pel_cli_free_artifact(amduat_artifact_t *artifact) { + if (artifact == NULL) { + return; + } + free((void *)artifact->bytes.data); + artifact->bytes.data = NULL; + artifact->bytes.len = 0u; +} + +static void amduat_pel_cli_free_refs(amduat_reference_t *refs, size_t refs_len) { + size_t i; + + if (refs == NULL) { + return; + } + for (i = 0; i < refs_len; ++i) { + amduat_pel_cli_free_reference(&refs[i]); + } + free(refs); +} + +static bool amduat_pel_cli_read_path_once(const char *path, + bool *stdin_used, + uint8_t **out_bytes, + size_t *out_len) { + if (path == NULL || out_bytes == NULL || out_len == NULL) { + return false; + } + if (strcmp(path, "-") == 0 && stdin_used != NULL) { + if (*stdin_used) { + return false; + } + *stdin_used = true; + } + return amduat_asl_read_path(path, out_bytes, out_len); +} + +static char *amduat_pel_cli_hex_encode(amduat_octets_t bytes) { + size_t out_len; + char *out; + + out_len = amduat_hex_encoded_size(bytes.len); + out = (char *)malloc(out_len); + if (out == NULL) { + return NULL; + } + if (!amduat_hex_encode_lower(bytes.data, bytes.len, out, out_len)) { + free(out); + return NULL; + } + return out; +} + +static bool amduat_pel_cli_ref_to_text(amduat_reference_t ref, + amduat_pel_cli_ref_format_t fmt, + char **out_text) { + if (out_text == NULL) { + return false; + } + *out_text = NULL; + + if (fmt == AMDUAT_PEL_CLI_REF_HEX) { + return amduat_asl_ref_encode_hex(ref, out_text); + } + + { + amduat_octets_t encoded; + char *hex; + if (!amduat_enc_asl1_core_encode_reference_v1(ref, &encoded)) { + return false; + } + hex = amduat_pel_cli_hex_encode(encoded); + free((void *)encoded.data); + if (hex == NULL) { + return false; + } + *out_text = hex; + return true; + } +} + +static bool amduat_pel_cli_ref_from_text(const char *text, + amduat_pel_cli_ref_format_t fmt, + bool *stdin_used, + amduat_reference_t *out_ref) { + if (text == NULL || out_ref == NULL) { + return false; + } + + memset(out_ref, 0, sizeof(*out_ref)); + if (fmt == AMDUAT_PEL_CLI_REF_HEX) { + return amduat_asl_ref_decode_hex(text, out_ref); + } + + { + uint8_t *bytes = NULL; + size_t len = 0; + if (!amduat_pel_cli_read_path_once(text, stdin_used, &bytes, &len)) { + return false; + } + if (!amduat_asl_ref_decode_bytes(bytes, len, out_ref)) { + free(bytes); + return false; + } + free(bytes); + } + return true; +} + +static bool amduat_pel_cli_append_ref(amduat_reference_t **refs, + size_t *refs_len, + amduat_reference_t ref) { + amduat_reference_t *next; + + if (refs == NULL || refs_len == NULL) { + return false; + } + next = (amduat_reference_t *)realloc( + *refs, (*refs_len + 1u) * sizeof(**refs)); + if (next == NULL) { + return false; + } + next[*refs_len] = ref; + *refs = next; + *refs_len += 1u; + return true; +} + +static bool amduat_pel_cli_append_refs_from_file( + const char *path, + amduat_pel_cli_ref_format_t fmt, + bool *stdin_used, + amduat_reference_t **refs, + size_t *refs_len) { + uint8_t *bytes = NULL; + size_t len = 0; + size_t i = 0; + + if (!amduat_pel_cli_read_path_once(path, stdin_used, &bytes, &len)) { + return false; + } + + while (i < len) { + size_t start; + size_t token_len; + char *token; + amduat_reference_t ref; + + while (i < len && isspace((unsigned char)bytes[i])) { + ++i; + } + if (i >= len) { + break; + } + start = i; + while (i < len && !isspace((unsigned char)bytes[i])) { + ++i; + } + token_len = i - start; + token = (char *)malloc(token_len + 1u); + if (token == NULL) { + free(bytes); + return false; + } + memcpy(token, bytes + start, token_len); + token[token_len] = '\0'; + + if (!amduat_pel_cli_ref_from_text(token, fmt, stdin_used, &ref)) { + free(token); + free(bytes); + return false; + } + free(token); + + if (!amduat_pel_cli_append_ref(refs, refs_len, ref)) { + amduat_pel_cli_free_reference(&ref); + free(bytes); + return false; + } + } + + free(bytes); + return true; +} + +static void amduat_pel_cli_json_escape(FILE *stream, + const uint8_t *data, + size_t len) { + size_t i; + + fputc('"', stream); + for (i = 0; i < len; ++i) { + uint8_t c = data[i]; + switch (c) { + case '"': + fputs("\\\"", stream); + break; + case '\\': + fputs("\\\\", stream); + break; + case '\b': + fputs("\\b", stream); + break; + case '\f': + fputs("\\f", stream); + break; + case '\n': + fputs("\\n", stream); + break; + case '\r': + fputs("\\r", stream); + break; + case '\t': + fputs("\\t", stream); + break; + default: + if (c < 0x20) { + fprintf(stream, "\\u%04x", (unsigned int)c); + } else { + fputc((int)c, stream); + } + break; + } + } + fputc('"', stream); +} + +static bool amduat_pel_cli_print_ref_text(FILE *stream, + amduat_reference_t ref, + amduat_pel_cli_ref_format_t fmt) { + char *ref_text = NULL; + if (!amduat_pel_cli_ref_to_text(ref, fmt, &ref_text)) { + return false; + } + fputs(ref_text, stream); + free(ref_text); + return true; +} + +static bool amduat_pel_cli_print_ref_json(FILE *stream, + amduat_reference_t ref, + amduat_pel_cli_ref_format_t fmt) { + char *ref_text = NULL; + if (!amduat_pel_cli_ref_to_text(ref, fmt, &ref_text)) { + return false; + } + amduat_pel_cli_json_escape(stream, + (const uint8_t *)ref_text, + strlen(ref_text)); + free(ref_text); + return true; +} + +static bool amduat_pel_cli_parse_op_name(const char *text, + const char **out_name, + uint32_t *out_version) { + const char *eq; + + if (text == NULL || out_name == NULL || out_version == NULL) { + return false; + } + + eq = strchr(text, '='); + if (eq == NULL) { + *out_name = text; + *out_version = 1; + return true; + } + + { + size_t name_len = (size_t)(eq - text); + char *name; + uint32_t version; + + if (name_len == 0) { + return false; + } + if (!amduat_asl_parse_u32(eq + 1, &version)) { + return false; + } + name = (char *)malloc(name_len + 1u); + if (name == NULL) { + return false; + } + memcpy(name, text, name_len); + name[name_len] = '\0'; + *out_name = name; + *out_version = version; + return true; + } +} + +static void amduat_pel_cli_free_op_name(const char *name, const char *original) { + if (name != NULL && name != original) { + free((void *)name); + } +} + +static bool amduat_pel_cli_print_program_text( + FILE *stream, + const amduat_pel_program_t *program) { + size_t i; + + if (program == NULL) { + return false; + } + + fprintf(stream, "nodes_len=%zu\n", program->nodes_len); + fprintf(stream, "roots_len=%zu\n", program->roots_len); + + for (i = 0; i < program->nodes_len; ++i) { + const amduat_pel_node_t *node = &program->nodes[i]; + size_t j; + char *params_hex; + + fprintf(stream, "node[%zu].id=%u\n", i, node->id); + fprintf(stream, "node[%zu].op.name=%.*s\n", i, + (int)node->op.name.len, + node->op.name.data ? (const char *)node->op.name.data : ""); + fprintf(stream, "node[%zu].op.version=%u\n", i, node->op.version); + fprintf(stream, "node[%zu].inputs_len=%zu\n", i, node->inputs_len); + + for (j = 0; j < node->inputs_len; ++j) { + const amduat_pel_dag_input_t *input = &node->inputs[j]; + if (input->kind == AMDUAT_PEL_DAG_INPUT_EXTERNAL) { + fprintf(stream, "node[%zu].input[%zu].kind=external\n", i, j); + fprintf(stream, + "node[%zu].input[%zu].external.input_index=%u\n", + i, j, input->value.external.input_index); + } else { + fprintf(stream, "node[%zu].input[%zu].kind=node\n", i, j); + fprintf(stream, + "node[%zu].input[%zu].node.node_id=%u\n", + i, j, input->value.node.node_id); + fprintf(stream, + "node[%zu].input[%zu].node.output_index=%u\n", + i, j, input->value.node.output_index); + } + } + + params_hex = amduat_pel_cli_hex_encode(node->params); + if (params_hex == NULL) { + return false; + } + fprintf(stream, "node[%zu].params_len=%zu\n", i, node->params.len); + fprintf(stream, "node[%zu].params_hex=%s\n", i, params_hex); + free(params_hex); + } + + for (i = 0; i < program->roots_len; ++i) { + fprintf(stream, "root[%zu].node_id=%u\n", + i, program->roots[i].node_id); + fprintf(stream, "root[%zu].output_index=%u\n", + i, program->roots[i].output_index); + } + + return true; +} + +static bool amduat_pel_cli_print_program_json( + FILE *stream, + const amduat_pel_program_t *program) { + size_t i; + + if (program == NULL) { + return false; + } + + fputs("{\"nodes\":[", stream); + for (i = 0; i < program->nodes_len; ++i) { + const amduat_pel_node_t *node = &program->nodes[i]; + size_t j; + char *params_hex; + + if (i != 0) { + fputc(',', stream); + } + fputs("{\"id\":", stream); + fprintf(stream, "%u", node->id); + fputs(",\"op\":{\"name\":", stream); + amduat_pel_cli_json_escape(stream, + node->op.name.data, + node->op.name.len); + fprintf(stream, ",\"version\":%u}", node->op.version); + fputs(",\"inputs\":[", stream); + for (j = 0; j < node->inputs_len; ++j) { + const amduat_pel_dag_input_t *input = &node->inputs[j]; + if (j != 0) { + fputc(',', stream); + } + if (input->kind == AMDUAT_PEL_DAG_INPUT_EXTERNAL) { + fprintf(stream, + "{\"kind\":\"external\",\"input_index\":%u}", + input->value.external.input_index); + } else { + fprintf(stream, + "{\"kind\":\"node\",\"node_id\":%u,\"output_index\":%u}", + input->value.node.node_id, + input->value.node.output_index); + } + } + fputs("]", stream); + params_hex = amduat_pel_cli_hex_encode(node->params); + if (params_hex == NULL) { + return false; + } + fputs(",\"params_hex\":", stream); + amduat_pel_cli_json_escape(stream, + (const uint8_t *)params_hex, + strlen(params_hex)); + free(params_hex); + fputs("}", stream); + } + fputs("],\"roots\":[", stream); + for (i = 0; i < program->roots_len; ++i) { + if (i != 0) { + fputc(',', stream); + } + fprintf(stream, + "{\"node_id\":%u,\"output_index\":%u}", + program->roots[i].node_id, + program->roots[i].output_index); + } + fputs("]}", stream); + return true; +} + +static bool amduat_pel_cli_print_diagnostics_text( + FILE *stream, + const char *prefix, + const amduat_pel_diagnostic_entry_t *diags, + size_t diags_len) { + size_t i; + + if (diags_len == 0) { + return true; + } + if (prefix == NULL) { + prefix = ""; + } + + for (i = 0; i < diags_len; ++i) { + char *msg_hex = amduat_pel_cli_hex_encode(diags[i].message); + if (msg_hex == NULL) { + return false; + } + fprintf(stream, "%sdiagnostic[%zu].code=%u\n", prefix, i, diags[i].code); + fprintf(stream, "%sdiagnostic[%zu].message_hex=%s\n", + prefix, i, msg_hex); + free(msg_hex); + } + return true; +} + +static bool amduat_pel_cli_print_diagnostics_json( + FILE *stream, + const amduat_pel_diagnostic_entry_t *diags, + size_t diags_len) { + size_t i; + + fputs("[", stream); + for (i = 0; i < diags_len; ++i) { + char *msg_hex = amduat_pel_cli_hex_encode(diags[i].message); + if (msg_hex == NULL) { + return false; + } + if (i != 0) { + fputc(',', stream); + } + fprintf(stream, "{\"code\":%u,\"message_hex\":", diags[i].code); + amduat_pel_cli_json_escape(stream, + (const uint8_t *)msg_hex, + strlen(msg_hex)); + fputs("}", stream); + free(msg_hex); + } + fputs("]", stream); + return true; +} + +static bool amduat_pel_cli_print_trace_text( + FILE *stream, + const amduat_pel_trace_dag_value_t *trace, + amduat_pel_cli_ref_format_t ref_format) { + size_t i; + + if (trace == NULL) { + return false; + } + + fprintf(stream, "pel1_version=%u\n", trace->pel1_version); + fprintf(stream, "scheme_ref="); + if (!amduat_pel_cli_print_ref_text(stream, trace->scheme_ref, ref_format)) { + return false; + } + fputc('\n', stream); + fprintf(stream, "program_ref="); + if (!amduat_pel_cli_print_ref_text(stream, trace->program_ref, ref_format)) { + return false; + } + fputc('\n', stream); + fprintf(stream, "status=%s(%u)\n", + amduat_pel_cli_status_name(trace->status), + (unsigned int)trace->status); + fprintf(stream, "error_kind=%s(%u)\n", + amduat_pel_cli_error_kind_name(trace->summary.kind), + (unsigned int)trace->summary.kind); + fprintf(stream, "status_code=%u\n", + (unsigned int)trace->summary.status_code); + + fprintf(stream, "has_exec_result_ref=%s\n", + trace->has_exec_result_ref ? "true" : "false"); + if (trace->has_exec_result_ref) { + fprintf(stream, "exec_result_ref="); + if (!amduat_pel_cli_print_ref_text(stream, + trace->exec_result_ref, + ref_format)) { + return false; + } + fputc('\n', stream); + } + + fprintf(stream, "input_refs_len=%zu\n", trace->input_refs_len); + for (i = 0; i < trace->input_refs_len; ++i) { + fprintf(stream, "input_ref[%zu]=", i); + if (!amduat_pel_cli_print_ref_text(stream, + trace->input_refs[i], + ref_format)) { + return false; + } + fputc('\n', stream); + } + + fprintf(stream, "has_params_ref=%s\n", + trace->has_params_ref ? "true" : "false"); + if (trace->has_params_ref) { + fprintf(stream, "params_ref="); + if (!amduat_pel_cli_print_ref_text(stream, + trace->params_ref, + ref_format)) { + return false; + } + fputc('\n', stream); + } + + fprintf(stream, "node_traces_len=%zu\n", trace->node_traces_len); + for (i = 0; i < trace->node_traces_len; ++i) { + const amduat_pel_node_trace_dag_t *node = &trace->node_traces[i]; + size_t j; + + fprintf(stream, "node_trace[%zu].node_id=%u\n", i, node->node_id); + fprintf(stream, "node_trace[%zu].op.name=%.*s\n", i, + (int)node->op_name.len, + node->op_name.data ? (const char *)node->op_name.data : ""); + fprintf(stream, "node_trace[%zu].op.version=%u\n", i, node->op_version); + fprintf(stream, "node_trace[%zu].status=%s(%u)\n", i, + amduat_pel_cli_node_status_name(node->status), + (unsigned int)node->status); + fprintf(stream, "node_trace[%zu].status_code=%u\n", i, + node->status_code); + fprintf(stream, "node_trace[%zu].output_refs_len=%zu\n", i, + node->output_refs_len); + for (j = 0; j < node->output_refs_len; ++j) { + fprintf(stream, "node_trace[%zu].output_ref[%zu]=", + i, j); + if (!amduat_pel_cli_print_ref_text(stream, + node->output_refs[j], + ref_format)) { + return false; + } + fputc('\n', stream); + } + { + char prefix[64]; + fprintf(stream, "node_trace[%zu].diagnostics_len=%zu\n", i, + node->diagnostics_len); + snprintf(prefix, sizeof(prefix), "node_trace[%zu].", i); + if (!amduat_pel_cli_print_diagnostics_text( + stream, + prefix, + node->diagnostics, + node->diagnostics_len)) { + return false; + } + } + } + + return true; +} + +static bool amduat_pel_cli_print_trace_json( + FILE *stream, + const amduat_pel_trace_dag_value_t *trace, + amduat_pel_cli_ref_format_t ref_format) { + size_t i; + + if (trace == NULL) { + return false; + } + + fputs("{\"pel1_version\":", stream); + fprintf(stream, "%u", trace->pel1_version); + fputs(",\"scheme_ref\":", stream); + if (!amduat_pel_cli_print_ref_json(stream, trace->scheme_ref, ref_format)) { + return false; + } + fputs(",\"program_ref\":", stream); + if (!amduat_pel_cli_print_ref_json(stream, trace->program_ref, ref_format)) { + return false; + } + fputs(",\"status\":", stream); + amduat_pel_cli_json_escape( + stream, + (const uint8_t *)amduat_pel_cli_status_name(trace->status), + strlen(amduat_pel_cli_status_name(trace->status))); + fprintf(stream, ",\"status_code\":%u", + (unsigned int)trace->summary.status_code); + fputs(",\"error_kind\":", stream); + amduat_pel_cli_json_escape( + stream, + (const uint8_t *)amduat_pel_cli_error_kind_name(trace->summary.kind), + strlen(amduat_pel_cli_error_kind_name(trace->summary.kind))); + fprintf(stream, ",\"error_kind_code\":%u", + (unsigned int)trace->summary.kind); + + fputs(",\"has_exec_result_ref\":", stream); + fputs(trace->has_exec_result_ref ? "true" : "false", stream); + fputs(",\"exec_result_ref\":", stream); + if (trace->has_exec_result_ref) { + if (!amduat_pel_cli_print_ref_json(stream, + trace->exec_result_ref, + ref_format)) { + return false; + } + } else { + fputs("null", stream); + } + + fputs(",\"input_refs\":[", stream); + for (i = 0; i < trace->input_refs_len; ++i) { + if (i != 0) { + fputc(',', stream); + } + if (!amduat_pel_cli_print_ref_json(stream, + trace->input_refs[i], + ref_format)) { + return false; + } + } + fputs("]", stream); + + fputs(",\"has_params_ref\":", stream); + fputs(trace->has_params_ref ? "true" : "false", stream); + fputs(",\"params_ref\":", stream); + if (trace->has_params_ref) { + if (!amduat_pel_cli_print_ref_json(stream, + trace->params_ref, + ref_format)) { + return false; + } + } else { + fputs("null", stream); + } + + fputs(",\"node_traces\":[", stream); + for (i = 0; i < trace->node_traces_len; ++i) { + const amduat_pel_node_trace_dag_t *node = &trace->node_traces[i]; + size_t j; + if (i != 0) { + fputc(',', stream); + } + fprintf(stream, "{\"node_id\":%u,", node->node_id); + fputs("\"op\":{\"name\":", stream); + amduat_pel_cli_json_escape(stream, node->op_name.data, node->op_name.len); + fprintf(stream, ",\"version\":%u},", node->op_version); + fputs("\"status\":", stream); + amduat_pel_cli_json_escape( + stream, + (const uint8_t *)amduat_pel_cli_node_status_name(node->status), + strlen(amduat_pel_cli_node_status_name(node->status))); + fprintf(stream, ",\"status_code\":%u", node->status_code); + fputs(",\"output_refs\":[", stream); + for (j = 0; j < node->output_refs_len; ++j) { + if (j != 0) { + fputc(',', stream); + } + if (!amduat_pel_cli_print_ref_json(stream, + node->output_refs[j], + ref_format)) { + return false; + } + } + fputs("],\"diagnostics\":", stream); + if (!amduat_pel_cli_print_diagnostics_json(stream, + node->diagnostics, + node->diagnostics_len)) { + return false; + } + fputs("}", stream); + } + fputs("]}", stream); + + return true; +} + +static bool amduat_pel_cli_print_result_text( + FILE *stream, + const amduat_pel_surface_execution_result_t *result, + amduat_pel_cli_ref_format_t ref_format) { + size_t i; + + if (result == NULL) { + return false; + } + + fprintf(stream, "pel1_version=%u\n", result->pel1_version); + fprintf(stream, "scheme_ref="); + if (!amduat_pel_cli_print_ref_text(stream, result->scheme_ref, ref_format)) { + return false; + } + fputc('\n', stream); + fprintf(stream, "program_ref="); + if (!amduat_pel_cli_print_ref_text(stream, result->program_ref, ref_format)) { + return false; + } + fputc('\n', stream); + + fprintf(stream, "status=%s(%u)\n", + amduat_pel_cli_status_name(result->core_result.status), + (unsigned int)result->core_result.status); + fprintf(stream, "error_kind=%s(%u)\n", + amduat_pel_cli_error_kind_name(result->core_result.summary.kind), + (unsigned int)result->core_result.summary.kind); + fprintf(stream, "status_code=%u\n", + (unsigned int)result->core_result.summary.status_code); + if (!amduat_pel_cli_print_diagnostics_text( + stream, + "", + result->core_result.diagnostics, + result->core_result.diagnostics_len)) { + return false; + } + + fprintf(stream, "input_refs_len=%zu\n", result->input_refs_len); + for (i = 0; i < result->input_refs_len; ++i) { + fprintf(stream, "input_ref[%zu]=", i); + if (!amduat_pel_cli_print_ref_text(stream, + result->input_refs[i], + ref_format)) { + return false; + } + fputc('\n', stream); + } + + fprintf(stream, "output_refs_len=%zu\n", result->output_refs_len); + for (i = 0; i < result->output_refs_len; ++i) { + fprintf(stream, "output_ref[%zu]=", i); + if (!amduat_pel_cli_print_ref_text(stream, + result->output_refs[i], + ref_format)) { + return false; + } + fputc('\n', stream); + } + + fprintf(stream, "has_params_ref=%s\n", + result->has_params_ref ? "true" : "false"); + if (result->has_params_ref) { + fprintf(stream, "params_ref="); + if (!amduat_pel_cli_print_ref_text(stream, + result->params_ref, + ref_format)) { + return false; + } + fputc('\n', stream); + } + + fprintf(stream, "has_trace_ref=%s\n", + result->has_trace_ref ? "true" : "false"); + if (result->has_trace_ref) { + fprintf(stream, "trace_ref="); + if (!amduat_pel_cli_print_ref_text(stream, + result->trace_ref, + ref_format)) { + return false; + } + fputc('\n', stream); + } + + fprintf(stream, "has_store_failure=%s\n", + result->has_store_failure ? "true" : "false"); + if (result->has_store_failure) { + fprintf(stream, "store_failure.phase=%s(%u)\n", + amduat_pel_cli_store_phase_name(result->store_failure.phase), + (unsigned int)result->store_failure.phase); + fprintf(stream, "store_failure.error_code=%s(%u)\n", + amduat_pel_cli_store_error_name(result->store_failure.error_code), + (unsigned int)result->store_failure.error_code); + fprintf(stream, "store_failure.failing_ref="); + if (!amduat_pel_cli_print_ref_text( + stream, result->store_failure.failing_ref, ref_format)) { + return false; + } + fputc('\n', stream); + } + + return true; +} + +static bool amduat_pel_cli_print_result_json( + FILE *stream, + const amduat_pel_surface_execution_result_t *result, + amduat_pel_cli_ref_format_t ref_format) { + size_t i; + + if (result == NULL) { + return false; + } + + fputs("{\"pel1_version\":", stream); + fprintf(stream, "%u", result->pel1_version); + fputs(",\"scheme_ref\":", stream); + if (!amduat_pel_cli_print_ref_json(stream, result->scheme_ref, ref_format)) { + return false; + } + fputs(",\"program_ref\":", stream); + if (!amduat_pel_cli_print_ref_json(stream, result->program_ref, ref_format)) { + return false; + } + fputs(",\"status\":", stream); + amduat_pel_cli_json_escape( + stream, + (const uint8_t *)amduat_pel_cli_status_name(result->core_result.status), + strlen(amduat_pel_cli_status_name(result->core_result.status))); + fprintf(stream, ",\"status_code\":%u", + (unsigned int)result->core_result.summary.status_code); + fputs(",\"error_kind\":", stream); + amduat_pel_cli_json_escape( + stream, + (const uint8_t *)amduat_pel_cli_error_kind_name( + result->core_result.summary.kind), + strlen(amduat_pel_cli_error_kind_name( + result->core_result.summary.kind))); + fprintf(stream, ",\"error_kind_code\":%u", + (unsigned int)result->core_result.summary.kind); + fputs(",\"diagnostics\":", stream); + if (!amduat_pel_cli_print_diagnostics_json( + stream, + result->core_result.diagnostics, + result->core_result.diagnostics_len)) { + return false; + } + + fputs(",\"input_refs\":[", stream); + for (i = 0; i < result->input_refs_len; ++i) { + if (i != 0) { + fputc(',', stream); + } + if (!amduat_pel_cli_print_ref_json(stream, + result->input_refs[i], + ref_format)) { + return false; + } + } + fputs("]", stream); + + fputs(",\"output_refs\":[", stream); + for (i = 0; i < result->output_refs_len; ++i) { + if (i != 0) { + fputc(',', stream); + } + if (!amduat_pel_cli_print_ref_json(stream, + result->output_refs[i], + ref_format)) { + return false; + } + } + fputs("]", stream); + + fputs(",\"has_params_ref\":", stream); + fputs(result->has_params_ref ? "true" : "false", stream); + fputs(",\"params_ref\":", stream); + if (result->has_params_ref) { + if (!amduat_pel_cli_print_ref_json(stream, + result->params_ref, + ref_format)) { + return false; + } + } else { + fputs("null", stream); + } + + fputs(",\"has_trace_ref\":", stream); + fputs(result->has_trace_ref ? "true" : "false", stream); + fputs(",\"trace_ref\":", stream); + if (result->has_trace_ref) { + if (!amduat_pel_cli_print_ref_json(stream, + result->trace_ref, + ref_format)) { + return false; + } + } else { + fputs("null", stream); + } + + fputs(",\"has_store_failure\":", stream); + fputs(result->has_store_failure ? "true" : "false", stream); + fputs(",\"store_failure\":", stream); + if (result->has_store_failure) { + fputs("{\"phase\":", stream); + amduat_pel_cli_json_escape( + stream, + (const uint8_t *)amduat_pel_cli_store_phase_name( + result->store_failure.phase), + strlen(amduat_pel_cli_store_phase_name(result->store_failure.phase))); + fprintf(stream, ",\"phase_code\":%u", + (unsigned int)result->store_failure.phase); + fputs(",\"error_code\":", stream); + amduat_pel_cli_json_escape( + stream, + (const uint8_t *)amduat_pel_cli_store_error_name( + result->store_failure.error_code), + strlen(amduat_pel_cli_store_error_name( + result->store_failure.error_code))); + fprintf(stream, ",\"error_code_id\":%u", + (unsigned int)result->store_failure.error_code); + fputs(",\"failing_ref\":", stream); + if (!amduat_pel_cli_print_ref_json(stream, + result->store_failure.failing_ref, + ref_format)) { + return false; + } + fputs("}", stream); + } else { + fputs("null", stream); + } + + fputs("}", stream); + return true; +} + +static int amduat_pel_cli_cmd_run(int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *program_ref_text = NULL; + const char *scheme_ref_text = "dag"; + const char *params_ref_text = NULL; + const char *inputs_file = NULL; + const char **input_ref_texts = NULL; + size_t input_ref_texts_len = 0; + amduat_pel_cli_ref_format_t ref_format; + amduat_pel_cli_output_format_t format; + const char *root; + bool quiet; + bool has_params_ref = false; + amduat_reference_t program_ref; + amduat_reference_t scheme_ref; + amduat_reference_t params_ref; + amduat_reference_t *input_refs = NULL; + size_t input_refs_len = 0; + amduat_reference_t *output_refs = NULL; + size_t output_refs_len = 0; + amduat_reference_t result_ref; + amduat_reference_t trace_ref; + amduat_artifact_t result_artifact; + amduat_pel_surface_execution_result_t result_value; + bool has_result_value = false; + bool has_trace_ref = false; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + bool stdin_used = false; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + root = global->root; + ref_format = global->ref_format; + format = global->format; + quiet = global->quiet; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--program-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --program-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + program_ref_text = argv[++i]; + } else if (strcmp(argv[i], "--scheme-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --scheme-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + scheme_ref_text = argv[++i]; + } else if (strcmp(argv[i], "--input-ref") == 0) { + const char **next; + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + next = (const char **)realloc( + input_ref_texts, + (input_ref_texts_len + 1u) * sizeof(*input_ref_texts)); + if (next == NULL) { + fprintf(stderr, "error: out of memory\n"); + return AMDUAT_PEL_CLI_EXIT_STORE; + } + input_ref_texts = next; + input_ref_texts[input_ref_texts_len++] = argv[++i]; + } else if (strcmp(argv[i], "--inputs-file") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --inputs-file requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + inputs_file = argv[++i]; + } else if (strcmp(argv[i], "--params-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --params-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + params_ref_text = argv[++i]; + has_params_ref = true; + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + root = argv[++i]; + } else if (strcmp(argv[i], "--quiet") == 0) { + quiet = true; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (program_ref_text == NULL) { + fprintf(stderr, "error: --program-ref is required\n"); + free(input_ref_texts); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + if (!amduat_asl_store_fs_load_config(root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", root); + free(input_ref_texts); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, root)) { + fprintf(stderr, "error: failed to initialize store\n"); + free(input_ref_texts); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + if (!amduat_pel_cli_ref_from_text(program_ref_text, + ref_format, + &stdin_used, + &program_ref)) { + fprintf(stderr, "error: invalid --program-ref\n"); + free(input_ref_texts); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + if (strcmp(scheme_ref_text, "dag") == 0) { + scheme_ref = amduat_pel_program_dag_scheme_ref(); + } else if (!amduat_pel_cli_ref_from_text(scheme_ref_text, + ref_format, + &stdin_used, + &scheme_ref)) { + amduat_pel_cli_free_reference(&program_ref); + free(input_ref_texts); + fprintf(stderr, "error: invalid --scheme-ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + if (has_params_ref) { + if (!amduat_pel_cli_ref_from_text(params_ref_text, + ref_format, + &stdin_used, + ¶ms_ref)) { + amduat_pel_cli_free_reference(&program_ref); + if (strcmp(scheme_ref_text, "dag") != 0) { + amduat_pel_cli_free_reference(&scheme_ref); + } + free(input_ref_texts); + fprintf(stderr, "error: invalid --params-ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + } else { + memset(¶ms_ref, 0, sizeof(params_ref)); + } + + for (i = 0; i < (int)input_ref_texts_len; ++i) { + amduat_reference_t ref; + if (!amduat_pel_cli_ref_from_text(input_ref_texts[i], + ref_format, + &stdin_used, + &ref)) { + fprintf(stderr, "error: invalid --input-ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto run_cleanup; + } + if (!amduat_pel_cli_append_ref(&input_refs, &input_refs_len, ref)) { + amduat_pel_cli_free_reference(&ref); + fprintf(stderr, "error: out of memory\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_STORE; + goto run_cleanup; + } + } + + if (inputs_file != NULL) { + if (!amduat_pel_cli_append_refs_from_file(inputs_file, + ref_format, + &stdin_used, + &input_refs, + &input_refs_len)) { + fprintf(stderr, "error: failed to read inputs file\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + goto run_cleanup; + } + } + + memset(&result_ref, 0, sizeof(result_ref)); + if (!amduat_pel_surf_run(&store, + scheme_ref, + program_ref, + input_refs, + input_refs_len, + has_params_ref, + params_ref, + &output_refs, + &output_refs_len, + &result_ref)) { + fprintf(stderr, "error: PEL surface execution failed\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_STORE; + goto run_cleanup; + } + + memset(&trace_ref, 0, sizeof(trace_ref)); + memset(&result_artifact, 0, sizeof(result_artifact)); + if (amduat_asl_store_get(&store, result_ref, &result_artifact) == + AMDUAT_ASL_STORE_OK) { + if (amduat_enc_pel1_result_decode_v1(result_artifact.bytes, + &result_value)) { + has_result_value = true; + if (result_value.has_trace_ref) { + trace_ref = result_value.trace_ref; + has_trace_ref = true; + } + } + } + + if (!quiet) { + fprintf(stderr, "root=%s\n", root); + fprintf(stderr, "program_ref="); + if (!amduat_pel_cli_print_ref_text(stderr, program_ref, ref_format)) { + fprintf(stderr, "\n"); + } else { + fputc('\n', stderr); + } + if (has_params_ref) { + fprintf(stderr, "params_ref="); + if (!amduat_pel_cli_print_ref_text(stderr, params_ref, ref_format)) { + fprintf(stderr, "\n"); + } else { + fputc('\n', stderr); + } + } + if (has_result_value) { + fprintf(stderr, "status=%s(%u)\n", + amduat_pel_cli_status_name(result_value.core_result.status), + (unsigned int)result_value.core_result.status); + fprintf(stderr, "error_kind=%s(%u)\n", + amduat_pel_cli_error_kind_name( + result_value.core_result.summary.kind), + (unsigned int)result_value.core_result.summary.kind); + fprintf(stderr, "status_code=%u\n", + (unsigned int)result_value.core_result.summary.status_code); + } + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + fprintf(stdout, "result_ref="); + if (!amduat_pel_cli_print_ref_text(stdout, result_ref, ref_format)) { + fprintf(stderr, "error: failed to encode result ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto run_cleanup; + } + fputc('\n', stdout); + if (has_trace_ref) { + fprintf(stdout, "trace_ref="); + if (!amduat_pel_cli_print_ref_text(stdout, trace_ref, ref_format)) { + fprintf(stderr, "error: failed to encode trace ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto run_cleanup; + } + fputc('\n', stdout); + } + for (i = 0; i < (int)output_refs_len; ++i) { + fprintf(stdout, "output_ref[%d]=", i); + if (!amduat_pel_cli_print_ref_text(stdout, + output_refs[i], + ref_format)) { + fprintf(stderr, "error: failed to encode output ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto run_cleanup; + } + fputc('\n', stdout); + } + } else { + fputs("{\"ref_format\":\"", stdout); + fputs(ref_format == AMDUAT_PEL_CLI_REF_HEX ? "hex" : "bytes", stdout); + fputs("\",\"result_ref\":", stdout); + if (!amduat_pel_cli_print_ref_json(stdout, result_ref, ref_format)) { + fprintf(stderr, "error: failed to encode result ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto run_cleanup; + } + fputs(",\"trace_ref\":", stdout); + if (has_trace_ref) { + if (!amduat_pel_cli_print_ref_json(stdout, trace_ref, ref_format)) { + fprintf(stderr, "error: failed to encode trace ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto run_cleanup; + } + } else { + fputs("null", stdout); + } + fputs(",\"output_refs\":[", stdout); + for (i = 0; i < (int)output_refs_len; ++i) { + if (i != 0) { + fputc(',', stdout); + } + if (!amduat_pel_cli_print_ref_json(stdout, + output_refs[i], + ref_format)) { + fprintf(stderr, "error: failed to encode output ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto run_cleanup; + } + } + fputs("]}\n", stdout); + } + +run_cleanup: + if (has_result_value) { + amduat_enc_pel1_result_free(&result_value); + } + if (output_refs != NULL) { + amduat_pel_surf_free_refs(output_refs, output_refs_len); + } + amduat_pel_surf_free_ref(&result_ref); + amduat_pel_cli_free_artifact(&result_artifact); + if (input_refs != NULL) { + amduat_pel_cli_free_refs(input_refs, input_refs_len); + } + if (has_params_ref) { + amduat_pel_cli_free_reference(¶ms_ref); + } + amduat_pel_cli_free_reference(&program_ref); + if (strcmp(scheme_ref_text, "dag") != 0) { + amduat_pel_cli_free_reference(&scheme_ref); + } + free(input_ref_texts); + return exit_code; +} + +static int amduat_pel_cli_cmd_exec(int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *program_path = NULL; + const char **input_paths = NULL; + size_t input_paths_len = 0; + amduat_pel_cli_io_format_t input_format = AMDUAT_PEL_CLI_IO_RAW; + amduat_pel_cli_io_format_t output_format = AMDUAT_PEL_CLI_IO_RAW; + amduat_pel_cli_output_format_t format; + const char *output_dir = "."; + const char *result_out = "-"; + amduat_pel_program_t program; + amduat_artifact_t *inputs = NULL; + amduat_artifact_t *outputs = NULL; + size_t outputs_len = 0; + amduat_pel_execution_result_value_t result; + uint8_t *program_bytes = NULL; + size_t program_len = 0; + bool stdin_used = false; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + FILE *out_stream = NULL; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--program") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --program requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + program_path = argv[++i]; + } else if (strcmp(argv[i], "--input") == 0) { + const char **next; + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + next = (const char **)realloc( + input_paths, (input_paths_len + 1u) * sizeof(*input_paths)); + if (next == NULL) { + fprintf(stderr, "error: out of memory\n"); + return AMDUAT_PEL_CLI_EXIT_STORE; + } + input_paths = next; + input_paths[input_paths_len++] = argv[++i]; + } else if (strcmp(argv[i], "--input-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_io_format(argv[++i], &input_format)) { + fprintf(stderr, "error: invalid input-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--output-dir") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output-dir requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + output_dir = argv[++i]; + } else if (strcmp(argv[i], "--output-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_io_format(argv[++i], &output_format)) { + fprintf(stderr, "error: invalid output-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--result-out") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --result-out requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + result_out = argv[++i]; + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + free(input_paths); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + free(input_paths); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (program_path == NULL) { + fprintf(stderr, "error: --program is required\n"); + free(input_paths); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + if (!amduat_pel_cli_read_path_once(program_path, + &stdin_used, + &program_bytes, + &program_len)) { + fprintf(stderr, "error: failed to read program\n"); + free(input_paths); + return AMDUAT_PEL_CLI_EXIT_IO; + } + + memset(&program, 0, sizeof(program)); + if (!amduat_enc_pel_program_dag_decode_v1( + amduat_octets(program_bytes, program_len), &program)) { + fprintf(stderr, "error: invalid program encoding\n"); + free(program_bytes); + free(input_paths); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + free(program_bytes); + + if (input_paths_len > 0) { + inputs = (amduat_artifact_t *)calloc( + input_paths_len, sizeof(*inputs)); + if (inputs == NULL) { + fprintf(stderr, "error: out of memory\n"); + amduat_enc_pel_program_dag_free(&program); + free(input_paths); + return AMDUAT_PEL_CLI_EXIT_STORE; + } + } + + for (i = 0; i < (int)input_paths_len; ++i) { + uint8_t *bytes = NULL; + size_t len = 0; + if (!amduat_pel_cli_read_path_once(input_paths[i], + &stdin_used, + &bytes, + &len)) { + fprintf(stderr, "error: failed to read input: %s\n", input_paths[i]); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + goto exec_cleanup; + } + if (input_format == AMDUAT_PEL_CLI_IO_ARTIFACT) { + if (!amduat_enc_asl1_core_decode_artifact_v1( + amduat_octets(bytes, len), &inputs[i])) { + free(bytes); + fprintf(stderr, "error: invalid input artifact\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto exec_cleanup; + } + free(bytes); + } else { + inputs[i] = amduat_artifact(amduat_octets(bytes, len)); + } + } + + if (!amduat_pel_program_dag_exec(&program, + inputs, + input_paths_len, + &outputs, + &outputs_len, + &result)) { + fprintf(stderr, "error: exec failed\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_STORE; + goto exec_cleanup; + } + + for (i = 0; i < (int)outputs_len; ++i) { + char path_buf[4096]; + if (snprintf(path_buf, sizeof(path_buf), "%s/output-%d", + output_dir, i) < 0) { + fprintf(stderr, "error: failed to build output path\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + goto exec_cleanup; + } + if (output_format == AMDUAT_PEL_CLI_IO_RAW) { + if (!amduat_asl_write_path(path_buf, + outputs[i].bytes.data, + outputs[i].bytes.len)) { + fprintf(stderr, "error: failed to write output: %s\n", path_buf); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + goto exec_cleanup; + } + } else { + amduat_octets_t encoded; + if (!amduat_enc_asl1_core_encode_artifact_v1(outputs[i], &encoded)) { + fprintf(stderr, "error: failed to encode output artifact\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + goto exec_cleanup; + } + if (!amduat_asl_write_path(path_buf, encoded.data, encoded.len)) { + fprintf(stderr, "error: failed to write output: %s\n", path_buf); + free((void *)encoded.data); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + goto exec_cleanup; + } + free((void *)encoded.data); + } + } + + if (strcmp(result_out, "-") == 0) { + out_stream = stdout; + } else { + out_stream = fopen(result_out, "wb"); + if (out_stream == NULL) { + fprintf(stderr, "error: failed to open result-out: %s\n", result_out); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + goto exec_cleanup; + } + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + fprintf(out_stream, "status=%s(%u)\n", + amduat_pel_cli_status_name(result.status), + (unsigned int)result.status); + fprintf(out_stream, "error_kind=%s(%u)\n", + amduat_pel_cli_error_kind_name(result.summary.kind), + (unsigned int)result.summary.kind); + fprintf(out_stream, "status_code=%u\n", + (unsigned int)result.summary.status_code); + fprintf(out_stream, "outputs_len=%zu\n", outputs_len); + for (i = 0; i < (int)outputs_len; ++i) { + fprintf(out_stream, "output_path[%d]=%s/output-%d\n", + i, output_dir, i); + } + } else { + fputs("{\"status\":", out_stream); + amduat_pel_cli_json_escape( + out_stream, + (const uint8_t *)amduat_pel_cli_status_name(result.status), + strlen(amduat_pel_cli_status_name(result.status))); + fprintf(out_stream, ",\"status_code\":%u", + (unsigned int)result.summary.status_code); + fputs(",\"error_kind\":", out_stream); + amduat_pel_cli_json_escape( + out_stream, + (const uint8_t *)amduat_pel_cli_error_kind_name(result.summary.kind), + strlen(amduat_pel_cli_error_kind_name(result.summary.kind))); + fprintf(out_stream, ",\"error_kind_code\":%u", + (unsigned int)result.summary.kind); + fputs(",\"output_paths\":[", out_stream); + for (i = 0; i < (int)outputs_len; ++i) { + if (i != 0) { + fputc(',', out_stream); + } + { + char path_buf[4096]; + if (snprintf(path_buf, sizeof(path_buf), "%s/output-%d", + output_dir, i) < 0) { + fprintf(stderr, "error: failed to build output path\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + goto exec_cleanup; + } + amduat_pel_cli_json_escape(out_stream, + (const uint8_t *)path_buf, + strlen(path_buf)); + } + } + fputs("]}\n", out_stream); + } + +exec_cleanup: + if (out_stream != NULL && out_stream != stdout) { + fclose(out_stream); + } + if (outputs != NULL) { + amduat_pel_program_dag_free_outputs(outputs, outputs_len); + } + if (inputs != NULL) { + for (i = 0; i < (int)input_paths_len; ++i) { + amduat_pel_cli_free_artifact(&inputs[i]); + } + free(inputs); + } + amduat_enc_pel_program_dag_free(&program); + free(input_paths); + return exit_code; +} + +static int amduat_pel_cli_cmd_validate( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *input_path = "-"; + const char *program_ref_text = NULL; + bool input_path_set = false; + amduat_pel_cli_io_format_t input_format = AMDUAT_PEL_CLI_IO_RAW; + amduat_pel_cli_output_format_t format; + amduat_pel_cli_ref_format_t ref_format; + const char *root; + bool has_expect_type_tag = false; + amduat_type_tag_t expect_type_tag; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_reference_t program_ref; + amduat_artifact_t artifact; + amduat_pel_program_t program; + bool stdin_used = false; + bool ok; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + format = global->format; + ref_format = global->ref_format; + root = global->root; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--input") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + input_path = argv[++i]; + input_path_set = true; + } else if (strcmp(argv[i], "--program-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --program-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + program_ref_text = argv[++i]; + } else if (strcmp(argv[i], "--input-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_io_format(argv[++i], &input_format)) { + fprintf(stderr, "error: invalid input-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--expect-type-tag") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --expect-type-tag requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_asl_parse_type_tag(argv[++i], &expect_type_tag)) { + fprintf(stderr, "error: invalid expect-type-tag\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + has_expect_type_tag = true; + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + root = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (program_ref_text != NULL && input_path_set) { + fprintf(stderr, "error: use --input or --program-ref\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + memset(&artifact, 0, sizeof(artifact)); + if (program_ref_text != NULL) { + if (!amduat_asl_store_fs_load_config(root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", root); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + if (!amduat_pel_cli_ref_from_text(program_ref_text, + ref_format, + &stdin_used, + &program_ref)) { + fprintf(stderr, "error: invalid program-ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + { + amduat_asl_store_error_t err = + amduat_asl_store_get(&store, program_ref, &artifact); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: store get failed: %s\n", + amduat_pel_cli_store_error_str(err)); + amduat_pel_cli_free_reference(&program_ref); + return amduat_pel_cli_map_store_error(err); + } + } + amduat_pel_cli_free_reference(&program_ref); + } else { + uint8_t *bytes = NULL; + size_t len = 0; + if (!amduat_pel_cli_read_path_once(input_path, + &stdin_used, + &bytes, + &len)) { + fprintf(stderr, "error: failed to read input\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + if (input_format == AMDUAT_PEL_CLI_IO_ARTIFACT) { + if (!amduat_enc_asl1_core_decode_artifact_v1( + amduat_octets(bytes, len), &artifact)) { + free(bytes); + fprintf(stderr, "error: invalid artifact encoding\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + free(bytes); + } else { + artifact = amduat_artifact(amduat_octets(bytes, len)); + } + } + + if (has_expect_type_tag) { + if (!artifact.has_type_tag || + artifact.type_tag.tag_id != expect_type_tag.tag_id) { + fprintf(stderr, "error: type-tag mismatch\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_UNSUPPORTED; + } + } + + memset(&program, 0, sizeof(program)); + ok = amduat_enc_pel_program_dag_decode_v1(artifact.bytes, &program); + if (!ok) { + fprintf(stderr, "error: invalid program encoding\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + ok = amduat_pel_program_dag_validate(&program); + amduat_enc_pel_program_dag_free(&program); + amduat_pel_cli_free_artifact(&artifact); + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + fprintf(stdout, "valid=%s\n", ok ? "true" : "false"); + } else { + fprintf(stdout, "{\"valid\":%s}\n", ok ? "true" : "false"); + } + + if (!ok) { + exit_code = AMDUAT_PEL_CLI_EXIT_UNSUPPORTED; + } + return exit_code; +} + +static int amduat_pel_cli_cmd_program_decode( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *input_path = "-"; + const char *program_ref_text = NULL; + bool input_path_set = false; + amduat_pel_cli_io_format_t input_format = AMDUAT_PEL_CLI_IO_RAW; + amduat_pel_cli_output_format_t format; + amduat_pel_cli_ref_format_t ref_format; + const char *root; + bool has_expect_type_tag = false; + amduat_type_tag_t expect_type_tag; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_reference_t program_ref; + amduat_artifact_t artifact; + amduat_pel_program_t program; + bool stdin_used = false; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + ref_format = global->ref_format; + root = global->root; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--input") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + input_path = argv[++i]; + input_path_set = true; + } else if (strcmp(argv[i], "--program-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --program-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + program_ref_text = argv[++i]; + } else if (strcmp(argv[i], "--input-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_io_format(argv[++i], &input_format)) { + fprintf(stderr, "error: invalid input-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--expect-type-tag") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --expect-type-tag requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_asl_parse_type_tag(argv[++i], &expect_type_tag)) { + fprintf(stderr, "error: invalid expect-type-tag\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + has_expect_type_tag = true; + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + root = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (program_ref_text != NULL && input_path_set) { + fprintf(stderr, "error: use --input or --program-ref\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + memset(&artifact, 0, sizeof(artifact)); + if (program_ref_text != NULL) { + if (!amduat_asl_store_fs_load_config(root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", root); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + if (!amduat_pel_cli_ref_from_text(program_ref_text, + ref_format, + &stdin_used, + &program_ref)) { + fprintf(stderr, "error: invalid program-ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + { + amduat_asl_store_error_t err = + amduat_asl_store_get(&store, program_ref, &artifact); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: store get failed: %s\n", + amduat_pel_cli_store_error_str(err)); + amduat_pel_cli_free_reference(&program_ref); + return amduat_pel_cli_map_store_error(err); + } + } + amduat_pel_cli_free_reference(&program_ref); + } else { + uint8_t *bytes = NULL; + size_t len = 0; + if (!amduat_pel_cli_read_path_once(input_path, + &stdin_used, + &bytes, + &len)) { + fprintf(stderr, "error: failed to read input\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + if (input_format == AMDUAT_PEL_CLI_IO_ARTIFACT) { + if (!amduat_enc_asl1_core_decode_artifact_v1( + amduat_octets(bytes, len), &artifact)) { + free(bytes); + fprintf(stderr, "error: invalid artifact encoding\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + free(bytes); + } else { + artifact = amduat_artifact(amduat_octets(bytes, len)); + } + } + + if (has_expect_type_tag) { + if (!artifact.has_type_tag || + artifact.type_tag.tag_id != expect_type_tag.tag_id) { + fprintf(stderr, "error: type-tag mismatch\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_UNSUPPORTED; + } + } + + memset(&program, 0, sizeof(program)); + if (!amduat_enc_pel_program_dag_decode_v1(artifact.bytes, &program)) { + fprintf(stderr, "error: invalid program encoding\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + if (!amduat_pel_cli_print_program_text(stdout, &program)) { + fprintf(stderr, "error: failed to format program\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } + } else { + if (!amduat_pel_cli_print_program_json(stdout, &program)) { + fprintf(stderr, "error: failed to format program\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputc('\n', stdout); + } + + amduat_enc_pel_program_dag_free(&program); + amduat_pel_cli_free_artifact(&artifact); + return exit_code; +} + +static int amduat_pel_cli_cmd_program_normalize( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *input_path = "-"; + const char *output_path = "-"; + amduat_pel_cli_io_format_t input_format = AMDUAT_PEL_CLI_IO_RAW; + amduat_pel_cli_io_format_t output_format = AMDUAT_PEL_CLI_IO_RAW; + bool has_type_tag = false; + amduat_type_tag_t type_tag; + amduat_artifact_t artifact; + amduat_pel_program_t program; + amduat_octets_t normalized; + bool stdin_used = false; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + int i; + + (void)global; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--input") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + input_path = argv[++i]; + } else if (strcmp(argv[i], "--input-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_io_format(argv[++i], &input_format)) { + fprintf(stderr, "error: invalid input-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--output") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + output_path = argv[++i]; + } else if (strcmp(argv[i], "--output-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_io_format(argv[++i], &output_format)) { + fprintf(stderr, "error: invalid output-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--type-tag") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --type-tag requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_asl_parse_type_tag(argv[++i], &type_tag)) { + fprintf(stderr, "error: invalid type-tag\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + has_type_tag = true; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + memset(&artifact, 0, sizeof(artifact)); + { + uint8_t *bytes = NULL; + size_t len = 0; + if (!amduat_pel_cli_read_path_once(input_path, + &stdin_used, + &bytes, + &len)) { + fprintf(stderr, "error: failed to read input\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + if (input_format == AMDUAT_PEL_CLI_IO_ARTIFACT) { + if (!amduat_enc_asl1_core_decode_artifact_v1( + amduat_octets(bytes, len), &artifact)) { + free(bytes); + fprintf(stderr, "error: invalid artifact encoding\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + free(bytes); + } else { + artifact = amduat_artifact(amduat_octets(bytes, len)); + } + } + + memset(&program, 0, sizeof(program)); + if (!amduat_enc_pel_program_dag_decode_v1(artifact.bytes, &program)) { + fprintf(stderr, "error: invalid program encoding\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + normalized = amduat_octets(NULL, 0); + if (!amduat_enc_pel_program_dag_encode_v1(&program, &normalized)) { + fprintf(stderr, "error: failed to normalize program\n"); + amduat_enc_pel_program_dag_free(&program); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + if (output_format == AMDUAT_PEL_CLI_IO_RAW) { + if (!amduat_asl_write_path(output_path, + normalized.data, + normalized.len)) { + fprintf(stderr, "error: failed to write output\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + } + } else { + amduat_type_tag_t tag = + has_type_tag ? type_tag + : (artifact.has_type_tag + ? artifact.type_tag + : amduat_type_tag(AMDUAT_PEL_TYPE_TAG_PROGRAM_DAG_1)); + amduat_artifact_t out_artifact = + amduat_artifact_with_type(normalized, tag); + amduat_octets_t encoded; + if (!amduat_enc_asl1_core_encode_artifact_v1(out_artifact, &encoded)) { + fprintf(stderr, "error: failed to encode output artifact\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } else { + if (!amduat_asl_write_path(output_path, encoded.data, encoded.len)) { + fprintf(stderr, "error: failed to write output\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_IO; + } + free((void *)encoded.data); + } + } + + free((void *)normalized.data); + amduat_enc_pel_program_dag_free(&program); + amduat_pel_cli_free_artifact(&artifact); + return exit_code; +} + +static int amduat_pel_cli_cmd_program( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + if (argc < 1) { + fprintf(stderr, "error: program requires a subcommand\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "decode") == 0) { + return amduat_pel_cli_cmd_program_decode(argc - 1, argv + 1, global); + } + if (strcmp(argv[0], "normalize") == 0) { + return amduat_pel_cli_cmd_program_normalize(argc - 1, argv + 1, global); + } + fprintf(stderr, "error: unknown program subcommand: %s\n", argv[0]); + return AMDUAT_PEL_CLI_EXIT_USAGE; +} + +static int amduat_pel_cli_cmd_trace_decode( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *input_path = "-"; + const char *trace_ref_text = NULL; + bool input_path_set = false; + amduat_pel_cli_io_format_t input_format = AMDUAT_PEL_CLI_IO_RAW; + amduat_pel_cli_output_format_t format; + amduat_pel_cli_ref_format_t ref_format; + const char *root; + bool has_expect_type_tag = false; + amduat_type_tag_t expect_type_tag; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_reference_t trace_ref; + amduat_artifact_t artifact; + amduat_pel_trace_dag_value_t trace; + bool stdin_used = false; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + ref_format = global->ref_format; + root = global->root; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--input") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + input_path = argv[++i]; + input_path_set = true; + } else if (strcmp(argv[i], "--trace-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --trace-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + trace_ref_text = argv[++i]; + } else if (strcmp(argv[i], "--input-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_io_format(argv[++i], &input_format)) { + fprintf(stderr, "error: invalid input-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--expect-type-tag") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --expect-type-tag requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_asl_parse_type_tag(argv[++i], &expect_type_tag)) { + fprintf(stderr, "error: invalid expect-type-tag\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + has_expect_type_tag = true; + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + root = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (trace_ref_text != NULL && input_path_set) { + fprintf(stderr, "error: use --input or --trace-ref\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + memset(&artifact, 0, sizeof(artifact)); + if (trace_ref_text != NULL) { + if (!amduat_asl_store_fs_load_config(root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", root); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + if (!amduat_pel_cli_ref_from_text(trace_ref_text, + ref_format, + &stdin_used, + &trace_ref)) { + fprintf(stderr, "error: invalid trace-ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + { + amduat_asl_store_error_t err = + amduat_asl_store_get(&store, trace_ref, &artifact); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: store get failed: %s\n", + amduat_pel_cli_store_error_str(err)); + amduat_pel_cli_free_reference(&trace_ref); + return amduat_pel_cli_map_store_error(err); + } + } + amduat_pel_cli_free_reference(&trace_ref); + } else { + uint8_t *bytes = NULL; + size_t len = 0; + if (!amduat_pel_cli_read_path_once(input_path, + &stdin_used, + &bytes, + &len)) { + fprintf(stderr, "error: failed to read input\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + if (input_format == AMDUAT_PEL_CLI_IO_ARTIFACT) { + if (!amduat_enc_asl1_core_decode_artifact_v1( + amduat_octets(bytes, len), &artifact)) { + free(bytes); + fprintf(stderr, "error: invalid artifact encoding\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + free(bytes); + } else { + artifact = amduat_artifact(amduat_octets(bytes, len)); + } + } + + if (has_expect_type_tag) { + if (!artifact.has_type_tag || + artifact.type_tag.tag_id != expect_type_tag.tag_id) { + fprintf(stderr, "error: type-tag mismatch\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_UNSUPPORTED; + } + } + + memset(&trace, 0, sizeof(trace)); + if (!amduat_enc_pel_trace_dag_decode_v1(artifact.bytes, &trace)) { + fprintf(stderr, "error: invalid trace encoding\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + if (!amduat_pel_cli_print_trace_text(stdout, &trace, ref_format)) { + fprintf(stderr, "error: failed to format trace\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } + } else { + if (!amduat_pel_cli_print_trace_json(stdout, &trace, ref_format)) { + fprintf(stderr, "error: failed to format trace\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputc('\n', stdout); + } + + amduat_enc_pel_trace_dag_free(&trace); + amduat_pel_cli_free_artifact(&artifact); + return exit_code; +} + +static int amduat_pel_cli_cmd_trace_from_result( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *result_ref_text = NULL; + amduat_pel_cli_output_format_t format; + amduat_pel_cli_ref_format_t ref_format; + const char *root; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_reference_t result_ref; + amduat_artifact_t artifact; + amduat_pel_surface_execution_result_t result; + bool stdin_used = false; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + ref_format = global->ref_format; + root = global->root; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--result-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --result-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + result_ref_text = argv[++i]; + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + root = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (result_ref_text == NULL) { + fprintf(stderr, "error: --result-ref is required\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + if (!amduat_asl_store_fs_load_config(root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", root); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + if (!amduat_pel_cli_ref_from_text(result_ref_text, + ref_format, + &stdin_used, + &result_ref)) { + fprintf(stderr, "error: invalid result-ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + memset(&artifact, 0, sizeof(artifact)); + { + amduat_asl_store_error_t err = + amduat_asl_store_get(&store, result_ref, &artifact); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: store get failed: %s\n", + amduat_pel_cli_store_error_str(err)); + amduat_pel_cli_free_reference(&result_ref); + return amduat_pel_cli_map_store_error(err); + } + } + + memset(&result, 0, sizeof(result)); + if (!amduat_enc_pel1_result_decode_v1(artifact.bytes, &result)) { + fprintf(stderr, "error: invalid result encoding\n"); + amduat_pel_cli_free_reference(&result_ref); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + fprintf(stdout, "has_trace_ref=%s\n", + result.has_trace_ref ? "true" : "false"); + fprintf(stdout, "trace_ref="); + if (result.has_trace_ref) { + if (!amduat_pel_cli_print_ref_text(stdout, + result.trace_ref, + ref_format)) { + fprintf(stderr, "error: failed to encode trace ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } + } + fputc('\n', stdout); + } else { + fputs("{\"has_trace_ref\":", stdout); + fputs(result.has_trace_ref ? "true" : "false", stdout); + fputs(",\"trace_ref\":", stdout); + if (result.has_trace_ref) { + if (!amduat_pel_cli_print_ref_json(stdout, + result.trace_ref, + ref_format)) { + fprintf(stderr, "error: failed to encode trace ref\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } + } else { + fputs("null", stdout); + } + fputs("}\n", stdout); + } + + amduat_enc_pel1_result_free(&result); + amduat_pel_cli_free_reference(&result_ref); + amduat_pel_cli_free_artifact(&artifact); + return exit_code; +} + +static int amduat_pel_cli_cmd_trace( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + if (argc < 1) { + fprintf(stderr, "error: trace requires a subcommand\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "decode") == 0) { + return amduat_pel_cli_cmd_trace_decode(argc - 1, argv + 1, global); + } + if (strcmp(argv[0], "from-result") == 0) { + return amduat_pel_cli_cmd_trace_from_result(argc - 1, argv + 1, global); + } + fprintf(stderr, "error: unknown trace subcommand: %s\n", argv[0]); + return AMDUAT_PEL_CLI_EXIT_USAGE; +} + +static int amduat_pel_cli_cmd_result_decode( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *input_path = "-"; + const char *result_ref_text = NULL; + bool input_path_set = false; + amduat_pel_cli_io_format_t input_format = AMDUAT_PEL_CLI_IO_RAW; + amduat_pel_cli_output_format_t format; + amduat_pel_cli_ref_format_t ref_format; + const char *root; + bool has_expect_type_tag = false; + amduat_type_tag_t expect_type_tag; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_reference_t result_ref; + amduat_artifact_t artifact; + amduat_pel_surface_execution_result_t result; + bool stdin_used = false; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + ref_format = global->ref_format; + root = global->root; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--input") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + input_path = argv[++i]; + input_path_set = true; + } else if (strcmp(argv[i], "--result-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --result-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + result_ref_text = argv[++i]; + } else if (strcmp(argv[i], "--input-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_io_format(argv[++i], &input_format)) { + fprintf(stderr, "error: invalid input-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--expect-type-tag") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --expect-type-tag requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_asl_parse_type_tag(argv[++i], &expect_type_tag)) { + fprintf(stderr, "error: invalid expect-type-tag\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + has_expect_type_tag = true; + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + root = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (result_ref_text != NULL && input_path_set) { + fprintf(stderr, "error: use --input or --result-ref\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + memset(&artifact, 0, sizeof(artifact)); + if (result_ref_text != NULL) { + if (!amduat_asl_store_fs_load_config(root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", root); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + if (!amduat_pel_cli_ref_from_text(result_ref_text, + ref_format, + &stdin_used, + &result_ref)) { + fprintf(stderr, "error: invalid result-ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + { + amduat_asl_store_error_t err = + amduat_asl_store_get(&store, result_ref, &artifact); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: store get failed: %s\n", + amduat_pel_cli_store_error_str(err)); + amduat_pel_cli_free_reference(&result_ref); + return amduat_pel_cli_map_store_error(err); + } + } + amduat_pel_cli_free_reference(&result_ref); + } else { + uint8_t *bytes = NULL; + size_t len = 0; + if (!amduat_pel_cli_read_path_once(input_path, + &stdin_used, + &bytes, + &len)) { + fprintf(stderr, "error: failed to read input\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + if (input_format == AMDUAT_PEL_CLI_IO_ARTIFACT) { + if (!amduat_enc_asl1_core_decode_artifact_v1( + amduat_octets(bytes, len), &artifact)) { + free(bytes); + fprintf(stderr, "error: invalid artifact encoding\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + free(bytes); + } else { + artifact = amduat_artifact(amduat_octets(bytes, len)); + } + } + + if (has_expect_type_tag) { + if (!artifact.has_type_tag || + artifact.type_tag.tag_id != expect_type_tag.tag_id) { + fprintf(stderr, "error: type-tag mismatch\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_UNSUPPORTED; + } + } + + memset(&result, 0, sizeof(result)); + if (!amduat_enc_pel1_result_decode_v1(artifact.bytes, &result)) { + fprintf(stderr, "error: invalid result encoding\n"); + amduat_pel_cli_free_artifact(&artifact); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + if (!amduat_pel_cli_print_result_text(stdout, &result, ref_format)) { + fprintf(stderr, "error: failed to format result\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } + } else { + if (!amduat_pel_cli_print_result_json(stdout, &result, ref_format)) { + fprintf(stderr, "error: failed to format result\n"); + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputc('\n', stdout); + } + + amduat_enc_pel1_result_free(&result); + amduat_pel_cli_free_artifact(&artifact); + return exit_code; +} + +static int amduat_pel_cli_cmd_result( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + if (argc < 1) { + fprintf(stderr, "error: result requires a subcommand\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "decode") == 0) { + return amduat_pel_cli_cmd_result_decode(argc - 1, argv + 1, global); + } + fprintf(stderr, "error: unknown result subcommand: %s\n", argv[0]); + return AMDUAT_PEL_CLI_EXIT_USAGE; +} + +static const struct { + const char *name; + uint32_t version; +} amduat_pel_cli_ops[] = { + {AMDUAT_PEL_KERNEL_OP_CONCAT_NAME, 1}, + {AMDUAT_PEL_KERNEL_OP_SLICE_NAME, 1}, + {AMDUAT_PEL_KERNEL_OP_CONST_NAME, 1}, + {AMDUAT_PEL_KERNEL_OP_HASH_ASL1_NAME, 1} +}; + +static const char *amduat_pel_cli_kernel_kind_name( + amduat_pel_kernel_op_kind_t kind) { + switch (kind) { + case AMDUAT_PEL_KERNEL_OP_CONCAT: + return "concat"; + case AMDUAT_PEL_KERNEL_OP_SLICE: + return "slice"; + case AMDUAT_PEL_KERNEL_OP_CONST: + return "const"; + case AMDUAT_PEL_KERNEL_OP_HASH_ASL1: + return "hash_asl1"; + default: + return "unknown"; + } +} + +static int amduat_pel_cli_cmd_op_list( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + amduat_pel_cli_output_format_t format; + size_t i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + + for (i = 0; i < (size_t)argc; ++i) { + if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= (size_t)argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + for (i = 0; i < sizeof(amduat_pel_cli_ops) / + sizeof(amduat_pel_cli_ops[0]); ++i) { + fprintf(stdout, "name=%s version=%u\n", + amduat_pel_cli_ops[i].name, + amduat_pel_cli_ops[i].version); + } + } else { + fputs("{\"ops\":[", stdout); + for (i = 0; i < sizeof(amduat_pel_cli_ops) / + sizeof(amduat_pel_cli_ops[0]); ++i) { + if (i != 0) { + fputc(',', stdout); + } + fprintf(stdout, "{\"name\":"); + amduat_pel_cli_json_escape( + stdout, + (const uint8_t *)amduat_pel_cli_ops[i].name, + strlen(amduat_pel_cli_ops[i].name)); + fprintf(stdout, ",\"version\":%u}", amduat_pel_cli_ops[i].version); + } + fputs("]}\n", stdout); + } + + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_op_describe( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *op_text = NULL; + const char *op_name = NULL; + uint32_t op_version = 1; + amduat_pel_cli_output_format_t format; + const amduat_pel_kernel_op_desc_t *desc; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else if (op_text == NULL) { + op_text = argv[i]; + } else { + fprintf(stderr, "error: unexpected argument: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (op_text == NULL) { + fprintf(stderr, "error: NAME[=VERSION] is required\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + if (!amduat_pel_cli_parse_op_name(op_text, &op_name, &op_version)) { + fprintf(stderr, "error: invalid op name\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + desc = amduat_pel_kernel_op_lookup( + amduat_octets(op_name, strlen(op_name)), op_version); + if (desc == NULL) { + fprintf(stderr, "error: op not found\n"); + amduat_pel_cli_free_op_name(op_name, op_text); + return AMDUAT_PEL_CLI_EXIT_NOT_FOUND; + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + fprintf(stdout, "name=%s\n", op_name); + fprintf(stdout, "version=%u\n", op_version); + fprintf(stdout, "kind=%s\n", amduat_pel_cli_kernel_kind_name(desc->kind)); + fprintf(stdout, "kernel_op_code=0x%04x\n", desc->kernel_op_code); + fprintf(stdout, "min_inputs=%zu\n", desc->min_inputs); + fprintf(stdout, "max_inputs=%zu\n", desc->max_inputs); + fprintf(stdout, "outputs_len=%zu\n", desc->outputs_len); + } else { + fputs("{\"name\":", stdout); + amduat_pel_cli_json_escape(stdout, + (const uint8_t *)op_name, + strlen(op_name)); + fprintf(stdout, ",\"version\":%u", op_version); + fputs(",\"kind\":", stdout); + amduat_pel_cli_json_escape( + stdout, + (const uint8_t *)amduat_pel_cli_kernel_kind_name(desc->kind), + strlen(amduat_pel_cli_kernel_kind_name(desc->kind))); + fprintf(stdout, ",\"kernel_op_code\":%u", desc->kernel_op_code); + fprintf(stdout, ",\"min_inputs\":%zu", desc->min_inputs); + fprintf(stdout, ",\"max_inputs\":%zu", desc->max_inputs); + fprintf(stdout, ",\"outputs_len\":%zu}\n", desc->outputs_len); + } + + amduat_pel_cli_free_op_name(op_name, op_text); + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_op_params_decode( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *op_text = NULL; + const char *op_name = NULL; + uint32_t op_version = 1; + const char *input_path = NULL; + amduat_pel_cli_output_format_t format; + const amduat_pel_kernel_op_desc_t *desc; + amduat_pel_kernel_params_t params; + uint8_t *bytes = NULL; + size_t len = 0; + bool stdin_used = false; + int exit_code = AMDUAT_PEL_CLI_EXIT_OK; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--op") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --op requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + op_text = argv[++i]; + } else if (strcmp(argv[i], "--input") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + input_path = argv[++i]; + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (op_text == NULL || input_path == NULL) { + fprintf(stderr, "error: --op and --input are required\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + if (!amduat_pel_cli_parse_op_name(op_text, &op_name, &op_version)) { + fprintf(stderr, "error: invalid op name\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + desc = amduat_pel_kernel_op_lookup( + amduat_octets(op_name, strlen(op_name)), op_version); + if (desc == NULL) { + fprintf(stderr, "error: op not found\n"); + amduat_pel_cli_free_op_name(op_name, op_text); + return AMDUAT_PEL_CLI_EXIT_NOT_FOUND; + } + + if (!amduat_pel_cli_read_path_once(input_path, + &stdin_used, + &bytes, + &len)) { + fprintf(stderr, "error: failed to read input\n"); + amduat_pel_cli_free_op_name(op_name, op_text); + return AMDUAT_PEL_CLI_EXIT_IO; + } + + if (!amduat_pel_kernel_params_decode( + desc, amduat_octets(bytes, len), ¶ms)) { + fprintf(stderr, "error: failed to decode params\n"); + amduat_pel_cli_free_op_name(op_name, op_text); + free(bytes); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + fprintf(stdout, "op=%s\n", op_name); + fprintf(stdout, "version=%u\n", op_version); + fprintf(stdout, "kind=%s\n", amduat_pel_cli_kernel_kind_name(params.kind)); + switch (params.kind) { + case AMDUAT_PEL_KERNEL_OP_CONCAT: + fprintf(stdout, "params=unit\n"); + break; + case AMDUAT_PEL_KERNEL_OP_SLICE: + fprintf(stdout, "offset=%" PRIu64 "\n", + (uint64_t)params.value.slice.offset); + fprintf(stdout, "length=%" PRIu64 "\n", + (uint64_t)params.value.slice.length); + break; + case AMDUAT_PEL_KERNEL_OP_CONST: { + char *hex = amduat_pel_cli_hex_encode(params.value.konst.bytes); + if (hex == NULL) { + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + break; + } + fprintf(stdout, "has_type_tag=%s\n", + params.value.konst.has_type_tag ? "true" : "false"); + if (params.value.konst.has_type_tag) { + fprintf(stdout, "type_tag=0x%08x\n", + (unsigned int)params.value.konst.tag_id); + } + fprintf(stdout, "bytes_hex=%s\n", hex); + free(hex); + break; + } + case AMDUAT_PEL_KERNEL_OP_HASH_ASL1: { + const amduat_hash_asl1_desc_t *desc_hash = + amduat_hash_asl1_desc_lookup(params.value.hash.hash_id); + fprintf(stdout, "hash_id=0x%04x\n", + (unsigned int)params.value.hash.hash_id); + if (desc_hash != NULL && desc_hash->name != NULL) { + fprintf(stdout, "hash_name=%s\n", desc_hash->name); + } + break; + } + default: + break; + } + } else { + fputs("{\"op\":", stdout); + amduat_pel_cli_json_escape(stdout, + (const uint8_t *)op_name, + strlen(op_name)); + fprintf(stdout, ",\"version\":%u", op_version); + fputs(",\"kind\":", stdout); + amduat_pel_cli_json_escape( + stdout, + (const uint8_t *)amduat_pel_cli_kernel_kind_name(params.kind), + strlen(amduat_pel_cli_kernel_kind_name(params.kind))); + switch (params.kind) { + case AMDUAT_PEL_KERNEL_OP_CONCAT: + fputs(",\"params\":null", stdout); + break; + case AMDUAT_PEL_KERNEL_OP_SLICE: + fprintf(stdout, ",\"offset\":%" PRIu64, + (uint64_t)params.value.slice.offset); + fprintf(stdout, ",\"length\":%" PRIu64, + (uint64_t)params.value.slice.length); + break; + case AMDUAT_PEL_KERNEL_OP_CONST: { + char *hex = amduat_pel_cli_hex_encode(params.value.konst.bytes); + if (hex == NULL) { + exit_code = AMDUAT_PEL_CLI_EXIT_CODEC; + break; + } + fputs(",\"has_type_tag\":", stdout); + fputs(params.value.konst.has_type_tag ? "true" : "false", stdout); + if (params.value.konst.has_type_tag) { + fprintf(stdout, ",\"type_tag\":%u", params.value.konst.tag_id); + } else { + fputs(",\"type_tag\":null", stdout); + } + fputs(",\"bytes_hex\":", stdout); + amduat_pel_cli_json_escape(stdout, + (const uint8_t *)hex, + strlen(hex)); + free(hex); + break; + } + case AMDUAT_PEL_KERNEL_OP_HASH_ASL1: { + const amduat_hash_asl1_desc_t *desc_hash = + amduat_hash_asl1_desc_lookup(params.value.hash.hash_id); + fprintf(stdout, ",\"hash_id\":%u", + (unsigned int)params.value.hash.hash_id); + fputs(",\"hash_name\":", stdout); + if (desc_hash != NULL && desc_hash->name != NULL) { + amduat_pel_cli_json_escape( + stdout, + (const uint8_t *)desc_hash->name, + strlen(desc_hash->name)); + } else { + fputs("null", stdout); + } + break; + } + default: + break; + } + fputs("}\n", stdout); + } + + free(bytes); + amduat_pel_cli_free_op_name(op_name, op_text); + return exit_code; +} + +static int amduat_pel_cli_cmd_op( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + if (argc < 1) { + fprintf(stderr, "error: op requires a subcommand\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "list") == 0) { + return amduat_pel_cli_cmd_op_list(argc - 1, argv + 1, global); + } + if (strcmp(argv[0], "describe") == 0) { + return amduat_pel_cli_cmd_op_describe(argc - 1, argv + 1, global); + } + if (strcmp(argv[0], "params-decode") == 0) { + return amduat_pel_cli_cmd_op_params_decode(argc - 1, argv + 1, global); + } + fprintf(stderr, "error: unknown op subcommand: %s\n", argv[0]); + return AMDUAT_PEL_CLI_EXIT_USAGE; +} + +static int amduat_pel_cli_cmd_scheme_show( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + amduat_pel_cli_output_format_t format; + amduat_pel_cli_ref_format_t ref_format; + amduat_reference_t scheme_ref; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + format = global->format; + ref_format = global->ref_format; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + scheme_ref = amduat_pel_program_dag_scheme_ref(); + + if (format == AMDUAT_PEL_CLI_FORMAT_TEXT) { + fprintf(stdout, "scheme_ref="); + if (!amduat_pel_cli_print_ref_text(stdout, scheme_ref, ref_format)) { + fprintf(stderr, "error: failed to encode scheme ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputc('\n', stdout); + fprintf(stdout, "program_type_tag=0x%08x\n", + AMDUAT_PEL_TYPE_TAG_PROGRAM_DAG_1); + fprintf(stdout, "trace_type_tag=0x%08x\n", + AMDUAT_TYPE_TAG_PEL_TRACE_DAG_1); + fprintf(stdout, "result_type_tag=0x%08x\n", + AMDUAT_TYPE_TAG_PEL1_RESULT_1); + fprintf(stdout, "program_profile_id=0x%04x\n", + AMDUAT_PEL_ENC_PROGRAM_DAG_V1); + fprintf(stdout, "trace_profile_id=0x%04x\n", + AMDUAT_PEL_ENC_TRACE_DAG_V1); + fprintf(stdout, "result_profile_id=0x%04x\n", + AMDUAT_PEL_ENC_EXECUTION_RESULT_V1); + } else { + fputs("{\"scheme_ref\":", stdout); + if (!amduat_pel_cli_print_ref_json(stdout, scheme_ref, ref_format)) { + fprintf(stderr, "error: failed to encode scheme ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + fprintf(stdout, ",\"program_type_tag\":%u", + AMDUAT_PEL_TYPE_TAG_PROGRAM_DAG_1); + fprintf(stdout, ",\"trace_type_tag\":%u", + AMDUAT_TYPE_TAG_PEL_TRACE_DAG_1); + fprintf(stdout, ",\"result_type_tag\":%u", + AMDUAT_TYPE_TAG_PEL1_RESULT_1); + fprintf(stdout, ",\"program_profile_id\":%u", + AMDUAT_PEL_ENC_PROGRAM_DAG_V1); + fprintf(stdout, ",\"trace_profile_id\":%u", + AMDUAT_PEL_ENC_TRACE_DAG_V1); + fprintf(stdout, ",\"result_profile_id\":%u}\n", + AMDUAT_PEL_ENC_EXECUTION_RESULT_V1); + } + + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_scheme_dag_ref(int argc, char **argv) { + amduat_pel_cli_ref_format_t fmt = AMDUAT_PEL_CLI_REF_HEX; + amduat_reference_t scheme_ref; + int i; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &fmt)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + scheme_ref = amduat_pel_program_dag_scheme_ref(); + if (fmt == AMDUAT_PEL_CLI_REF_HEX) { + char *text = NULL; + if (!amduat_asl_ref_encode_hex(scheme_ref, &text)) { + fprintf(stderr, "error: failed to encode scheme ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputs(text, stdout); + fputc('\n', stdout); + free(text); + } else { + amduat_octets_t encoded; + if (!amduat_enc_asl1_core_encode_reference_v1(scheme_ref, &encoded)) { + fprintf(stderr, "error: failed to encode scheme ref\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + if (!amduat_asl_write_stream(stdout, encoded.data, encoded.len)) { + fprintf(stderr, "error: failed to write scheme ref\n"); + free((void *)encoded.data); + return AMDUAT_PEL_CLI_EXIT_IO; + } + free((void *)encoded.data); + } + + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_scheme( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + if (argc < 1) { + fprintf(stderr, "error: scheme requires a subcommand\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "show") == 0) { + return amduat_pel_cli_cmd_scheme_show(argc - 1, argv + 1, global); + } + if (strcmp(argv[0], "dag-ref") == 0) { + return amduat_pel_cli_cmd_scheme_dag_ref(argc - 1, argv + 1); + } + fprintf(stderr, "error: unknown scheme subcommand: %s\n", argv[0]); + return AMDUAT_PEL_CLI_EXIT_USAGE; +} + +static int amduat_pel_cli_cmd_help(int argc, char **argv) { + (void)argc; + (void)argv; + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; +} + +int main(int argc, char **argv) { + amduat_pel_cli_global_opts_t global; + const char *command = NULL; + int i; + + memset(&global, 0, sizeof(global)); + global.root = AMDUAT_PEL_CLI_DEFAULT_ROOT; + global.ref_format = AMDUAT_PEL_CLI_REF_HEX; + global.format = AMDUAT_PEL_CLI_FORMAT_TEXT; + global.quiet = false; + + for (i = 1; i < argc; ++i) { + if (argv[i][0] != '-') { + command = argv[i]; + ++i; + break; + } + if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + global.root = argv[++i]; + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_ref_format(argv[++i], &global.ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --format requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_parse_output_format(argv[++i], &global.format)) { + fprintf(stderr, "error: invalid format\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--quiet") == 0) { + global.quiet = true; + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (command == NULL) { + fprintf(stderr, "error: command required\n"); + amduat_pel_cli_print_usage(stderr); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + if (strcmp(command, "run") == 0) { + return amduat_pel_cli_cmd_run(argc - i, argv + i, &global); + } + if (strcmp(command, "exec") == 0) { + return amduat_pel_cli_cmd_exec(argc - i, argv + i, &global); + } + if (strcmp(command, "validate") == 0) { + return amduat_pel_cli_cmd_validate(argc - i, argv + i, &global); + } + if (strcmp(command, "program") == 0) { + return amduat_pel_cli_cmd_program(argc - i, argv + i, &global); + } + if (strcmp(command, "trace") == 0) { + return amduat_pel_cli_cmd_trace(argc - i, argv + i, &global); + } + if (strcmp(command, "result") == 0) { + return amduat_pel_cli_cmd_result(argc - i, argv + i, &global); + } + if (strcmp(command, "op") == 0) { + return amduat_pel_cli_cmd_op(argc - i, argv + i, &global); + } + if (strcmp(command, "scheme") == 0) { + return amduat_pel_cli_cmd_scheme(argc - i, argv + i, &global); + } + if (strcmp(command, "help") == 0) { + return amduat_pel_cli_cmd_help(argc - i, argv + i); + } + + fprintf(stderr, "error: unknown command: %s\n", command); + amduat_pel_cli_print_usage(stderr); + return AMDUAT_PEL_CLI_EXIT_USAGE; +}