From f406e1d10a11ddd098c12302addda94e566d8331 Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Sat, 20 Dec 2025 08:40:57 +0100 Subject: [PATCH] Add amduat-asl CLI skeleton --- CMakeLists.txt | 10 + src/tools/amduat_asl_cli.c | 1046 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1056 insertions(+) create mode 100644 src/tools/amduat_asl_cli.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 78cbbe2..e9eca1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,3 +134,13 @@ amduat_link(tgk_store_mem amduat_tgk amduat_asl amduat_enc amduat_hash_asl1 amdu amduat_add_lib(tgk_store_fs SRCS ${AMDUAT_TGK_STORE_FS_SRCS}) amduat_link(tgk_store_fs amduat_tgk amduat_asl_store_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util) + +add_executable(amduat_asl_cli src/tools/amduat_asl_cli.c) +target_include_directories(amduat_asl_cli + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_asl_cli + PRIVATE amduat_asl_store_fs amduat_enc amduat_hash_asl1 amduat_util +) +set_target_properties(amduat_asl_cli PROPERTIES OUTPUT_NAME amduat-asl) diff --git a/src/tools/amduat_asl_cli.c b/src/tools/amduat_asl_cli.c new file mode 100644 index 0000000..c86c951 --- /dev/null +++ b/src/tools/amduat_asl_cli.c @@ -0,0 +1,1046 @@ +#include "amduat/asl/asl_store_fs.h" +#include "amduat/asl/asl_store_fs_meta.h" +#include "amduat/asl/store.h" +#include "amduat/enc/asl1_core.h" +#include "amduat/enc/asl1_core_codec.h" +#include "amduat/hash/asl1.h" +#include "amduat/util/hex.h" + +#include +#include +#include +#include +#include +#include + +enum { + AMDUAT_ASL_CLI_EXIT_OK = 0, + AMDUAT_ASL_CLI_EXIT_USAGE = 2, + AMDUAT_ASL_CLI_EXIT_IO = 3, + AMDUAT_ASL_CLI_EXIT_NOT_FOUND = 4, + AMDUAT_ASL_CLI_EXIT_STORE = 5, + AMDUAT_ASL_CLI_EXIT_UNSUPPORTED = 6, + AMDUAT_ASL_CLI_EXIT_CODEC = 7, + AMDUAT_ASL_CLI_EXIT_CONFIG = 8 +}; + +enum { + AMDUAT_ASL_CLI_STORE_ID_MAX = AMDUAT_ASL_STORE_FS_STORE_ID_MAX, + AMDUAT_ASL_CLI_BUF_CHUNK = 4096 +}; + +static const char *const AMDUAT_ASL_CLI_DEFAULT_ROOT = ".amduat-asl"; + +typedef enum { + AMDUAT_ASL_CLI_REF_HEX = 0, + AMDUAT_ASL_CLI_REF_BYTES = 1 +} amduat_asl_cli_ref_format_t; + +typedef enum { + AMDUAT_ASL_CLI_IO_RAW = 0, + AMDUAT_ASL_CLI_IO_ARTIFACT = 1 +} amduat_asl_cli_io_format_t; + +typedef struct { + const char *root; + const char *store_id; + const char *profile; + const char *hash; + bool force; +} amduat_asl_cli_init_opts_t; + +typedef struct { + const char *root; + const char *input_path; + const char *output_path; + amduat_asl_cli_io_format_t input_format; + amduat_asl_cli_ref_format_t ref_format; + bool has_type_tag; + amduat_type_tag_t type_tag; +} amduat_asl_cli_put_opts_t; + +typedef struct { + const char *root; + const char *ref; + const char *output_path; + amduat_asl_cli_io_format_t output_format; + amduat_asl_cli_ref_format_t ref_format; + bool has_expect_type_tag; + amduat_type_tag_t expect_type_tag; + bool print_type_tag; +} amduat_asl_cli_get_opts_t; + +static void amduat_asl_cli_print_usage(FILE *stream) { + fprintf(stream, + "usage:\n" + " amduat-asl init [--root PATH] [--store-id ID]\n" + " [--profile PROFILE_ID|name]\n" + " [--hash HASH_ID|name] [--force]\n" + " amduat-asl put [--root PATH] [--type-tag TAG]\n" + " [--input PATH|-] [--input-format raw|artifact]\n" + " [--ref-format hex|bytes] [--output PATH|-]\n" + " amduat-asl get --ref REF [--root PATH]\n" + " [--ref-format hex|bytes] [--output PATH|-]\n" + " [--output-format raw|artifact]\n" + " [--expect-type-tag TAG] [--print-type-tag]\n" + "\n" + "defaults:\n" + " --root %s\n" + " --input -\n" + " --output -\n" + " --input-format raw\n" + " --output-format raw\n" + " --ref-format hex\n" + "\n" + "notes:\n" + " --ref-format bytes expects --ref as a path or -\n", + AMDUAT_ASL_CLI_DEFAULT_ROOT); +} + +static bool amduat_asl_cli_is_store_id_char(char c) { + if (c >= 'a' && c <= 'z') { + return true; + } + if (c >= 'A' && c <= 'Z') { + return true; + } + if (c >= '0' && c <= '9') { + return true; + } + return c == '.' || c == '_' || c == '-'; +} + +static bool amduat_asl_cli_validate_store_id(const char *store_id) { + size_t i; + + if (store_id == NULL || store_id[0] == '\0') { + return false; + } + for (i = 0; store_id[i] != '\0'; ++i) { + if (i >= AMDUAT_ASL_CLI_STORE_ID_MAX) { + return false; + } + if (!amduat_asl_cli_is_store_id_char(store_id[i])) { + return false; + } + } + return true; +} + +static bool amduat_asl_cli_copy_store_id( + char out_id[AMDUAT_ASL_CLI_STORE_ID_MAX + 1], + const char *store_id) { + size_t len; + + if (out_id == NULL || store_id == NULL) { + return false; + } + if (!amduat_asl_cli_validate_store_id(store_id)) { + return false; + } + len = strlen(store_id); + if (len > AMDUAT_ASL_CLI_STORE_ID_MAX) { + return false; + } + memcpy(out_id, store_id, len); + out_id[len] = '\0'; + return true; +} + +static bool amduat_asl_cli_parse_u16(const char *text, uint16_t *out) { + char *end; + unsigned long value; + + if (text == NULL || text[0] == '\0' || out == NULL) { + return false; + } + + errno = 0; + value = strtoul(text, &end, 0); + if (errno != 0 || end == text || *end != '\0' || value > UINT16_MAX) { + return false; + } + *out = (uint16_t)value; + return true; +} + +static bool amduat_asl_cli_parse_u32(const char *text, uint32_t *out) { + char *end; + unsigned long value; + + if (text == NULL || text[0] == '\0' || out == NULL) { + return false; + } + + errno = 0; + value = strtoul(text, &end, 0); + if (errno != 0 || end == text || *end != '\0' || value > UINT32_MAX) { + return false; + } + *out = (uint32_t)value; + return true; +} + +static bool amduat_asl_cli_parse_profile_id( + const char *text, + amduat_asl_encoding_profile_id_t *out_id) { + uint16_t id; + size_t i; + size_t count; + const amduat_enc_asl1_core_profile_desc_t *descs; + + if (text == NULL || out_id == NULL) { + return false; + } + + if (amduat_asl_cli_parse_u16(text, &id)) { + if (amduat_enc_asl1_core_desc_lookup(id) == NULL) { + return false; + } + *out_id = id; + return true; + } + + descs = amduat_enc_asl1_core_descs(&count); + for (i = 0; i < count; ++i) { + if (descs[i].name != NULL && strcmp(descs[i].name, text) == 0) { + *out_id = descs[i].profile_id; + return true; + } + } + + return false; +} + +static bool amduat_asl_cli_parse_hash_id(const char *text, + amduat_hash_id_t *out_id) { + uint16_t id; + size_t i; + size_t count; + const amduat_hash_asl1_desc_t *descs; + + if (text == NULL || out_id == NULL) { + return false; + } + + if (amduat_asl_cli_parse_u16(text, &id)) { + if (amduat_hash_asl1_desc_lookup(id) == NULL) { + return false; + } + *out_id = id; + return true; + } + + descs = amduat_hash_asl1_descs(&count); + for (i = 0; i < count; ++i) { + if (descs[i].name != NULL && strcmp(descs[i].name, text) == 0) { + *out_id = descs[i].hash_id; + return true; + } + } + + return false; +} + +static bool amduat_asl_cli_parse_type_tag(const char *text, + amduat_type_tag_t *out_tag) { + uint32_t tag_id; + + if (text == NULL || out_tag == NULL) { + return false; + } + if (!amduat_asl_cli_parse_u32(text, &tag_id)) { + return false; + } + *out_tag = amduat_type_tag(tag_id); + return true; +} + +static bool amduat_asl_cli_parse_ref_format( + const char *text, + amduat_asl_cli_ref_format_t *out_fmt) { + if (text == NULL || out_fmt == NULL) { + return false; + } + if (strcmp(text, "hex") == 0) { + *out_fmt = AMDUAT_ASL_CLI_REF_HEX; + return true; + } + if (strcmp(text, "bytes") == 0) { + *out_fmt = AMDUAT_ASL_CLI_REF_BYTES; + return true; + } + return false; +} + +static bool amduat_asl_cli_parse_io_format( + const char *text, + amduat_asl_cli_io_format_t *out_fmt) { + if (text == NULL || out_fmt == NULL) { + return false; + } + if (strcmp(text, "raw") == 0) { + *out_fmt = AMDUAT_ASL_CLI_IO_RAW; + return true; + } + if (strcmp(text, "artifact") == 0) { + *out_fmt = AMDUAT_ASL_CLI_IO_ARTIFACT; + return true; + } + return false; +} + +static bool amduat_asl_cli_read_stream(FILE *stream, + uint8_t **out, + size_t *out_len) { + uint8_t *buffer; + size_t cap; + size_t len; + + if (out == NULL || out_len == NULL) { + return false; + } + *out = NULL; + *out_len = 0; + + buffer = NULL; + cap = 0; + len = 0; + + while (true) { + if (len == cap) { + size_t new_cap = cap == 0 ? AMDUAT_ASL_CLI_BUF_CHUNK : cap * 2u; + uint8_t *next; + if (new_cap < cap) { + free(buffer); + return false; + } + next = (uint8_t *)realloc(buffer, new_cap); + if (next == NULL) { + free(buffer); + return false; + } + buffer = next; + cap = new_cap; + } + + size_t nread = fread(buffer + len, 1u, cap - len, stream); + len += nread; + if (nread == 0u) { + if (ferror(stream)) { + free(buffer); + return false; + } + break; + } + } + + if (len == 0u) { + free(buffer); + buffer = NULL; + } + *out = buffer; + *out_len = len; + return true; +} + +static bool amduat_asl_cli_read_path(const char *path, + uint8_t **out, + size_t *out_len) { + FILE *stream; + bool ok; + + if (path == NULL || out == NULL || out_len == NULL) { + return false; + } + + if (strcmp(path, "-") == 0) { + return amduat_asl_cli_read_stream(stdin, out, out_len); + } + + stream = fopen(path, "rb"); + if (stream == NULL) { + return false; + } + ok = amduat_asl_cli_read_stream(stream, out, out_len); + if (fclose(stream) != 0) { + ok = false; + } + return ok; +} + +static bool amduat_asl_cli_write_stream(FILE *stream, + const uint8_t *bytes, + size_t len) { + size_t offset; + + if (len != 0u && bytes == NULL) { + return false; + } + + offset = 0u; + while (offset < len) { + size_t written = fwrite(bytes + offset, 1u, len - offset, stream); + if (written == 0u) { + return false; + } + offset += written; + } + return fflush(stream) == 0; +} + +static bool amduat_asl_cli_write_path(const char *path, + const uint8_t *bytes, + size_t len) { + FILE *stream; + bool ok; + + if (path == NULL) { + return false; + } + if (strcmp(path, "-") == 0) { + return amduat_asl_cli_write_stream(stdout, bytes, len); + } + + stream = fopen(path, "wb"); + if (stream == NULL) { + return false; + } + ok = amduat_asl_cli_write_stream(stream, bytes, len); + if (fclose(stream) != 0) { + ok = false; + } + return ok; +} + +static bool amduat_asl_cli_write_text_line(const char *path, + const char *text) { + FILE *stream; + bool ok; + + if (path == NULL || text == NULL) { + return false; + } + if (strcmp(path, "-") == 0) { + stream = stdout; + } else { + stream = fopen(path, "wb"); + if (stream == NULL) { + return false; + } + } + + ok = fputs(text, stream) >= 0 && fputc('\n', stream) != EOF && + fflush(stream) == 0; + if (stream != stdout && fclose(stream) != 0) { + ok = false; + } + return ok; +} + +static bool amduat_asl_cli_encode_reference_hex(amduat_reference_t ref, + char **out_hex) { + amduat_octets_t bytes; + char *hex; + size_t hex_size; + + if (out_hex == NULL) { + return false; + } + *out_hex = NULL; + + if (!amduat_enc_asl1_core_encode_reference_v1(ref, &bytes)) { + return false; + } + hex_size = amduat_hex_encoded_size(bytes.len); + if (hex_size == 0u) { + free((void *)bytes.data); + return false; + } + hex = (char *)malloc(hex_size); + if (hex == NULL) { + free((void *)bytes.data); + return false; + } + if (!amduat_hex_encode_lower(bytes.data, bytes.len, hex, hex_size)) { + free(hex); + free((void *)bytes.data); + return false; + } + free((void *)bytes.data); + *out_hex = hex; + return true; +} + +static bool amduat_asl_cli_decode_reference_hex(const char *hex, + amduat_reference_t *out_ref) { + uint8_t *bytes; + size_t len; + const char *hex_text; + bool ok; + + if (hex == NULL || out_ref == NULL) { + return false; + } + + hex_text = hex; + if (hex_text[0] == '0' && (hex_text[1] == 'x' || hex_text[1] == 'X')) { + hex_text += 2; + } + + bytes = NULL; + len = 0; + if (!amduat_hex_decode_alloc(hex_text, &bytes, &len)) { + return false; + } + + ok = amduat_enc_asl1_core_decode_reference_v1( + amduat_octets(bytes, len), + out_ref); + free(bytes); + return ok; +} + +static bool amduat_asl_cli_decode_reference_bytes(const uint8_t *bytes, + size_t len, + amduat_reference_t *out_ref) { + if (out_ref == NULL || (len != 0u && bytes == NULL)) { + return false; + } + return amduat_enc_asl1_core_decode_reference_v1( + amduat_octets(bytes, len), + out_ref); +} + +static void amduat_asl_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_asl_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_asl_cli_print_store_meta( + const amduat_asl_store_fs_config_t *cfg) { + if (cfg == NULL) { + return; + } + fprintf(stderr, "store_id=%s\n", cfg->store_id); + fprintf(stderr, "profile=0x%04x\n", + (unsigned int)cfg->config.encoding_profile_id); + fprintf(stderr, "hash=0x%04x\n", (unsigned int)cfg->config.hash_id); +} + +static const char *amduat_asl_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_asl_cli_map_store_error(amduat_asl_store_error_t err) { + switch (err) { + case AMDUAT_ASL_STORE_ERR_NOT_FOUND: + return AMDUAT_ASL_CLI_EXIT_NOT_FOUND; + case AMDUAT_ASL_STORE_ERR_UNSUPPORTED: + return AMDUAT_ASL_CLI_EXIT_UNSUPPORTED; + case AMDUAT_ASL_STORE_ERR_INTEGRITY: + return AMDUAT_ASL_CLI_EXIT_STORE; + case AMDUAT_ASL_STORE_OK: + default: + return AMDUAT_ASL_CLI_EXIT_STORE; + } +} + +static int amduat_asl_cli_cmd_init(int argc, char **argv) { + amduat_asl_cli_init_opts_t opts; + amduat_asl_store_fs_config_t cfg_in; + amduat_asl_store_fs_config_t cfg_out; + bool ok; + int i; + + memset(&opts, 0, sizeof(opts)); + opts.root = AMDUAT_ASL_CLI_DEFAULT_ROOT; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.root = argv[++i]; + } else if (strcmp(argv[i], "--store-id") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --store-id requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.store_id = argv[++i]; + } else if (strcmp(argv[i], "--profile") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --profile requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.profile = argv[++i]; + } else if (strcmp(argv[i], "--hash") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --hash requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.hash = argv[++i]; + } else if (strcmp(argv[i], "--force") == 0) { + opts.force = true; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_asl_cli_print_usage(stdout); + return AMDUAT_ASL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } + + memset(&cfg_in, 0, sizeof(cfg_in)); + if (opts.store_id != NULL) { + if (!amduat_asl_cli_copy_store_id(cfg_in.store_id, opts.store_id)) { + fprintf(stderr, "error: invalid store-id\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } + if (opts.profile != NULL) { + if (!amduat_asl_cli_parse_profile_id( + opts.profile, + &cfg_in.config.encoding_profile_id)) { + fprintf(stderr, "error: unknown profile: %s\n", opts.profile); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } + if (opts.hash != NULL) { + if (!amduat_asl_cli_parse_hash_id(opts.hash, &cfg_in.config.hash_id)) { + fprintf(stderr, "error: unknown hash: %s\n", opts.hash); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } + + ok = amduat_asl_store_fs_init_root(opts.root, &cfg_in, &cfg_out); + if (!ok && opts.force) { + ok = amduat_asl_store_fs_init_root(opts.root, NULL, &cfg_out); + if (ok && (opts.store_id || opts.profile || opts.hash)) { + fprintf(stderr, + "warning: existing config kept; overrides ignored\n"); + } + } + if (!ok) { + fprintf(stderr, "error: failed to initialize store root: %s\n", + opts.root); + return AMDUAT_ASL_CLI_EXIT_CONFIG; + } + + fprintf(stderr, "root=%s\n", opts.root); + amduat_asl_cli_print_store_meta(&cfg_out); + return AMDUAT_ASL_CLI_EXIT_OK; +} + +static int amduat_asl_cli_cmd_put(int argc, char **argv) { + amduat_asl_cli_put_opts_t opts; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_artifact_t artifact; + amduat_reference_t ref; + uint8_t *input_bytes; + size_t input_len; + amduat_octets_t encoded_ref; + bool ok; + int i; + int exit_code; + + memset(&opts, 0, sizeof(opts)); + opts.root = AMDUAT_ASL_CLI_DEFAULT_ROOT; + opts.input_path = "-"; + opts.output_path = "-"; + opts.input_format = AMDUAT_ASL_CLI_IO_RAW; + opts.ref_format = AMDUAT_ASL_CLI_REF_HEX; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.root = argv[++i]; + } else if (strcmp(argv[i], "--type-tag") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --type-tag requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + if (!amduat_asl_cli_parse_type_tag(argv[++i], &opts.type_tag)) { + fprintf(stderr, "error: invalid type-tag\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.has_type_tag = true; + } else if (strcmp(argv[i], "--input") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path or -\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.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_ASL_CLI_EXIT_USAGE; + } + if (!amduat_asl_cli_parse_io_format(argv[++i], &opts.input_format)) { + fprintf(stderr, "error: invalid input-format\n"); + return AMDUAT_ASL_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_ASL_CLI_EXIT_USAGE; + } + if (!amduat_asl_cli_parse_ref_format(argv[++i], &opts.ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--output") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output requires a path or -\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.output_path = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_asl_cli_print_usage(stdout); + return AMDUAT_ASL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } + + if (opts.input_format == AMDUAT_ASL_CLI_IO_ARTIFACT && opts.has_type_tag) { + fprintf(stderr, + "error: --type-tag is not valid with --input-format artifact\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + + if (!amduat_asl_store_fs_load_config(opts.root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", opts.root); + return AMDUAT_ASL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, opts.root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_ASL_CLI_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + input_bytes = NULL; + input_len = 0u; + if (!amduat_asl_cli_read_path(opts.input_path, &input_bytes, &input_len)) { + fprintf(stderr, "error: failed to read input: %s\n", opts.input_path); + return AMDUAT_ASL_CLI_EXIT_IO; + } + + if (opts.input_format == AMDUAT_ASL_CLI_IO_ARTIFACT) { + ok = amduat_enc_asl1_core_decode_artifact_v1( + amduat_octets(input_bytes, input_len), + &artifact); + free(input_bytes); + if (!ok) { + fprintf(stderr, "error: invalid artifact encoding\n"); + return AMDUAT_ASL_CLI_EXIT_CODEC; + } + } else { + artifact = opts.has_type_tag + ? amduat_artifact_with_type( + amduat_octets(input_bytes, input_len), + opts.type_tag) + : amduat_artifact(amduat_octets(input_bytes, input_len)); + } + + memset(&ref, 0, sizeof(ref)); + exit_code = AMDUAT_ASL_CLI_EXIT_OK; + { + amduat_asl_store_error_t err = + amduat_asl_store_put(&store, artifact, &ref); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: store put failed: %s\n", + amduat_asl_cli_store_error_str(err)); + exit_code = amduat_asl_cli_map_store_error(err); + } else { + if (opts.ref_format == AMDUAT_ASL_CLI_REF_HEX) { + char *hex_ref = NULL; + if (!amduat_asl_cli_encode_reference_hex(ref, &hex_ref)) { + fprintf(stderr, "error: failed to encode reference\n"); + exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; + } else { + if (!amduat_asl_cli_write_text_line(opts.output_path, hex_ref)) { + fprintf(stderr, "error: failed to write output: %s\n", + opts.output_path); + exit_code = AMDUAT_ASL_CLI_EXIT_IO; + } + free(hex_ref); + } + } else { + if (!amduat_enc_asl1_core_encode_reference_v1(ref, &encoded_ref)) { + fprintf(stderr, "error: failed to encode reference\n"); + exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; + } else { + if (!amduat_asl_cli_write_path(opts.output_path, + encoded_ref.data, + encoded_ref.len)) { + fprintf(stderr, "error: failed to write output: %s\n", + opts.output_path); + exit_code = AMDUAT_ASL_CLI_EXIT_IO; + } + free((void *)encoded_ref.data); + } + } + + if (exit_code == AMDUAT_ASL_CLI_EXIT_OK) { + char *hex_ref = NULL; + fprintf(stderr, "root=%s\n", opts.root); + amduat_asl_cli_print_store_meta(&cfg); + fprintf(stderr, "bytes=%zu\n", artifact.bytes.len); + if (artifact.has_type_tag) { + fprintf(stderr, "type_tag=0x%08x\n", + (unsigned int)artifact.type_tag.tag_id); + } + if (amduat_asl_cli_encode_reference_hex(ref, &hex_ref)) { + fprintf(stderr, "ref=%s\n", hex_ref); + free(hex_ref); + } + } + } + } + + if (opts.input_format == AMDUAT_ASL_CLI_IO_ARTIFACT) { + amduat_asl_cli_free_artifact(&artifact); + } else { + free((void *)artifact.bytes.data); + } + amduat_asl_cli_free_reference(&ref); + return exit_code; +} + +static int amduat_asl_cli_cmd_get(int argc, char **argv) { + amduat_asl_cli_get_opts_t opts; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_artifact_t artifact; + amduat_reference_t ref; + uint8_t *ref_bytes; + size_t ref_len; + amduat_octets_t encoded_artifact; + bool ok; + int i; + int exit_code; + + memset(&opts, 0, sizeof(opts)); + opts.root = AMDUAT_ASL_CLI_DEFAULT_ROOT; + opts.output_path = "-"; + opts.output_format = AMDUAT_ASL_CLI_IO_RAW; + opts.ref_format = AMDUAT_ASL_CLI_REF_HEX; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.ref = argv[++i]; + } else if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.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_ASL_CLI_EXIT_USAGE; + } + if (!amduat_asl_cli_parse_ref_format(argv[++i], &opts.ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--output") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output requires a path or -\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.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_ASL_CLI_EXIT_USAGE; + } + if (!amduat_asl_cli_parse_io_format(argv[++i], &opts.output_format)) { + fprintf(stderr, "error: invalid output-format\n"); + return AMDUAT_ASL_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_ASL_CLI_EXIT_USAGE; + } + if (!amduat_asl_cli_parse_type_tag(argv[++i], + &opts.expect_type_tag)) { + fprintf(stderr, "error: invalid expect-type-tag\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + opts.has_expect_type_tag = true; + } else if (strcmp(argv[i], "--print-type-tag") == 0) { + opts.print_type_tag = true; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_asl_cli_print_usage(stdout); + return AMDUAT_ASL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } + + if (opts.ref == NULL) { + fprintf(stderr, "error: --ref is required\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + + if (!amduat_asl_store_fs_load_config(opts.root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", opts.root); + return AMDUAT_ASL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, opts.root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_ASL_CLI_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + memset(&ref, 0, sizeof(ref)); + if (opts.ref_format == AMDUAT_ASL_CLI_REF_HEX) { + if (!amduat_asl_cli_decode_reference_hex(opts.ref, &ref)) { + fprintf(stderr, "error: invalid hex reference\n"); + return AMDUAT_ASL_CLI_EXIT_CODEC; + } + } else { + ref_bytes = NULL; + ref_len = 0u; + if (!amduat_asl_cli_read_path(opts.ref, &ref_bytes, &ref_len)) { + fprintf(stderr, "error: failed to read reference: %s\n", opts.ref); + return AMDUAT_ASL_CLI_EXIT_IO; + } + ok = amduat_asl_cli_decode_reference_bytes(ref_bytes, ref_len, &ref); + free(ref_bytes); + if (!ok) { + fprintf(stderr, "error: invalid reference bytes\n"); + return AMDUAT_ASL_CLI_EXIT_CODEC; + } + } + + memset(&artifact, 0, sizeof(artifact)); + exit_code = AMDUAT_ASL_CLI_EXIT_OK; + { + amduat_asl_store_error_t err = amduat_asl_store_get(&store, ref, &artifact); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: store get failed: %s\n", + amduat_asl_cli_store_error_str(err)); + exit_code = amduat_asl_cli_map_store_error(err); + } else { + if (opts.has_expect_type_tag) { + if (!artifact.has_type_tag || + artifact.type_tag.tag_id != opts.expect_type_tag.tag_id) { + fprintf(stderr, "error: type-tag mismatch\n"); + exit_code = AMDUAT_ASL_CLI_EXIT_UNSUPPORTED; + } + } + + if (exit_code == AMDUAT_ASL_CLI_EXIT_OK) { + if (opts.output_format == AMDUAT_ASL_CLI_IO_RAW) { + if (!amduat_asl_cli_write_path(opts.output_path, + artifact.bytes.data, + artifact.bytes.len)) { + fprintf(stderr, "error: failed to write output: %s\n", + opts.output_path); + exit_code = AMDUAT_ASL_CLI_EXIT_IO; + } + } else { + if (!amduat_enc_asl1_core_encode_artifact_v1( + artifact, &encoded_artifact)) { + fprintf(stderr, "error: failed to encode artifact\n"); + exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; + } else { + if (!amduat_asl_cli_write_path(opts.output_path, + encoded_artifact.data, + encoded_artifact.len)) { + fprintf(stderr, "error: failed to write output: %s\n", + opts.output_path); + exit_code = AMDUAT_ASL_CLI_EXIT_IO; + } + free((void *)encoded_artifact.data); + } + } + } + + if (exit_code == AMDUAT_ASL_CLI_EXIT_OK) { + fprintf(stderr, "root=%s\n", opts.root); + amduat_asl_cli_print_store_meta(&cfg); + fprintf(stderr, "bytes=%zu\n", artifact.bytes.len); + if (opts.print_type_tag) { + if (artifact.has_type_tag) { + fprintf(stderr, "type_tag=0x%08x\n", + (unsigned int)artifact.type_tag.tag_id); + } else { + fprintf(stderr, "type_tag=none\n"); + } + } + } + } + } + + amduat_asl_cli_free_reference(&ref); + amduat_asl_cli_free_artifact(&artifact); + return exit_code; +} + +int main(int argc, char **argv) { + if (argc < 2) { + amduat_asl_cli_print_usage(stderr); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + + if (strcmp(argv[1], "init") == 0) { + return amduat_asl_cli_cmd_init(argc - 2, argv + 2); + } + if (strcmp(argv[1], "put") == 0) { + return amduat_asl_cli_cmd_put(argc - 2, argv + 2); + } + if (strcmp(argv[1], "get") == 0) { + return amduat_asl_cli_cmd_get(argc - 2, argv + 2); + } + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { + amduat_asl_cli_print_usage(stdout); + return AMDUAT_ASL_CLI_EXIT_OK; + } + + fprintf(stderr, "error: unknown command: %s\n", argv[1]); + amduat_asl_cli_print_usage(stderr); + return AMDUAT_ASL_CLI_EXIT_USAGE; +}