dtob-sys 0.1.0

Raw FFI bindings to the dtob C library (encoder + decoder).
Documentation
#include "cli.h"
#include <unistd.h>

/* ------------------------------------------------------------------ */

static int cmd_extract(int argc, char **argv)
{
    if (argc < 2) {
        fprintf(stderr, "usage: dtob extract <input.dtob> <key> > output\n");
        return 1;
    }

    size_t len;
    uint8_t *buf = read_file(argv[0], &len);
    if (!buf) return 1;

    DtobValue *root = dtob_decode(buf, len);
    free(buf);
    if (!root) {
        fprintf(stderr, "error: failed to decode DTOB\n");
        return 1;
    }

    if (root->type != DTOB_KV_SET) {
        fprintf(stderr, "error: root is not a kv_set\n");
        dtob_free(root);
        return 1;
    }

    const char *target = argv[1];
    DtobValue *found = NULL;
    for (size_t i = 0; i < root->num_pairs; i++) {
        if (root->pairs[i].key_len == strlen(target) &&
            memcmp(root->pairs[i].key, target, root->pairs[i].key_len) == 0) {
            found = root->pairs[i].value;
            break;
        }
    }

    if (!found) {
        fprintf(stderr, "error: key '%s' not found\n", target);
        dtob_free(root);
        return 1;
    }

    if (found->data && found->data_len > 0) {
        fwrite(found->data, 1, found->data_len, stdout);
    }

    dtob_free(root);
    return 0;
}

static int cmd_info(int argc, char **argv)
{
    if (argc < 1) {
        fprintf(stderr, "usage: dtob info <input.dtob>\n");
        return 1;
    }

    size_t len;
    uint8_t *buf = read_file(argv[0], &len);
    if (!buf) return 1;

    DtobValue *root = dtob_decode(buf, len);
    free(buf);
    if (!root) {
        fprintf(stderr, "error: failed to decode DTOB\n");
        return 1;
    }

    fprintf(stderr, "file size: %zu bytes\n", len);
    dtob_print(root, 0);
    dtob_free(root);
    return 0;
}

/* ------------------------------------------------------------------ */

static void exec_subcommand(const char *argv0, const char *cmd,
                             int argc, char **argv)
{
    char subcmd[256];
    snprintf(subcmd, sizeof(subcmd), "dtob-%s", cmd);

    /* build new argv: { "dtob-<cmd>", arg1, ..., NULL } */
    char **new_argv = malloc(((size_t)argc + 2) * sizeof(char *));
    if (!new_argv) { perror("malloc"); exit(1); }
    new_argv[0] = subcmd;
    for (int i = 0; i < argc; i++)
        new_argv[i + 1] = argv[i];
    new_argv[argc + 1] = NULL;

    /* try sibling binary in the same directory as argv[0] */
    const char *slash = strrchr(argv0, '/');
    if (slash) {
        size_t dir_len = (size_t)(slash - argv0 + 1);
        char path[512];
        if (dir_len + strlen(subcmd) < sizeof(path)) {
            memcpy(path, argv0, dir_len);
            strcpy(path + dir_len, subcmd);
            new_argv[0] = path; /* pass full path so subcommand knows its location */
            execv(path, new_argv);
        }
    }

    /* fall back to PATH */
    execvp(subcmd, new_argv);

    fprintf(stderr, "dtob: '%s' is not a dtob command\n", cmd);
    free(new_argv);
    exit(1);
}

/* ------------------------------------------------------------------ */

int main(int argc, char **argv)
{
    if (argc < 2) {
        fprintf(stderr, "usage: dtob <command> [args...]\n");
        fprintf(stderr, "commands: encode, represent, extract, info, memcache\n");
        return 1;
    }

    const char *cmd = argv[1];
    int sub_argc = argc - 2;
    char **sub_argv = argv + 2;

    if (strcmp(cmd, "extract") == 0)  return cmd_extract(sub_argc, sub_argv);
    if (strcmp(cmd, "info") == 0)     return cmd_info(sub_argc, sub_argv);
    if (strcmp(cmd, "memcache") == 0) return cmd_memcache(sub_argc, sub_argv);

    exec_subcommand(argv[0], cmd, sub_argc, sub_argv);
    return 1; /* unreachable */
}