Skip to content

Command Line

struct cmdline_range {
    uint64_t  start;
    uint64_t  end;
};
struct cmdline_list {
    size_t                count;
    struct cmdline_value  *items;
};
struct cmdline_value {
    enum cmdline_value_mode  mode;
    union {
        enum type_enum     c_type;
        enum cmdline_type  type;
    };
    union {
        void       *write_to;
        void       *data;
        uint64_t   u64;
        int64_t    i64;
        bool       b;
        fx32_32_t  fx;
        time_ns_t  duration;
    };
    enum errno               (*parse)(void *write_to, const char *text);
};
struct cmdline_entry {
    const char                 *name;
    const char                 *desc;
    const char                 *arg;
    const char                 *default_val;
    enum cmdline_entry_flags   flags;
    enum cmdline_entry_status  status;
    struct cmdline_entry       *parent;
    uint64_t                   types;
    struct range               range;
    struct cmdline_value       value;
    const char                 *const *choices;
    const struct cmdline_map   *mappings;
    const struct cmdline_flag  *flags_table;
};
struct cmdline_map {
    const char  *name;
    uint64_t    value;
};
struct cmdline_flag {
    const char  *name;
    uint64_t    value;
};
struct cmdline_schema_prop {
    const char                 *name;
    const char                 *desc;
    size_t                     offset;
    enum type_enum             c_type;
    uint64_t                   types;
    enum errno                 (*parse)(void *dst, const char *text);
    struct range               range;
    const char                 *const *choices;
    const struct cmdline_map   *mappings;
    const struct cmdline_flag  *flags_table;
};
struct cmdline_schema {
    const char                        *prefix;
    const char                        *path_hint;
    const char                        *desc;
    cmdline_instance_resolver_t       resolve;
    const struct cmdline_schema_prop  *props;
    size_t                            prop_count;
};
enum cmdline_entry_status {
    CMDLINE_ENTRY_NOT_FOUND = 0,
    CMDLINE_ENTRY_DEFAULTED,
    CMDLINE_ENTRY_FOUND,
};
enum cmdline_entry_flags {
    CMDLINE_ENTRY_FLAGS_NONE = 0,
    CMDLINE_ENTRY_SYMBOLIC = 1,
    CMDLINE_ENTRY_REQUIRED = 1 << 1,
    CMDLINE_ENTRY_HIDDEN = 1 << 2,
};
enum cmdline_value_mode {
    CMDLINE_MODE_POLYMORPHIC = 0,
    CMDLINE_MODE_VAR,
    CMDLINE_MODE_TYPED = CMDLINE_MODE_VAR,
    CMDLINE_MODE_CUSTOM = CMDLINE_MODE_VAR,
};
enum cmdline_type {
    CMDLINE_TYPE_FIRST_ = CMDLINE_TYPE_OFFSET - 1,
    CMDLINE_TYPE_BOOL = CMDLINE_TYPE_OFFSET,
    CMDLINE_TYPE_INT,
    CMDLINE_TYPE_UINT,
    CMDLINE_TYPE_FX,
    CMDLINE_TYPE_DURATION,
    CMDLINE_TYPE_DATA_SIZE,
    CMDLINE_TYPE_RANGE,
    CMDLINE_TYPE_CPU_MASK,
    CMDLINE_TYPE_MAC,
    CMDLINE_TYPE_STRING,
    CMDLINE_TYPE_LIST,
    CMDLINE_TYPE_ERR,
    CMDLINE_TYPE_NONE,
};
typedef void * (*cmdline_instance_resolver_t)(const char * path, size_t path_len);
void cmdline_parse(const char *input);
bool cmdline_wants_help(const char *input);
__noreturn void cmdline_dump_help(void);
void cmdline_debug_hook(void);
struct cmdline_entry * cmdline_lookup(const char *key);
uint64_t cmdline_entry_value_u64(const struct cmdline_entry *e);
#define CMDLINE_TYPE_OFFSET 6
#define CMDLINE_TYPES(...) \
    _DISPATCH(CMDLINE_IMPL_TYPE_BIT, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define CMDLINE_MAP(str, enum_val) \
    {.name = (str), .value = (uint64_t) (enum_val)}
#define CMDLINE_MAPPINGS(...) \
    ((const struct cmdline_map[]) {__VA_ARGS__, {NULL, 0}})
#define CMDLINE_FLAG(str, bit_val) \
    {.name = (str), .value = (uint64_t) (bit_val)}
#define CMDLINE_FLAGS(...) \
    ((const struct cmdline_flag[]) {__VA_ARGS__, {NULL, 0}})
#define CMDLINE_CHOICES(...) ((const char *const[]) {__VA_ARGS__, NULL})
#define CMDLINE_PARSER(fn) .value.parse = (fn)
#define CMDLINE_DECLARE(n, ...) \
    LINKER_SECTION_OBJECT(struct cmdline_entry, cmdline_entries) \
    __cmdline_##n = {.name = #n, \
                     .status = CMDLINE_ENTRY_NOT_FOUND, \
                     .types = 0, \
                     .range = RANGE(1, 0), \
                     .choices = NULL, \
                     .mappings = NULL, \
                     .flags_table = NULL, \
                     .value.mode = CMDLINE_MODE_POLYMORPHIC, \
                     .value.type = CMDLINE_TYPE_NONE, \
                     .flags = CMDLINE_ENTRY_FLAGS_NONE, \
                     __VA_ARGS__}
#define CMDLINE_DECLARE_VAR(n, var, ...) \
    LINKER_SECTION_OBJECT(struct cmdline_entry, cmdline_entries) \
    __cmdline_##n = {.name = #n, \
                     .status = CMDLINE_ENTRY_NOT_FOUND, \
                     .types = 0, \
                     .range = RANGE(1, 0), \
                     .choices = NULL, \
                     .mappings = NULL, \
                     .flags_table = NULL, \
                     .value.mode = CMDLINE_MODE_VAR, \
                     .value.write_to = &(var), \
                     .value.c_type = TYPE_TO_ENUM((var)), \
                     .value.parse = NULL, \
                     .flags = CMDLINE_ENTRY_FLAGS_NONE, \
                     __VA_ARGS__}
#define CMDLINE_CHILD_DECLARE(parent_n, n, ...) \
    CMDLINE_DECLARE(parent_n##_##n, .name = #n, .parent = CMDLINE(parent_n), \
                    __VA_ARGS__)
#define CMDLINE_CHILD_DECLARE_VAR(parent_n, n, var, ...) \
    CMDLINE_DECLARE_VAR(parent_n##_##n, var, .name = #n, \
                        .parent = CMDLINE(parent_n), __VA_ARGS__)
#define CMDLINE_INNER(n, ...) (_CMDLINE_CHILD_KIND_POLY, n, 0, ##__VA_ARGS__)
#define CMDLINE_INNER_VAR(n, var, ...) \
    (_CMDLINE_CHILD_KIND_VAR, n, var, ##__VA_ARGS__)
#define CMDLINE_CHILDREN_DECLARE(parent_n, ...) \
    _DISPATCH(_CMDLINE_CHILDREN_MAP, PP_NARG(__VA_ARGS__))(parent_n, \
                                                           __VA_ARGS__)
#define CMDLINE_EXTRACT(val, var) \
    _Generic(&(var), \
        bool *: cmdline_extract_bool((val), (bool *) &(var)), \
        uint64_t *: cmdline_extract_u64((val), (uint64_t *) &(var)), \
        int64_t *: cmdline_extract_i64((val), (int64_t *) &(var)), \
        uint32_t *: cmdline_extract_u32((val), (uint32_t *) &(var)), \
        int32_t *: cmdline_extract_i32((val), (int32_t *) &(var)), \
        uint16_t *: cmdline_extract_u16((val), (uint16_t *) &(var)), \
        int16_t *: cmdline_extract_i16((val), (int16_t *) &(var)), \
        uint8_t *: cmdline_extract_u8((val), (uint8_t *) &(var)), \
        int8_t *: cmdline_extract_i8((val), (int8_t *) &(var)), \
        struct cmdline_range *: cmdline_extract_range( \
            (val), (struct cmdline_range *) &(var)), \
        char **: cmdline_extract_string((val), (char **) &(var)), \
        struct cpu_mask *: cmdline_extract_cpu_mask( \
            (val), (struct cpu_mask *) &(var)), \
        struct cmdline_list *: cmdline_extract_list( \
            (val), (struct cmdline_list *) &(var)), \
        const char **: cmdline_extract_const_string((val), \
                                                    (const char **) &(var)))
#define CMDLINE_EXTRACT_LIST(list, type, out_buf, max_count, parse_fn) \
    ({ \
        size_t __n = 0; \
        const struct cmdline_list *__l = (list); \
        const size_t __max = (max_count); \
        for (size_t __i = 0; __i < __l->count && __n < __max; __i++) { \
            type __v = (type) {0}; \
            if ((parse_fn) (&__l->items[__i], &__v) == ERR_OK) \
                (out_buf)[__n++] = __v; \
        } \
        __n; \
    })
#define CMDLINE_NODE_1(a) a
#define CMDLINE_NODE_2(a, b) a##_##b
#define CMDLINE_NODE_3(a, b, c) a##_##b##_##c
#define CMDLINE_NODE_4(a, b, c, d) a##_##b##_##c##_##d
#define CMDLINE_NODE(...) \
    _DISPATCH(CMDLINE_NODE, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define CMDLINE_DEFINE(n) extern struct cmdline_entry CONCAT(__cmdline_, n)
#define CMDLINE(n) (&CONCAT(__cmdline_, n))
#define CMDLINE_VALUE(n) cmdline_entry_value_u64(CMDLINE(n))
#define CMDLINE_CHILD_DEFINE(...) CMDLINE_DEFINE(CMDLINE_NODE(__VA_ARGS__))
#define CMDLINE_CHILD(...) CMDLINE(CMDLINE_NODE(__VA_ARGS__))
#define CMDLINE_CHILD_VALUE(...) \
    cmdline_entry_value_u64(CMDLINE_CHILD(__VA_ARGS__))
#define CMDLINE_ENTRY_NAME_LEN_MAX 256
#define cmdline_list_for_each(val, list) \
    for (size_t __i = 0, __count = (list)->count; \
         __i < __count && (((val) = (list)->items[__i]), true); __i++)
#define CMDLINE_SCHEMA_PROP_PARSER(fn) .parse = (fn)
#define CMDLINE_SCHEMA_PROP(struct_type, member, ...) \
    {.name = #member, \
     .offset = offsetof(struct_type, member), \
     .c_type = TYPE_TO_ENUM(((struct_type *) 0)->member), \
     .types = 0, \
     .parse = NULL, \
     .range = RANGE(1, 0), \
     .choices = NULL, \
     .mappings = NULL, \
     .flags_table = NULL, \
     __VA_ARGS__}
#define CMDLINE_SCHEMA_DECLARE(n, prefix_str, path_hint_str, desc_str, \
                               resolver_fn, ...) \
    static const struct cmdline_schema_prop __cmdline_schema_props_##n[] = { \
        __VA_ARGS__}; \
    LINKER_SECTION_OBJECT(struct cmdline_schema, cmdline_schemas) \
    __cmdline_schema_##n = { \
        .prefix = (prefix_str), \
        .path_hint = (path_hint_str), \
        .desc = (desc_str), \
        .resolve = (resolver_fn), \
        .props = __cmdline_schema_props_##n, \
        .prop_count = sizeof(__cmdline_schema_props_##n) / \
                      sizeof(__cmdline_schema_props_##n[0]), \
    }
#define CMDLINE_GET(key, type, fallback) \
    ({ \
        type __out = (fallback); \
        struct cmdline_entry *__e = cmdline_lookup(key); \
        if (__e && (__e->status == CMDLINE_ENTRY_FOUND || \
                    __e->status == CMDLINE_ENTRY_DEFAULTED)) { \
            CMDLINE_EXTRACT(&__e->value, __out); \
        } \
        __out; \
    })
#define cmdline_read_or(key, target_var, fallback) \
    ({ \
        bool __overridden = false; \
        struct cmdline_entry *__e = cmdline_lookup(key); \
        if (__e && __e->status == CMDLINE_ENTRY_FOUND && \
            CMDLINE_EXTRACT(&__e->value, target_var) == ERR_OK) { \
            __overridden = true; \
        } else { \
            (target_var) = (fallback); \
        } \
        __overridden; \
    })