diff options
Diffstat (limited to 'package/utils/ucode-mod-uline/src/ucode.c')
-rw-r--r-- | package/utils/ucode-mod-uline/src/ucode.c | 908 |
1 files changed, 908 insertions, 0 deletions
diff --git a/package/utils/ucode-mod-uline/src/ucode.c b/package/utils/ucode-mod-uline/src/ucode.c new file mode 100644 index 0000000000..bf53a636ed --- /dev/null +++ b/package/utils/ucode-mod-uline/src/ucode.c @@ -0,0 +1,908 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name> + */ +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <poll.h> + +#include <ucode/module.h> +#include <libubox/list.h> +#include <libubox/uloop.h> + +#include "uline.h" + +static uc_value_t *registry; +static uc_resource_type_t *state_type, *argp_type; + +enum { + STATE_RES, + STATE_CB, + STATE_INPUT, + STATE_OUTPUT, + STATE_POLL_CB, +}; + +struct uc_uline_state { + struct uloop_fd fd; + + struct uline_state s; + int registry_index; + + uc_vm_t *vm; + uc_value_t *state, *cb, *res, *poll_cb; + + uc_value_t *line; + + uint32_t input_mask[256 / 32]; +}; + +struct uc_arg_parser { + char line_sep; +}; + +static unsigned int +registry_set(uc_vm_t *vm, uc_value_t *val) +{ + uc_value_t *registry; + size_t i, len; + + registry = uc_vm_registry_get(vm, "uline.registry"); + len = ucv_array_length(registry); + for (i = 0; i < len; i++) + if (ucv_array_get(registry, i) == NULL) + break; + + ucv_array_set(registry, i, ucv_get(val)); + return i; +} + +static uc_value_t * +uc_uline_poll(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + uc_value_t *val; + + if (!us) + return NULL; + + uline_poll(&us->s); + val = us->line; + us->line = NULL; + + return val; +} + +static uc_value_t * +uc_uline_poll_key(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + uc_value_t *timeout_arg = uc_fn_arg(0); + struct pollfd pfd = {}; + int timeout, len; + char c; + + if (!us) + return NULL; + + if (ucv_type(timeout_arg) == UC_INTEGER) + timeout = ucv_int64_get(timeout_arg); + else + timeout = -1; + + pfd.fd = us->s.input; + pfd.events = POLLIN; + poll(&pfd, 1, timeout); + if (!(pfd.revents & POLLIN)) + return NULL; + + do { + len = read(pfd.fd, &c, 1); + } while (len < 0 && errno == EINTR); + + if (len != 1) + return NULL; + + return ucv_string_new_length(&c, 1); +} + +static uc_value_t * +uc_uline_poll_stop(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + + if (!us) + return NULL; + + us->s.stop = true; + + return NULL; +} + +static uc_value_t * +uc_uline_get_window(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + uc_value_t *val; + + if (!us) + return NULL; + + val = ucv_object_new(vm); + ucv_object_add(val, "x", ucv_int64_new(us->s.cols)); + ucv_object_add(val, "y", ucv_int64_new(us->s.rows)); + return val; +} + +static uc_value_t * +uc_uline_get_line(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + uc_value_t *line2 = uc_fn_arg(0); + uc_value_t *state; + const char *line; + size_t len; + + if (!us) + return NULL; + + state = ucv_object_new(vm); + if (ucv_is_truish(line2)) + uline_get_line2(&us->s, &line, &len); + else + uline_get_line(&us->s, &line, &len); + ucv_object_add(state, "line", ucv_string_new_length(line, len)); + ucv_object_add(state, "pos", ucv_int64_new(us->s.line.pos)); + + return state; +} + +static uc_value_t * +uc_uline_set_state(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + uc_value_t *state = uc_fn_arg(0); + uc_value_t *arg; + bool found; + + if (!us || ucv_type(state) != UC_OBJECT) + return NULL; + + if ((arg = ucv_object_get(state, "prompt", NULL)) != NULL) { + if (ucv_type(arg) != UC_STRING) + return NULL; + + uline_set_prompt(&us->s, ucv_string_get(arg)); + } + + if ((arg = ucv_object_get(state, "line", NULL)) != NULL) { + if (ucv_type(arg) != UC_STRING) + return NULL; + + uline_set_line(&us->s, ucv_string_get(arg), ucv_string_length(arg)); + } + + if ((arg = ucv_object_get(state, "pos", NULL)) != NULL) { + if (ucv_type(arg) != UC_INTEGER) + return NULL; + + uline_set_cursor(&us->s, ucv_int64_get(arg)); + } + + arg = ucv_object_get(state, "line2_prompt", &found); + if (found) { + if (!arg) + uline_set_line2_prompt(&us->s, NULL); + else if (ucv_type(arg) == UC_STRING) + uline_set_line2_prompt(&us->s, ucv_string_get(arg)); + else + return NULL; + } + + if ((arg = ucv_object_get(state, "line2", NULL)) != NULL) { + if (ucv_type(arg) != UC_STRING) + return NULL; + + uline_set_line2(&us->s, ucv_string_get(arg), ucv_string_length(arg)); + } + + if ((arg = ucv_object_get(state, "line2_pos", NULL)) != NULL) { + if (ucv_type(arg) != UC_INTEGER) + return NULL; + + uline_set_line2_cursor(&us->s, ucv_int64_get(arg)); + } + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_uline_set_hint(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + uc_value_t *arg = uc_fn_arg(0); + + if (!us || ucv_type(arg) != UC_STRING) + return NULL; + + uline_set_hint(&us->s, ucv_string_get(arg), ucv_string_length(arg)); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_uline_set_uloop(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + uc_value_t *cb = uc_fn_arg(0); + + if (!us || (cb && !ucv_is_callable(cb))) + return NULL; + + us->poll_cb = cb; + ucv_array_set(us->state, STATE_POLL_CB, ucv_get(cb)); + if (cb) { + uloop_fd_add(&us->fd, ULOOP_READ); + us->fd.cb(&us->fd, 0); + } else { + uloop_fd_delete(&us->fd); + } + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_uline_reset_key_input(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + + us->s.repeat_char = 0; + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_uline_hide_prompt(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + + if (!us) + return NULL; + + uline_hide_prompt(&us->s); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_uline_refresh_prompt(uc_vm_t *vm, size_t nargs) +{ + struct uc_uline_state *us = uc_fn_thisval("uline.state"); + + if (!us) + return NULL; + + uline_refresh_prompt(&us->s); + + return ucv_boolean_new(true); +} + +static bool +cb_prepare(struct uc_uline_state *us, const char *name) +{ + uc_value_t *func; + + func = ucv_object_get(us->cb, name, NULL); + if (!func) + return false; + + uc_vm_stack_push(us->vm, ucv_get(us->res)); + uc_vm_stack_push(us->vm, ucv_get(func)); + return true; +} + +static uc_value_t * +cb_call_ret(struct uc_uline_state *us, size_t args, ...) +{ + uc_vm_t *vm = us->vm; + va_list ap; + + va_start(ap, args); + for (size_t i = 0; i < args; i++) + uc_vm_stack_push(vm, ucv_get(va_arg(ap, void *))); + va_end(ap); + + if (uc_vm_call(vm, true, args) == EXCEPTION_NONE) + return uc_vm_stack_pop(vm); + + return NULL; +} +#define cb_call(...) ucv_put(cb_call_ret(__VA_ARGS__)) + +static bool +uc_uline_cb_line(struct uline_state *s, const char *str, size_t len) +{ + struct uc_uline_state *us = container_of(s, struct uc_uline_state, s); + bool complete = true; + uc_value_t *ret; + + if (cb_prepare(us, "line_check")) { + ret = cb_call_ret(us, 1, ucv_string_new_length(str, len)); + complete = ucv_is_truish(ret); + ucv_put(ret); + } + + s->stop = complete; + if (complete) + us->line = ucv_string_new_length(str, len); + + return complete; +} + +static void +uc_uline_cb_event(struct uline_state *s, enum uline_event ev) +{ + struct uc_uline_state *us = container_of(s, struct uc_uline_state, s); + static const char * const ev_types[] = { + [EDITLINE_EV_CURSOR_UP] = "cursor_up", + [EDITLINE_EV_CURSOR_DOWN] = "cursor_down", + [EDITLINE_EV_WINDOW_CHANGED] = "window_changed", + [EDITLINE_EV_EOF] = "eof", + [EDITLINE_EV_INTERRUPT] = "interrupt", + }; + + if (ev > ARRAY_SIZE(ev_types) || !ev_types[ev]) + return; + + if (!cb_prepare(us, ev_types[ev])) + return; + + if (ev == EDITLINE_EV_WINDOW_CHANGED) + cb_call(us, 2, ucv_int64_new(s->cols), ucv_int64_new(s->rows)); + else + cb_call(us, 0); +} + +static void uc_uline_poll_cb(struct uloop_fd *fd, unsigned int events) +{ + struct uc_uline_state *us = container_of(fd, struct uc_uline_state, fd); + uc_value_t *res = ucv_get(us->res); + uc_value_t *val; + + while (!uloop_cancelled && ucv_resource_data(res, NULL) && us->poll_cb) { + uline_poll(&us->s); + + val = us->line; + if (!val) + break; + + us->line = NULL; + if (!ucv_is_callable(us->poll_cb)) + break; + + uc_vm_stack_push(us->vm, ucv_get(res)); + uc_vm_stack_push(us->vm, ucv_get(us->poll_cb)); + cb_call(us, 1, val); + } + ucv_put(res); +} + +static bool +uc_uline_cb_key_input(struct uline_state *s, unsigned char c, unsigned int count) +{ + struct uc_uline_state *us = container_of(s, struct uc_uline_state, s); + uc_value_t *ret; + bool retval; + + if (!(us->input_mask[c / 32] & (1 << (c % 32)))) + return false; + + if (!cb_prepare(us, "key_input")) + return false; + + ret = cb_call_ret(us, 2, ucv_string_new_length((char *)&c, 1), ucv_int64_new(count)); + retval = ucv_is_truish(ret); + ucv_put(ret); + + return retval; +} + +static void +uc_uline_cb_line2_update(struct uline_state *s, const char *str, size_t len) +{ + struct uc_uline_state *us = container_of(s, struct uc_uline_state, s); + + if (cb_prepare(us, "line2_update")) + cb_call(us, 1, ucv_string_new_length(str, len)); +} + +static bool +uc_uline_cb_line2_cursor(struct uline_state *s) +{ + struct uc_uline_state *us = container_of(s, struct uc_uline_state, s); + uc_value_t *retval; + bool ret = true; + + if (cb_prepare(us, "line2_cursor")) { + retval = cb_call_ret(us, 0); + ret = ucv_is_truish(retval); + ucv_put(retval); + } + + return ret; +} + +static bool +uc_uline_cb_line2_newline(struct uline_state *s, const char *str, size_t len) +{ + struct uc_uline_state *us = container_of(s, struct uc_uline_state, s); + uc_value_t *retval; + bool ret = false; + + if (cb_prepare(us, "line2_newline")) { + retval = cb_call_ret(us, 1, ucv_string_new_length(str, len)); + ret = ucv_is_truish(retval); + ucv_put(retval); + } + + return ret; +} + +static uc_value_t * +uc_uline_new(uc_vm_t *vm, size_t nargs) +{ + static const struct uline_cb uline_cb = { +#define _CB(_type) ._type = uc_uline_cb_##_type + _CB(key_input), + _CB(line), + _CB(event), + _CB(line2_update), + _CB(line2_cursor), + _CB(line2_newline), +#undef _CB + }; + uc_value_t *data = uc_fn_arg(0); + struct uc_uline_state *us; + FILE *input, *output; + uc_value_t *arg, *cb, *state, *res; + + if (ucv_type(data) != UC_OBJECT) + return NULL; + + cb = ucv_object_get(data, "cb", NULL); + if (ucv_type(cb) != UC_OBJECT) + return NULL; + + state = ucv_array_new(vm); + ucv_array_set(state, 0, ucv_get(cb)); + if ((arg = ucv_object_get(data, "input", NULL)) != NULL) { + input = ucv_resource_data(arg, "fs.file"); + ucv_array_set(state, STATE_INPUT, ucv_get(arg)); + } else { + input = stdin; + } + + if ((arg = ucv_object_get(data, "output", NULL)) != NULL) { + output = ucv_resource_data(arg, "fs.file"); + ucv_array_set(state, STATE_OUTPUT, ucv_get(arg)); + } else { + output = stdout; + } + + if (!input || !output) { + input = output = NULL; + return NULL; + } + + us = calloc(1, sizeof(*us)); + us->vm = vm; + us->state = ucv_array_new(vm); + ucv_array_set(us->state, STATE_CB, ucv_get(cb)); + us->cb = cb; + us->registry_index = registry_set(vm, state); + + if ((arg = ucv_object_get(data, "key_input_list", NULL)) != NULL) { + uc_value_t *val; + size_t len; + + if (ucv_type(arg) != UC_ARRAY) + goto free; + + len = ucv_array_length(arg); + for (size_t i = 0; i < len; i++) { + unsigned char c; + + val = ucv_array_get(arg, i); + if (ucv_type(val) != UC_STRING || ucv_string_length(val) != 1) + goto free; + + c = ucv_string_get(val)[0]; + us->input_mask[c / 32] |= 1 << (c % 32); + } + } + + res = ucv_resource_new(state_type, us); + ucv_array_set(us->state, STATE_RES, ucv_get(res)); + us->res = res; + us->fd.fd = fileno(input); + us->fd.cb = uc_uline_poll_cb; + + uline_init(&us->s, &uline_cb, us->fd.fd, output, true); + + return res; + +free: + free(us); + return NULL; +} + +static void free_state(void *ptr) +{ + struct uc_uline_state *us = ptr; + uc_value_t *registry; + + if (!us) + return; + + uloop_fd_delete(&us->fd); + registry = uc_vm_registry_get(us->vm, "uline.registry"); + ucv_array_set(registry, us->registry_index, NULL); + uline_free(&us->s); + free(us); +} + +static uc_value_t * +uc_uline_close(uc_vm_t *vm, size_t nargs) +{ + struct uline_state **s = uc_fn_this("uline.state"); + + if (!s || !*s) + return NULL; + + free_state(*s); + *s = NULL; + + return NULL; +} + +static bool +skip_space(const char **str, const char *end) +{ + while (*str < end && isspace(**str)) + (*str)++; + return *str < end; +} + +static void +add_str(uc_stringbuf_t **buf, const char *str, const char *next) +{ + if (str == next) + return; + + if (!*buf) + *buf = ucv_stringbuf_new(); + ucv_stringbuf_addstr(*buf, str, next - str); +} + +static void +uc_uline_add_pos(uc_vm_t *vm, uc_value_t *list, ssize_t start, ssize_t end) +{ + uc_value_t *val = ucv_array_new(vm); + ucv_array_push(val, ucv_int64_new(start)); + ucv_array_push(val, ucv_int64_new(end)); + ucv_array_push(list, val); +} + +static uc_value_t * +uc_uline_parse_args(uc_vm_t *vm, size_t nargs, bool check) +{ + struct uc_arg_parser *argp = uc_fn_thisval("uline.argp"); + uc_value_t *list = NULL, *pos_list = NULL; + uc_value_t *args = NULL, *pos_args = NULL; + uc_value_t *str_arg = uc_fn_arg(0); + uc_stringbuf_t *buf = NULL; + uc_value_t *missing = NULL; + uc_value_t *ret; + const char *start, *str, *end; + ssize_t start_idx = -1, end_idx = 0; + enum { + UNQUOTED, + BACKSLASH, + SINGLE_QUOTE, + DOUBLE_QUOTE, + DOUBLE_QUOTE_BACKSLASH, + } state = UNQUOTED; + static const char * const state_str[] = { + [BACKSLASH] = "\\", + [SINGLE_QUOTE] = "'", + [DOUBLE_QUOTE] = "\"", + [DOUBLE_QUOTE_BACKSLASH] = "\\\"", + }; +#define UNQUOTE_TOKENS " \t\r\n'\"\\" + char unquote_tok[] = UNQUOTE_TOKENS "\x00"; + unquote_tok[strlen(UNQUOTE_TOKENS)] = argp->line_sep; + + if (!argp || ucv_type(str_arg) != UC_STRING) + return NULL; + + if (!check) { + list = ucv_array_new(vm); + pos_list = ucv_array_new(vm); + if (argp->line_sep) { + args = ucv_array_new(vm); + pos_args = ucv_array_new(vm); + ucv_array_push(args, list); + ucv_array_push(pos_args, pos_list); + } else { + args = list; + pos_args = pos_list; + } + } + + start = str = ucv_string_get(str_arg); + end = str + ucv_string_length(str_arg); + skip_space(&str, end); + + while (*str && str < end) { + const char *next; + + switch (state) { + case UNQUOTED: + if (isspace(*str)) { + skip_space(&str, end); + if (!buf) + continue; + + ucv_array_push(list, ucv_stringbuf_finish(buf)); + uc_uline_add_pos(vm, pos_list, start_idx, end_idx); + start_idx = -1; + buf = NULL; + continue; + } + + if (start_idx < 0) + start_idx = str - start; + next = str + strcspn(str, unquote_tok); + if (list) + add_str(&buf, str, next); + str = next; + end_idx = str - start; + + switch (*str) { + case 0: + continue; + case '\'': + state = SINGLE_QUOTE; + break; + case '"': + state = DOUBLE_QUOTE; + break; + case '\\': + state = BACKSLASH; + break; + default: + if (argp->line_sep && + *str == argp->line_sep) { + str++; + if (list) { + if (buf) { + ucv_array_push(list, ucv_stringbuf_finish(buf)); + uc_uline_add_pos(vm, pos_list, start_idx, end_idx); + start_idx = -1; + } + + buf = NULL; + list = ucv_array_new(vm); + ucv_array_push(args, list); + + pos_list = ucv_array_new(vm); + ucv_array_push(pos_args, pos_list); + } + } + continue; + } + if (!buf) + buf = ucv_stringbuf_new(); + str++; + break; + + case BACKSLASH: + case DOUBLE_QUOTE_BACKSLASH: + if (start_idx < 0) + start_idx = str - start; + if (list && *str != '\n') + add_str(&buf, str, str + 1); + str++; + state--; + end_idx = str - start; + break; + + case SINGLE_QUOTE: + if (start_idx < 0) + start_idx = str - start; + next = str + strcspn(str, "'"); + if (list) + add_str(&buf, str, next); + str = next; + + if (*str == '\'') { + state = UNQUOTED; + str++; + } + end_idx = str - start; + break; + + case DOUBLE_QUOTE: + if (start_idx < 0) + start_idx = str - start; + next = str + strcspn(str, "\"\\"); + if (list) + add_str(&buf, str, next); + str = next; + + if (*str == '"') { + state = UNQUOTED; + str++; + } else if (*str == '\\') { + state = DOUBLE_QUOTE_BACKSLASH; + str++; + } + end_idx = str - start; + } + } + + if (buf) { + ucv_array_push(list, ucv_stringbuf_finish(buf)); + uc_uline_add_pos(vm, pos_list, start_idx, end_idx); + } + + if (state_str[state]) + missing = ucv_string_new(state_str[state]); + + if (!list) + return missing; + + ret = ucv_object_new(vm); + ucv_object_add(ret, "args", args); + ucv_object_add(ret, "pos", pos_args); + if (missing) + ucv_object_add(ret, "missing", missing); + + return ret; +} + +static uc_value_t * +uc_uline_arg_parser(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *opts = uc_fn_arg(0); + struct uc_arg_parser *argp; + uc_value_t *a; + char sep = 0; + + if ((a = ucv_object_get(opts, "line_separator", NULL)) != NULL) { + if (ucv_type(a) != UC_STRING || ucv_string_length(a) != 1) + return NULL; + + sep = ucv_string_get(a)[0]; + } + + argp = calloc(1, sizeof(*argp)); + argp->line_sep = sep; + + return ucv_resource_new(argp_type, argp); +} + +static uc_value_t * +uc_uline_argp_parse(uc_vm_t *vm, size_t nargs) +{ + return uc_uline_parse_args(vm, nargs, false); +} + +static uc_value_t * +uc_uline_argp_check(uc_vm_t *vm, size_t nargs) +{ + return uc_uline_parse_args(vm, nargs, true); +} + +static uc_value_t * +uc_uline_argp_escape(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + uc_value_t *ref_arg = uc_fn_arg(1); + const char *str, *next; + uc_stringbuf_t *buf; + char ref = 0; + + if (ucv_type(arg) != UC_STRING) + return NULL; + + if (ucv_type(ref_arg) == UC_STRING) + ref = ucv_string_get(ref_arg)[0]; + + str = ucv_string_get(arg); + if (ref != '"' && ref != '\'') { + next = str + strcspn(str, "\n\t '\""); + if (*next) + ref = '"'; + } + if (ref != '"' && ref != '\'') + return ucv_string_new(str); + + buf = ucv_stringbuf_new(); + ucv_stringbuf_addstr(buf, &ref, 1); + + while (*str) { + next = strchr(str, ref); + if (!next) { + ucv_stringbuf_addstr(buf, str, strlen(str)); + break; + } + + if (next - str) + ucv_stringbuf_addstr(buf, str, next - str); + if (ref == '\'') + ucv_stringbuf_addstr(buf, "'\\''", 4); + else + ucv_stringbuf_addstr(buf, "\\\"", 2); + str = next + 1; + } + + ucv_stringbuf_addstr(buf, &ref, 1); + + return ucv_stringbuf_finish(buf); +} + +static uc_value_t * +uc_uline_getpass(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *prompt = uc_fn_arg(0); + char *pw; + + if (ucv_type(prompt) != UC_STRING) + return NULL; + + pw = getpass(ucv_string_get(prompt)); + if (!pw) + return NULL; + + return ucv_string_new(pw); +} + +static const uc_function_list_t argp_fns[] = { + { "parse", uc_uline_argp_parse }, + { "check", uc_uline_argp_check }, + { "escape", uc_uline_argp_escape }, +}; + +static const uc_function_list_t state_fns[] = { + { "close", uc_uline_close }, + { "poll", uc_uline_poll }, + { "poll_stop", uc_uline_poll_stop }, + { "poll_key", uc_uline_poll_key }, + { "reset_key_input", uc_uline_reset_key_input }, + { "get_line", uc_uline_get_line }, + { "get_window", uc_uline_get_window }, + { "set_hint", uc_uline_set_hint }, + { "set_state", uc_uline_set_state }, + { "set_uloop", uc_uline_set_uloop }, + { "hide_prompt", uc_uline_hide_prompt }, + { "refresh_prompt", uc_uline_refresh_prompt }, +}; + +static const uc_function_list_t global_fns[] = { + { "new", uc_uline_new }, + { "arg_parser", uc_uline_arg_parser }, + { "getpass", uc_uline_getpass }, +}; + +void uc_module_init(uc_vm_t *vm, uc_value_t *scope) +{ + uc_function_list_register(scope, global_fns); + + state_type = uc_type_declare(vm, "uline.state", state_fns, free_state); + argp_type = uc_type_declare(vm, "uline.argp", argp_fns, free); + registry = ucv_array_new(vm); + uc_vm_registry_set(vm, "uline.registry", registry); +} |