* Rename rz_list_find_ptr() to rz_list_find_val() * Rename rz_list_delete_data() to rz_list_delete_val()
305 lines
6.8 KiB
C
305 lines
6.8 KiB
C
// SPDX-FileCopyrightText: 2009-2018 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_lang.h>
|
|
#include <rz_util.h>
|
|
#include <rz_cons.h>
|
|
#include <rz_lib.h>
|
|
#include "rz_lang_plugins.h"
|
|
|
|
RZ_LIB_VERSION(rz_lang);
|
|
|
|
static RzLangPlugin *lang_static_plugins[] = { RZ_LANG_STATIC_PLUGINS };
|
|
|
|
static bool plugin_fini(RzLang *lang, RzLangPlugin *plugin) {
|
|
if (plugin->fini) {
|
|
return plugin->fini(lang);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RZ_API RzLang *rz_lang_new(void) {
|
|
RzLang *lang = RZ_NEW0(RzLang);
|
|
if (!lang) {
|
|
return NULL;
|
|
}
|
|
lang->sys_path = rz_path_new();
|
|
if (!lang->sys_path) {
|
|
free(lang);
|
|
return NULL;
|
|
}
|
|
lang->user = NULL;
|
|
lang->langs = rz_list_new();
|
|
if (!lang->langs) {
|
|
rz_lang_free(lang);
|
|
return NULL;
|
|
}
|
|
lang->defs = rz_list_new();
|
|
if (!lang->defs) {
|
|
rz_lang_free(lang);
|
|
return NULL;
|
|
}
|
|
lang->defs->free = (RzListFree)rz_lang_def_free;
|
|
lang->cb_printf = (PrintfCallback)printf;
|
|
for (int i = 0; i < RZ_ARRAY_SIZE(lang_static_plugins); i++) {
|
|
rz_lang_plugin_add(lang, lang_static_plugins[i]);
|
|
}
|
|
|
|
return lang;
|
|
}
|
|
|
|
RZ_API void rz_lang_free(RzLang *lang) {
|
|
if (!lang) {
|
|
return;
|
|
}
|
|
RzListIter *it;
|
|
RzLangPlugin *p;
|
|
|
|
rz_list_foreach (lang->langs, it, p) {
|
|
plugin_fini(lang, p);
|
|
}
|
|
|
|
rz_lang_undef(lang, NULL);
|
|
rz_list_free(lang->langs);
|
|
rz_list_free(lang->defs);
|
|
rz_path_free(lang->sys_path);
|
|
free(lang);
|
|
}
|
|
|
|
// XXX: This is only used actually to pass 'core' structure
|
|
// TODO: when language bindings are done we will need an api to
|
|
// define symbols from C to the language namespace
|
|
// XXX: Depcreate!!
|
|
RZ_API void rz_lang_set_user_ptr(RzLang *lang, void *user) {
|
|
lang->user = user;
|
|
}
|
|
|
|
RZ_API bool rz_lang_define(RzLang *lang, const char *type, const char *name, void *value) {
|
|
RzLangDef *def;
|
|
RzListIter *iter;
|
|
rz_list_foreach (lang->defs, iter, def) {
|
|
if (!rz_str_casecmp(name, def->name)) {
|
|
def->value = value;
|
|
return true;
|
|
}
|
|
}
|
|
def = RZ_NEW0(RzLangDef);
|
|
if (!def) {
|
|
return false;
|
|
}
|
|
def->type = rz_str_dup(type);
|
|
def->name = rz_str_dup(name);
|
|
def->value = value;
|
|
rz_list_append(lang->defs, def);
|
|
return true;
|
|
}
|
|
|
|
RZ_API void rz_lang_def_free(RzLangDef *def) {
|
|
free(def->name);
|
|
free(def->type);
|
|
free(def);
|
|
}
|
|
|
|
RZ_API void rz_lang_undef(RzLang *lang, const char *name) {
|
|
if (name && *name) {
|
|
RzLangDef *def;
|
|
RzListIter *iter;
|
|
/* No _safe loop necessary because we return immediately after the delete. */
|
|
rz_list_foreach (lang->defs, iter, def) {
|
|
if (!name || !rz_str_casecmp(name, def->name)) {
|
|
rz_list_delete(lang->defs, iter);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
rz_list_free(lang->defs);
|
|
lang->defs = NULL;
|
|
}
|
|
}
|
|
|
|
RZ_API bool rz_lang_setup(RzLang *lang) {
|
|
if (lang && lang->cur && lang->cur->setup) {
|
|
return lang->cur->setup(lang);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API bool rz_lang_plugin_add(RzLang *lang, RZ_NONNULL RzLangPlugin *plugin) {
|
|
rz_return_val_if_fail(lang && plugin && plugin->name, false);
|
|
if (rz_lang_get_by_name(lang, plugin->name)) {
|
|
return false;
|
|
}
|
|
rz_list_append(lang->langs, plugin);
|
|
if (plugin->init) {
|
|
plugin->init(lang);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_lang_plugin_del(RzLang *lang, RZ_NONNULL RzLangPlugin *plugin) {
|
|
rz_return_val_if_fail(lang && plugin, false);
|
|
if (!plugin_fini(lang, plugin)) {
|
|
return false;
|
|
}
|
|
return rz_list_delete_val(lang->langs, plugin);
|
|
}
|
|
|
|
RZ_API RzLangPlugin *rz_lang_get_by_extension(RzLang *lang, const char *ext) {
|
|
RzListIter *iter;
|
|
RzLangPlugin *h;
|
|
const char *p = rz_str_lchr(ext, '.');
|
|
if (p) {
|
|
ext = p + 1;
|
|
}
|
|
rz_list_foreach (lang->langs, iter, h) {
|
|
if (!rz_str_casecmp(h->ext, ext)) {
|
|
return h;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API RzLangPlugin *rz_lang_get_by_name(RzLang *lang, const char *name) {
|
|
RzListIter *iter;
|
|
RzLangPlugin *h;
|
|
rz_list_foreach (lang->langs, iter, h) {
|
|
if (!rz_str_casecmp(h->name, name)) {
|
|
return h;
|
|
}
|
|
if (h->alias && !rz_str_casecmp(h->alias, name)) {
|
|
return h;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API bool rz_lang_use(RzLang *lang, const char *name) {
|
|
RzLangPlugin *h = rz_lang_get_by_name(lang, name);
|
|
if (h) {
|
|
lang->cur = h;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// TODO: store in rz_lang and use it from the plugin?
|
|
RZ_API bool rz_lang_set_argv(RzLang *lang, int argc, char **argv) {
|
|
if (lang->cur && lang->cur->set_argv) {
|
|
return lang->cur->set_argv(lang, argc, argv);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API int rz_lang_run(RzLang *lang, const char *code, int len) {
|
|
if (lang->cur && lang->cur->run) {
|
|
return lang->cur->run(lang, code, len);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API int rz_lang_run_string(RzLang *lang, const char *code) {
|
|
return rz_lang_run(lang, code, strlen(code));
|
|
}
|
|
|
|
RZ_API int rz_lang_run_file(RzLang *lang, const char *file) {
|
|
int ret = false;
|
|
if (lang->cur) {
|
|
if (!lang->cur->run_file) {
|
|
if (lang->cur->run) {
|
|
size_t len;
|
|
char *code = rz_file_slurp(file, &len);
|
|
if (!code) {
|
|
eprintf("Could not open '%s'.\n", file);
|
|
return 0;
|
|
}
|
|
ret = lang->cur->run(lang, code, (int)len);
|
|
free(code);
|
|
}
|
|
} else {
|
|
ret = lang->cur->run_file(lang, file);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* TODO: deprecate or make it more modular .. reading from stdin in a lib! */
|
|
RZ_API int rz_lang_prompt(RzLang *lang) {
|
|
char buf[1024];
|
|
const char *p;
|
|
|
|
if (!lang || !lang->cur) {
|
|
return false;
|
|
}
|
|
|
|
if (lang->cur->prompt && lang->cur->prompt(lang)) {
|
|
return true;
|
|
}
|
|
/* init line */
|
|
RzLine *line = rz_cons_singleton()->line;
|
|
RzLineHistory hist = line->history;
|
|
RzLineHistory histnull = { 0 };
|
|
RzLineCompletion oc = line->completion;
|
|
RzLineCompletion ocnull = { 0 };
|
|
char *prompt = rz_str_dup(line->prompt);
|
|
line->completion = ocnull;
|
|
line->history = histnull;
|
|
|
|
/* foo */
|
|
for (;;) {
|
|
rz_cons_flush();
|
|
snprintf(buf, sizeof(buf) - 1, "%s> ", lang->cur->name);
|
|
rz_line_set_prompt(line, buf);
|
|
p = rz_line_readline(line);
|
|
if (!p) {
|
|
break;
|
|
}
|
|
rz_line_hist_add(line, p);
|
|
strncpy(buf, p, sizeof(buf) - 1);
|
|
if (*buf == '!') {
|
|
if (buf[1]) {
|
|
rz_sys_xsystem(buf + 1);
|
|
}
|
|
continue;
|
|
}
|
|
if (!memcmp(buf, ". ", 2)) {
|
|
char *file = rz_file_abspath(buf + 2);
|
|
if (file) {
|
|
rz_lang_run_file(lang, file);
|
|
free(file);
|
|
}
|
|
continue;
|
|
}
|
|
if (!strcmp(buf, "q")) {
|
|
free(prompt);
|
|
return true;
|
|
}
|
|
if (!strcmp(buf, "?")) {
|
|
RzLangDef *def;
|
|
RzListIter *iter;
|
|
eprintf(" ? - show this help message\n"
|
|
" !command - run system command\n"
|
|
" . file - interpret file\n"
|
|
" q - quit prompt\n");
|
|
eprintf("%s example:\n", lang->cur->name);
|
|
if (lang->cur->help) {
|
|
eprintf("%s", *lang->cur->help);
|
|
}
|
|
if (!rz_list_empty(lang->defs)) {
|
|
eprintf("variables:\n");
|
|
}
|
|
rz_list_foreach (lang->defs, iter, def) {
|
|
eprintf(" %s %s\n", def->type, def->name);
|
|
}
|
|
} else {
|
|
rz_lang_run(lang, buf, strlen(buf));
|
|
}
|
|
}
|
|
// XXX: leaking history
|
|
rz_line_set_prompt(line, prompt);
|
|
line->completion = oc;
|
|
line->history = hist;
|
|
clearerr(stdin);
|
|
printf("\n");
|
|
free(prompt);
|
|
return true;
|
|
}
|