Created
May 29, 2012 14:36
-
-
Save esmil/2828779 to your computer and use it in GitHub Desktop.
Experiments with GIO and DBus which ended in forth-like interpreter..
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <string.h> | |
#include <gio/gio.h> | |
static GMainLoop *mainloop; | |
static GDBusConnection *bus; | |
static int exitcode = EXIT_FAILURE; | |
#define AGENT_PATH "/agent" | |
#define STACK_SIZE 20 | |
static void | |
request_input(GVariant *par, GDBusMethodInvocation *inv) | |
{ | |
GVariantIter iter; | |
GVariant *obj = g_variant_get_child_value(par, 0); | |
GVariant *fields = g_variant_get_child_value(par, 1); | |
gchar *key; | |
GVariant *dict; | |
g_variant_unref(par); | |
g_print("%s:\n", g_variant_get_string(obj, NULL)); | |
g_variant_unref(obj); | |
g_variant_iter_init(&iter, fields); | |
while (g_variant_iter_next(&iter, "{sv}", &key, &dict)) { | |
g_print("%s\n", key); | |
g_free(key); | |
g_variant_unref(dict); | |
} | |
g_variant_unref(fields); | |
} | |
static void | |
agent_method_call(GDBusConnection *bus, | |
const gchar *sender, | |
const gchar *object_path, | |
const gchar *interface_name, | |
const gchar *method_name, | |
GVariant *par, | |
GDBusMethodInvocation *inv, | |
gpointer data) | |
{ | |
(void)bus; | |
(void)sender; | |
(void)object_path; | |
(void)interface_name; | |
(void)data; | |
g_print("agent_method_call\n"); | |
if (g_strcmp0(method_name, "RequestInput") == 0) | |
return request_input(par, inv); | |
} | |
#if 0 | |
static GVariant * | |
agent_get_property(GDBusConnection *bus, | |
const gchar *sender, | |
const gchar *object_path, | |
const gchar *interface_name, | |
const gchar *property_name, | |
GError **error, | |
gpointer user_data) | |
{ | |
g_print("agent_get_property\n"); | |
return NULL; | |
} | |
static gboolean | |
agent_set_property(GDBusConnection *bus, | |
const gchar *sender, | |
const gchar *object_path, | |
const gchar *interface_name, | |
const gchar *property_name, | |
GVariant *value, | |
GError **error, | |
gpointer user_data) | |
{ | |
g_print("agent_set_property\n"); | |
return FALSE; | |
} | |
#endif | |
static GDBusArgInfo service_arg = { | |
.ref_count = -1, | |
.name = "service", | |
.signature = "o", | |
}; | |
/* | |
* Release() | |
*/ | |
static GDBusMethodInfo release_info = { | |
.ref_count = -1, | |
.name = "Release", | |
}; | |
/* | |
* ReportError() | |
*/ | |
static GDBusArgInfo reporterror_in2 = { | |
.ref_count = -1, | |
.name = "error", | |
.signature = "s", | |
}; | |
static GDBusArgInfo *reporterror_in[] = { | |
&service_arg, | |
&reporterror_in2, | |
NULL | |
}; | |
static GDBusMethodInfo reporterror_info = { | |
.ref_count = -1, | |
.name = "ReportError", | |
.in_args = reporterror_in, | |
}; | |
/* | |
* RequestBrowser() | |
*/ | |
static GDBusArgInfo requestbrowser_in2 = { | |
.ref_count = -1, | |
.name = "url", | |
.signature = "s", | |
}; | |
static GDBusArgInfo *requestbrowser_in[] = { | |
&service_arg, | |
&requestbrowser_in2, | |
NULL | |
}; | |
static GDBusMethodInfo requestbrowser_info = { | |
.ref_count = -1, | |
.name = "RequestBrowser", | |
.in_args = requestbrowser_in, | |
}; | |
/* | |
* RequestInput() | |
*/ | |
static GDBusArgInfo requestinput_in2 = { | |
.ref_count = -1, | |
.name = "fields", | |
.signature = "a{sv}", | |
}; | |
static GDBusArgInfo *requestinput_in[] = { | |
&service_arg, | |
&requestinput_in2, | |
NULL | |
}; | |
static GDBusArgInfo requestinput_out1 = { | |
.ref_count = -1, | |
.signature = "a{sv}", | |
}; | |
static GDBusArgInfo *requestinput_out[] = { | |
&requestinput_out1, | |
NULL | |
}; | |
static GDBusMethodInfo requestinput_info = { | |
.ref_count = -1, | |
.name = "RequestInput", | |
.in_args = requestinput_in, | |
.out_args = requestinput_out, | |
}; | |
/* | |
* Cancel() | |
*/ | |
static GDBusMethodInfo cancel_info = { | |
.ref_count = -1, | |
.name = "Cancel", | |
}; | |
/* | |
* Agent | |
*/ | |
static GDBusMethodInfo *agent_methods[] = { | |
&release_info, | |
&reporterror_info, | |
&requestbrowser_info, | |
&requestinput_info, | |
&cancel_info, | |
NULL | |
}; | |
static GDBusInterfaceInfo agent_interface_info = { | |
.ref_count = -1, | |
.name = "net.connman.Agent", | |
.methods = agent_methods, | |
}; | |
static const GDBusInterfaceVTable agent_vtable = { | |
.method_call = agent_method_call, | |
/* | |
.get_property = agent_get_property, | |
.set_property = agent_set_property, | |
*/ | |
}; | |
enum stack_type { | |
STACK_EMPTY, | |
STACK_GUINT, | |
STACK_STRING, | |
STACK_GSTRING, | |
STACK_GVAR, | |
}; | |
struct stack_elem { | |
enum stack_type type; | |
union { | |
guint u; | |
const char *str; | |
gchar *gstr; | |
GVariant *gv; | |
}; | |
void (*free)(struct stack_elem *e); | |
}; | |
static struct stack_elem stack[STACK_SIZE]; | |
static guint stack_top; | |
static struct stack_elem * | |
stack_push(void) | |
{ | |
stack_top++; | |
g_assert(stack_top < STACK_SIZE); | |
return &stack[stack_top]; | |
} | |
static struct stack_elem * | |
stack_pop(void) | |
{ | |
g_assert(stack_top > 0); | |
return &stack[stack_top--]; | |
} | |
static void | |
stack_free(void) | |
{ | |
guint i; | |
for (i = stack_top; i > 0; i--) { | |
struct stack_elem *e = &stack[i]; | |
e->free(e); | |
} | |
} | |
#if 0 | |
static gchar *stack_type_name[] = { | |
"EMPTY", | |
"GUINT", | |
"STRING", | |
"GSTRING", | |
"GVAR", | |
}; | |
static void | |
stack_dump(void) | |
{ | |
guint i; | |
for (i = stack_top; i > 0; i--) | |
g_print("%s\n", stack_type_name[stack[i].type]); | |
} | |
#endif | |
static void | |
stack_free_noop(struct stack_elem *e) | |
{ | |
(void)e; /* noop */ | |
} | |
static void | |
stack_guint_push(guint u) | |
{ | |
struct stack_elem *e = stack_push(); | |
e->type = STACK_GUINT; | |
e->u = u; | |
e->free = stack_free_noop; | |
} | |
static guint | |
stack_guint_pop(void) | |
{ | |
struct stack_elem *e = stack_pop(); | |
g_assert(e->type == STACK_GUINT); | |
return e->u; | |
} | |
static void | |
stack_string_push(char *str) | |
{ | |
struct stack_elem *e = stack_push(); | |
e->type = STACK_STRING; | |
e->str = str; | |
e->free = stack_free_noop; | |
} | |
static const char * | |
stack_string_pop(void) | |
{ | |
struct stack_elem *e = stack_pop(); | |
g_assert(e->type == STACK_STRING); | |
return e->str; | |
} | |
static void | |
stack_gstring_free(struct stack_elem *e) | |
{ | |
g_free(e->gstr); | |
} | |
static void | |
stack_gstring_push(gchar *gstr) | |
{ | |
struct stack_elem *e = stack_push(); | |
e->type = STACK_GSTRING; | |
e->gstr = gstr; | |
e->free = stack_gstring_free; | |
} | |
static gchar * | |
stack_gstring_pop(void) | |
{ | |
struct stack_elem *e = stack_pop(); | |
g_assert(e->type == STACK_GSTRING); | |
return e->gstr; | |
} | |
static void | |
stack_gvar_free(struct stack_elem *e) | |
{ | |
g_variant_unref(e->gv); | |
} | |
static void | |
stack_gvar_push(GVariant *gv) | |
{ | |
struct stack_elem *e = stack_push(); | |
e->type = STACK_GVAR; | |
e->gv = gv; | |
e->free = stack_gvar_free; | |
} | |
static GVariant * | |
stack_gvar_pop(void) | |
{ | |
struct stack_elem *e = stack_pop(); | |
g_assert(e->type == STACK_GVAR); | |
return e->gv; | |
} | |
static void | |
bus_quit_cb(GObject *source, GAsyncResult *res, gpointer data) | |
{ | |
GError *err = NULL; | |
(void)source; | |
(void)data; | |
if (!g_dbus_connection_close_finish(bus, res, &err)) { | |
g_printerr("%s\n", err->message); | |
g_error_free(err); | |
exitcode = EXIT_FAILURE; | |
} | |
g_main_loop_quit(mainloop); | |
} | |
static void | |
quit(int ret) | |
{ | |
exitcode = ret; | |
stack_free(); | |
g_dbus_connection_close(bus, | |
NULL, | |
bus_quit_cb, | |
NULL); | |
} | |
enum ins_return { | |
INS_DONE, | |
INS_YIELD, | |
INS_ERROR, | |
}; | |
typedef enum ins_return (*ins_t)(void); | |
static const ins_t *ins_next; | |
static void | |
ins_run(void) | |
{ | |
do { | |
switch ((*ins_next++)()) { | |
case INS_DONE: | |
break; | |
case INS_YIELD: | |
return; | |
case INS_ERROR: | |
quit(EXIT_FAILURE); | |
return; | |
} | |
} while (*ins_next); | |
quit(EXIT_SUCCESS); | |
} | |
#if 0 | |
/* | |
* X -> () | |
*/ | |
static enum ins_return | |
ins_pop(void) | |
{ | |
struct stack_elem *e = stack_pop(); | |
e->free(e); | |
return INS_DONE; | |
} | |
#endif | |
/* | |
* () -> GSTRING | |
*/ | |
static enum ins_return | |
ins_push_wifi_path(void) | |
{ | |
stack_string_push("/net/connman/technology/wifi"); | |
return INS_DONE; | |
} | |
/* | |
* () -> GSTRING | |
*/ | |
static enum ins_return | |
ins_push_ethernet_path(void) | |
{ | |
stack_string_push("/net/connman/technology/ethernet"); | |
return INS_DONE; | |
} | |
static void | |
print_error(GError *err) | |
{ | |
g_printerr("%s\n", err->message); | |
g_error_free(err); | |
} | |
static void | |
bus_call_cb(GObject *source, GAsyncResult *res, gpointer data) | |
{ | |
GVariant *ret; | |
GError *err = NULL; | |
(void)source; | |
(void)data; | |
ret = g_dbus_connection_call_finish(bus, res, &err); | |
if (err != NULL) { | |
print_error(err); | |
quit(EXIT_FAILURE); | |
return; | |
} | |
if (g_variant_is_of_type(ret, G_VARIANT_TYPE_TUPLE) && | |
g_variant_n_children(ret) == 1) { | |
GVariant *child = g_variant_get_child_value(ret, 0); | |
g_variant_unref(ret); | |
ret = child; | |
} | |
stack_gvar_push(ret); | |
return ins_run(); | |
} | |
static void | |
bus_call(const gchar *object_path, | |
const gchar *interface_name, | |
const gchar *method_name, | |
GVariant *parameters, | |
const GVariantType *reply_type) | |
{ | |
g_dbus_connection_call(bus, | |
"net.connman", | |
object_path, | |
interface_name, | |
method_name, | |
parameters, | |
reply_type, | |
G_DBUS_CALL_FLAGS_NONE, | |
-1, | |
NULL, | |
bus_call_cb, | |
NULL); | |
} | |
static GVariant * | |
service_get(GVariant *array, guint index) | |
{ | |
GVariant *entry; | |
g_assert(g_variant_is_of_type(array, G_VARIANT_TYPE_ARRAY)); | |
if (index > g_variant_n_children(array)) { | |
g_printerr("Invalid service index %u\n", index); | |
return NULL; | |
} | |
entry = g_variant_get_child_value(array, index-1); | |
g_variant_unref(array); | |
g_assert(g_variant_is_of_type(entry, G_VARIANT_TYPE_TUPLE)); | |
return entry; | |
} | |
static gchar * | |
service_get_path(GVariant *array, guint index) | |
{ | |
GVariant *entry = service_get(array, index); | |
GVariant *obj; | |
gchar *ret; | |
if (entry == NULL) | |
return NULL; | |
obj = g_variant_get_child_value(entry, 0); | |
g_variant_unref(entry); | |
g_assert(g_variant_is_of_type(obj, G_VARIANT_TYPE_OBJECT_PATH)); | |
ret = g_variant_dup_string(obj, NULL); | |
g_variant_unref(obj); | |
return ret; | |
} | |
static GVariant * | |
service_get_dict(GVariant *array, guint index) | |
{ | |
GVariant *entry = service_get(array, index); | |
GVariant *dict; | |
if (entry == NULL) | |
return NULL; | |
dict = g_variant_get_child_value(entry, 1); | |
g_variant_unref(entry); | |
g_assert(g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); | |
return dict; | |
} | |
/* | |
* () -> GVAR | |
*/ | |
static enum ins_return | |
ins_call_manager_getservices(void) | |
{ | |
bus_call("/", | |
"net.connman.Manager", | |
"GetServices", | |
NULL, | |
G_VARIANT_TYPE("(a(oa{sv}))")); | |
return INS_YIELD; | |
} | |
/* | |
* STRING -> GVAR | |
*/ | |
static enum ins_return | |
ins_call_technology_getproperties(void) | |
{ | |
const char *obj_path = stack_string_pop(); | |
bus_call(obj_path, | |
"net.connman.Technology", | |
"GetProperties", | |
NULL, | |
G_VARIANT_TYPE("(a{sv})")); | |
return INS_YIELD; | |
} | |
/* | |
* STRING -> GVAR | |
*/ | |
static enum ins_return | |
ins_call_technology_scan(void) | |
{ | |
const char *obj_path = stack_string_pop(); | |
bus_call(obj_path, | |
"net.connman.Technology", | |
"Scan", | |
NULL, | |
G_VARIANT_TYPE("(a{sv})")); | |
return INS_YIELD; | |
} | |
/* | |
* () -> GVAR | |
*/ | |
static enum ins_return | |
ins_register_agent(void) | |
{ | |
GError *err = NULL; | |
GVariant *children[1]; | |
g_print("Registering agent.. "); | |
g_dbus_connection_register_object(bus, | |
AGENT_PATH, | |
&agent_interface_info, | |
&agent_vtable, | |
NULL, | |
NULL, | |
&err); | |
if (err != NULL) { | |
print_error(err); | |
return INS_ERROR; | |
} | |
children[0] = g_variant_new_object_path(AGENT_PATH); | |
bus_call("/", | |
"net.connman.Manager", | |
"RegisterAgent", | |
g_variant_new_tuple(children, 1), | |
NULL); | |
return INS_YIELD; | |
} | |
/* | |
* GVAR GUINT -> GVAR | |
*/ | |
static enum ins_return | |
ins_call_service_connect(void) | |
{ | |
GVariant *array = stack_gvar_pop(); | |
guint index = stack_guint_pop(); | |
gchar *obj_path = service_get_path(array, index); | |
if (obj_path == NULL) | |
return INS_ERROR; | |
g_print("Connecting to service.. "); | |
bus_call(obj_path, | |
"net.connman.Service", | |
"Connect", | |
NULL, | |
NULL); | |
g_free(obj_path); | |
return INS_YIELD; | |
} | |
/* | |
* GVAR GUINT -> GVAR | |
*/ | |
static enum ins_return | |
ins_call_service_remove(void) | |
{ | |
GVariant *array = stack_gvar_pop(); | |
guint index = stack_guint_pop(); | |
gchar *obj_path = service_get_path(array, index); | |
if (obj_path == NULL) | |
return INS_ERROR; | |
g_print("Removing service.. "); | |
bus_call(obj_path, | |
"net.connman.Service", | |
"Remove", | |
NULL, | |
NULL); | |
g_free(obj_path); | |
return INS_YIELD; | |
} | |
static void | |
print_key_boolean(GVariant *dict, const gchar *key) | |
{ | |
GVariant *val = g_variant_lookup_value(dict, key, G_VARIANT_TYPE("b")); | |
g_print("%s: %s\n", key, g_variant_get_boolean(val) ? "true" : "false"); | |
g_variant_unref(val); | |
} | |
static void | |
print_key_string(GVariant *dict, const gchar *key) | |
{ | |
GVariant *val = g_variant_lookup_value(dict, key, G_VARIANT_TYPE("s")); | |
g_print("%s: %s\n", key, g_variant_get_string(val, NULL)); | |
g_variant_unref(val); | |
} | |
/* | |
* GVAR -> () | |
*/ | |
static enum ins_return | |
ins_print_ok(void) | |
{ | |
GVariant *ret = stack_gvar_pop(); | |
g_variant_unref(ret); | |
g_print("OK\n"); | |
return INS_DONE; | |
} | |
/* | |
* GVAR -> () | |
*/ | |
static enum ins_return | |
ins_print_service_list(void) | |
{ | |
GVariant *array = stack_gvar_pop(); | |
GVariant *entry; | |
GVariantIter iter; | |
guint n; | |
g_variant_iter_init(&iter, array); | |
n = 1; | |
while ((entry = g_variant_iter_next_value(&iter))) { | |
GVariant *dict = g_variant_get_child_value(entry, 1); | |
GVariant *fav = g_variant_lookup_value(dict, | |
"Favorite", | |
G_VARIANT_TYPE("b")); | |
GVariant *name = g_variant_lookup_value(dict, | |
"Name", | |
G_VARIANT_TYPE("s")); | |
GVariant *type = g_variant_lookup_value(dict, | |
"Type", | |
G_VARIANT_TYPE("s")); | |
GVariant *state = g_variant_lookup_value(dict, | |
"State", | |
G_VARIANT_TYPE("s")); | |
g_print("%3u: %c %s [%s,%s]\n", n++, | |
g_variant_get_boolean(fav) ? '*' : ' ', | |
name ? g_variant_get_string(name, NULL) : "<no name>", | |
g_variant_get_string(type, NULL), | |
g_variant_get_string(state, NULL)); | |
g_variant_unref(fav); | |
if (name) | |
g_variant_unref(name); | |
g_variant_unref(type); | |
g_variant_unref(state); | |
g_variant_unref(dict); | |
g_variant_unref(entry); | |
} | |
g_variant_unref(array); | |
return INS_DONE; | |
} | |
/* | |
* GVAR GUINT -> () | |
*/ | |
static enum ins_return | |
ins_print_service(void) | |
{ | |
GVariant *array = stack_gvar_pop(); | |
guint index = stack_guint_pop(); | |
GVariant *dict = service_get_dict(array, index); | |
print_key_string(dict, "Name"); | |
print_key_string(dict, "State"); | |
print_key_boolean(dict, "AutoConnect"); | |
g_variant_unref(dict); | |
return INS_DONE; | |
} | |
/* | |
* GVAR -> () | |
*/ | |
static enum ins_return | |
ins_print_technology(void) | |
{ | |
GVariant *dict = stack_gvar_pop(); | |
print_key_boolean(dict, "Powered"); | |
print_key_boolean(dict, "Connected"); | |
print_key_boolean(dict, "Tethering"); | |
g_variant_unref(dict); | |
return INS_DONE; | |
} | |
static const ins_t root[] = { | |
ins_call_manager_getservices, | |
ins_print_service_list, | |
NULL | |
}; | |
static const ins_t service[] = { | |
ins_call_manager_getservices, | |
ins_print_service, | |
NULL | |
}; | |
static const ins_t service_connect[] = { | |
ins_call_manager_getservices, | |
ins_register_agent, | |
ins_print_ok, | |
ins_call_service_connect, | |
ins_print_ok, | |
NULL | |
}; | |
static const ins_t service_remove[] = { | |
ins_call_manager_getservices, | |
ins_call_service_remove, | |
ins_print_ok, | |
NULL | |
}; | |
static const ins_t ethernet[] = { | |
ins_push_ethernet_path, | |
ins_call_technology_getproperties, | |
ins_print_technology, | |
NULL | |
}; | |
static const ins_t ethernet_scan[] = { | |
ins_push_ethernet_path, | |
ins_call_technology_scan, | |
ins_print_ok, | |
NULL | |
}; | |
static const ins_t wifi[] = { | |
ins_push_wifi_path, | |
ins_call_technology_getproperties, | |
ins_print_technology, | |
NULL | |
}; | |
static const ins_t wifi_scan[] = { | |
ins_push_wifi_path, | |
ins_call_technology_scan, | |
ins_print_ok, | |
NULL | |
}; | |
static void | |
bus_get_cb(GObject *source, GAsyncResult *res, gpointer data) | |
{ | |
GError *err = NULL; | |
(void)source; | |
(void)data; | |
bus = g_bus_get_finish(res, &err); | |
if (bus != NULL) | |
return ins_run(); | |
g_printerr("%s\n", err->message); | |
g_error_free(err); | |
exitcode = EXIT_FAILURE; | |
g_main_loop_quit(mainloop); | |
} | |
static int | |
loop(void) | |
{ | |
mainloop = g_main_loop_new(NULL, FALSE); | |
g_bus_get(G_BUS_TYPE_SYSTEM, | |
NULL, | |
bus_get_cb, | |
NULL); | |
g_main_loop_run(mainloop); | |
g_main_loop_unref(mainloop); | |
return exitcode; | |
} | |
static int | |
help(void) | |
{ | |
g_print("Hello, World!\n"); | |
return EXIT_SUCCESS; | |
} | |
struct cmd_node; | |
struct cmd_list { | |
const char *cmd; | |
const struct cmd_node *node; | |
}; | |
struct cmd_node { | |
union { | |
const ins_t *program; | |
int (*func)(void); | |
}; | |
gboolean noloop; | |
const struct cmd_node *num; | |
struct cmd_list cmds[]; | |
}; | |
static const struct cmd_node service_connect_cmds = { | |
.program = service_connect, | |
.cmds = { { NULL } } | |
}; | |
static const struct cmd_node service_remove_cmds = { | |
.program = service_remove, | |
.cmds = { { NULL } }, | |
}; | |
static const struct cmd_node service_cmds = { | |
.program = service, | |
.cmds = { | |
{ "connect", &service_connect_cmds }, | |
{ "remove", &service_remove_cmds }, | |
{ NULL }, | |
} | |
}; | |
static const struct cmd_node ethernet_scan_cmds = { | |
.program = ethernet_scan, | |
.cmds = { { NULL } } | |
}; | |
static const struct cmd_node ethernet_cmds = { | |
.program = ethernet, | |
.cmds = { | |
{ "scan", ðernet_scan_cmds }, | |
{ NULL } | |
} | |
}; | |
static const struct cmd_node wifi_scan_cmds = { | |
.program = wifi_scan, | |
.cmds = { { NULL } } | |
}; | |
static const struct cmd_node wifi_cmds = { | |
.program = wifi, | |
.cmds = { | |
{ "scan", &wifi_scan_cmds }, | |
{ NULL } | |
} | |
}; | |
static const struct cmd_node help_cmds = { | |
.func = help, | |
.noloop = TRUE, | |
.cmds = { { NULL } } | |
}; | |
static const struct cmd_node root_cmds = { | |
.program = root, | |
.num = &service_cmds, | |
.cmds = { | |
{ "ethernet", ðernet_cmds }, | |
{ "wifi", &wifi_cmds }, | |
{ "help", &help_cmds }, | |
{ NULL }, | |
} | |
}; | |
static gboolean | |
str_to_index(const char *str) | |
{ | |
guint d; | |
if (*str < '1' || *str > '9') | |
return FALSE; | |
for (d = *str++ - '0'; *str >= '0' && *str <= '9'; str++) { | |
d *= 10; | |
d += *str - '0'; | |
} | |
if (*str != '\0') | |
return FALSE; | |
stack_guint_push(d); | |
return TRUE; | |
} | |
int | |
main(int argc, char **argv) | |
{ | |
const struct cmd_node *node = &root_cmds; | |
int i; | |
g_type_init(); | |
for (i = 1; i < argc; i++) { | |
const char *arg = argv[i]; | |
unsigned int len; | |
const struct cmd_list *c; | |
if (node->num && str_to_index(arg)) { | |
node = node->num; | |
continue; | |
} | |
len = strlen(arg); | |
for (c = node->cmds;; c++) { | |
if (c->cmd == NULL) { | |
g_printerr("Unknown command \"%s\", " | |
"try \"cmi help\".\n", arg); | |
return EXIT_FAILURE; | |
} | |
if (len > strlen(c->cmd)) | |
continue; | |
if (memcmp(arg, c->cmd, len) == 0) { | |
node = c->node; | |
break; | |
} | |
} | |
} | |
if (node->noloop) | |
return node->func(); | |
ins_next = node->program; | |
return loop(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment