summaryrefslogtreecommitdiffhomepage
path: root/mixlib/mix_vm_command.c
diff options
context:
space:
mode:
Diffstat (limited to 'mixlib/mix_vm_command.c')
-rw-r--r--mixlib/mix_vm_command.c641
1 files changed, 641 insertions, 0 deletions
diff --git a/mixlib/mix_vm_command.c b/mixlib/mix_vm_command.c
new file mode 100644
index 0000000..350dae5
--- /dev/null
+++ b/mixlib/mix_vm_command.c
@@ -0,0 +1,641 @@
+/* -*-c-*- -------------- mix_vm_command.c :
+ * Implementation of the functions declared in mix_vm_command.h
+ * ------------------------------------------------------------------
+ * $Id: mix_vm_command.c,v 1.30 2005/09/20 19:43:13 jao Exp $
+ * ------------------------------------------------------------------
+ * Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#include "xmix_vm_command.h"
+#include "xmix_vm_handlers.h"
+
+#ifdef HAVE_LIBHISTORY
+# include <readline/history.h>
+#endif
+
+#ifdef HAVE_LIBREADLINE
+# include <readline/readline.h>
+#endif
+
+/* hook execution */
+static void
+exec_hook_list_ (GSList *list, mix_vm_cmd_dispatcher_t *dis, const gchar *arg)
+{
+ while (list)
+ {
+ hook_ *hook = (hook_ *)list->data;
+ if (hook)
+ (hook->func)(dis, arg, hook->data);
+ list = list->next;
+ }
+}
+
+static void
+exec_global_hook_list_ (GSList *list,
+ mix_vm_cmd_dispatcher_t *dis,
+ mix_vm_command_t cmd, const gchar *arg)
+{
+ while (list)
+ {
+ global_hook_ *hook = (global_hook_ *)list->data;
+ if (hook)
+ (hook->func)(dis, cmd, arg, hook->data);
+ list = list->next;
+ }
+}
+
+/* conversion from/to commands to strings */
+const gchar *
+mix_vm_command_to_string (mix_vm_command_t cmd)
+{
+ if (cmd < MIX_CMD_INVALID) return commands_[cmd].name;
+ else return NULL;
+}
+
+mix_vm_command_t
+mix_vm_command_from_string (const gchar *name)
+{
+ gint cmd = 0;
+ while (cmd < MIX_CMD_INVALID && strcmp (name, commands_[cmd].name))
+ ++cmd;
+ return cmd;
+}
+
+/* get help string about a command */
+const gchar *
+mix_vm_command_help (mix_vm_command_t cmd)
+{
+ if (cmd < MIX_CMD_INVALID) return commands_[cmd].doc;
+ else return NULL;
+}
+
+const gchar *
+mix_vm_command_usage (mix_vm_command_t cmd)
+{
+ if (cmd < MIX_CMD_INVALID) return commands_[cmd].usage;
+ else return NULL;
+}
+
+/* create a new command dispatcher */
+static GCompletion *
+make_completions_ (void)
+{
+ GList *cmds = NULL;
+ gint k;
+
+ GCompletion *completions = g_completion_new (NULL);
+ for (k = 0; k < MIX_CMD_INVALID; ++k)
+ cmds = g_list_append (cmds, (gpointer) mix_vm_command_to_string (k));
+ g_completion_add_items (completions, cmds);
+ return completions;
+}
+
+mix_vm_cmd_dispatcher_t *
+mix_vm_cmd_dispatcher_new (FILE *out_fd, /* output messages file */
+ FILE *err_fd /* error messages file */)
+{
+ mix_vm_cmd_dispatcher_t *result = NULL;
+ int k;
+
+ /* g_return_val_if_fail (out_fd && err_fd, NULL); */
+
+ result = g_new (mix_vm_cmd_dispatcher_t, 1);
+ result->result = TRUE;
+ result->out = out_fd;
+ result->err = err_fd;
+ result->log_msg = TRUE;
+ result->uptime = result->laptime = result->progtime = 0;
+ result->printtime = TRUE;
+ result->trace = FALSE;
+ result->program = NULL;
+ result->editor = NULL;
+ result->assembler = NULL;
+ result->eval = mix_eval_new ();
+ result->dump = mix_dump_context_new (out_fd,
+ MIX_SHORT_ZERO, MIX_SHORT_ZERO,
+ MIX_DUMP_ALL);
+ result->vm = mix_vm_new ();
+ result->global_pre = result->global_post = NULL;
+
+ for (k =0; k < MIX_CMD_INVALID; ++k)
+ result->pre_hooks[k] = result->post_hooks[k] = NULL;
+
+ result->config = NULL;
+
+ for (k = 0; k < PRNO_; ++k)
+ result->preds[k] = mix_predicate_new (k);
+
+ result->mem_preds = g_hash_table_new (NULL, NULL);
+ result->commands = g_hash_table_new (g_str_hash, g_str_equal);
+ result->completions = make_completions_ ();
+
+ return result;
+}
+
+mix_vm_cmd_dispatcher_t *
+mix_vm_cmd_dispatcher_new_with_config (FILE *out, FILE *err,
+ mix_config_t *config)
+{
+ mix_vm_cmd_dispatcher_t *result = mix_vm_cmd_dispatcher_new (out, err);
+ if (result != NULL && (result->config = config) != NULL)
+ {
+ gint hsize = 0;
+ const gchar *val = mix_config_get (result->config, TRACING_KEY_);
+ if (val) cmd_strace_ (result, val);
+ val = mix_config_get (result->config, EDITOR_KEY_);
+ if (val) mix_vm_cmd_dispatcher_set_editor (result, val);
+ val = mix_config_get (result->config, ASM_KEY_);
+ if (val) mix_vm_cmd_dispatcher_set_assembler (result, val);
+ val = mix_config_get (result->config, TIMING_KEY_);
+ if (val) cmd_stime_ (result, val);
+ val = mix_config_get_devices_dir (result->config);
+ if (!val || !mix_stat_dir (val, "devices"))
+ {
+ gchar *dirname =
+ g_path_get_dirname (mix_config_get_filename (config));
+ cmd_sddir_ (result, dirname);
+ g_free (dirname);
+ }
+ else
+ mix_device_set_dir (val);
+ val = mix_config_get (result->config, LOGGING_KEY_);
+ if (val) cmd_slog_ (result, val);
+#ifdef HAVE_LIBHISTORY
+ val = mix_config_get_history_file (result->config);
+ hsize = mix_config_get_history_size (result->config);
+ using_history ();
+ stifle_history (hsize);
+ if (val)
+ {
+ read_history ((char *)val);
+ history_set_pos (history_base + history_length - 1);
+ }
+#endif
+ }
+ return result;
+}
+
+
+/* delete (does not close the fds in the constructor) */
+static gboolean
+del_pred_ (gpointer key, gpointer val, gpointer data)
+{
+ if (val) mix_predicate_delete ((mix_predicate_t *)val);
+ return TRUE;
+}
+
+static void
+del_hook_ (gpointer data, gpointer ignored)
+{
+ if (data) g_free (data);
+}
+
+static void
+del_hook_list_ (GSList *s)
+{
+ if (s)
+ {
+ g_slist_foreach (s, del_hook_, NULL);
+ g_slist_free (s);
+ }
+}
+
+void
+mix_vm_cmd_dispatcher_delete (mix_vm_cmd_dispatcher_t *dis)
+{
+ const gchar *hfile = NULL;
+ gint k;
+
+ g_return_if_fail (dis != NULL);
+ mix_eval_delete (dis->eval);
+ mix_dump_context_delete (dis->dump);
+ mix_vm_delete (dis->vm);
+ if (dis->editor) g_free (dis->editor);
+ if (dis->editor) g_free (dis->assembler);
+#ifdef HAVE_LIBHISTORY
+ if (dis->config && (hfile = mix_config_get_history_file
+ (dis->config)))
+ write_history ((char *)hfile);
+#endif
+ for (k = 0; k < PRNO_; ++k) mix_predicate_delete (dis->preds[k]);
+ g_hash_table_foreach_remove (dis->mem_preds, del_pred_, NULL);
+ g_hash_table_destroy (dis->mem_preds);
+ g_hash_table_destroy (dis->commands);
+ g_completion_free (dis->completions);
+ for (k = 0; k < MIX_CMD_INVALID; ++k)
+ {
+ del_hook_list_ (dis->pre_hooks[k]);
+ del_hook_list_ (dis->post_hooks[k]);
+ }
+ del_hook_list_ (dis->global_pre);
+ del_hook_list_ (dis->global_post);
+ g_free (dis);
+}
+
+/* register new commands for a dispatcher */
+void
+mix_vm_cmd_dispatcher_register_new (mix_vm_cmd_dispatcher_t *dis,
+ mix_vm_command_info_t *cmd)
+{
+ GList *list = NULL;
+ g_return_if_fail (dis != NULL);
+ g_return_if_fail (cmd != NULL);
+ g_hash_table_insert (dis->commands, (gpointer)cmd->name, (gpointer)cmd);
+ list = g_list_append (list, (gpointer)cmd->name);
+ g_completion_add_items (dis->completions, list);
+}
+
+const GList *
+mix_vm_cmd_dispatcher_complete (const mix_vm_cmd_dispatcher_t *dis,
+ const gchar *cmd, gchar **prefix)
+{
+ char *cp;
+ GList *result;
+
+ g_return_val_if_fail (dis != NULL, NULL);
+ g_return_val_if_fail (cmd != NULL, NULL);
+
+ cp = g_strdup (cmd);
+ result = g_completion_complete (dis->completions, cp, prefix);
+ g_free (cp);
+ return result;
+}
+
+/* set/get out/error streams */
+FILE *
+mix_vm_cmd_dispatcher_get_out_stream (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis, NULL);
+ return dis->out;
+}
+
+FILE *
+mix_vm_cmd_dispatcher_get_err_stream (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis, NULL);
+ return dis->err;
+}
+
+FILE * /* old output stream */
+mix_vm_cmd_dispatcher_set_out_stream (mix_vm_cmd_dispatcher_t *dis, FILE *out)
+{
+ FILE *old = NULL;
+ g_return_val_if_fail (dis != NULL, old);
+ old = dis->out;
+ dis->out = out;
+ dis->dump->channel = out;
+ return old;
+}
+
+FILE * /* old error stream */
+mix_vm_cmd_dispatcher_set_error_stream (mix_vm_cmd_dispatcher_t *dis,
+ FILE *err)
+{
+ FILE *old = NULL;
+ g_return_val_if_fail (dis != NULL, old);
+ old = dis->err;
+ dis->err = err;
+ return old;
+}
+
+
+/* set editor and compiler templates */
+void
+mix_vm_cmd_dispatcher_set_editor (mix_vm_cmd_dispatcher_t *dis,
+ const gchar *edit_tplt)
+{
+ g_return_if_fail (dis != NULL);
+ if (dis->editor) g_free (dis->editor);
+ dis->editor = (edit_tplt) ? g_strdup (edit_tplt) : NULL;
+ if (dis->config && dis->editor)
+ mix_config_update (dis->config, EDITOR_KEY_, dis->editor);
+}
+
+void
+mix_vm_cmd_dispatcher_set_assembler (mix_vm_cmd_dispatcher_t *dis,
+ const gchar *asm_tplt)
+{
+ g_return_if_fail (dis != NULL);
+ if (dis->assembler) g_free (dis->assembler);
+ dis->assembler = (asm_tplt) ? g_strdup (asm_tplt) : NULL;
+ if (dis->config && dis->assembler)
+ mix_config_update (dis->config, ASM_KEY_, dis->assembler);
+}
+
+const gchar *
+mix_vm_cmd_dispatcher_get_editor (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, NULL);
+ return dis->editor;
+}
+
+const gchar *
+mix_vm_cmd_dispatcher_get_assembler (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, NULL);
+ return dis->assembler;
+}
+
+const gchar *
+mix_vm_cmd_dispatcher_get_src_file_path (const mix_vm_cmd_dispatcher_t *dis)
+{
+ static gchar *PATH = NULL;
+
+ const mix_vm_t *vm = mix_vm_cmd_dispatcher_get_vm (dis);
+ const mix_src_file_t *f = mix_vm_get_src_file (vm);
+
+ if (PATH)
+ {
+ g_free (PATH);
+ PATH = NULL;
+ }
+
+ if (f)
+ PATH = mix_file_complete_name (mix_src_file_get_path (f), MIX_SRC_DEFEXT);
+
+ return PATH;
+}
+
+const gchar *
+mix_vm_cmd_dispatcher_get_program_path (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, NULL);
+ return dis->program;
+}
+
+/* install hooks */
+void
+mix_vm_cmd_dispatcher_pre_hook (mix_vm_cmd_dispatcher_t *dis,
+ mix_vm_command_t cmd,
+ mix_vm_cmd_hook_t hook, gpointer data)
+{
+ hook_ *phook;
+
+ g_return_if_fail (dis != NULL);
+ g_return_if_fail (cmd < MIX_CMD_INVALID);
+
+ phook = g_new (hook_, 1);
+ phook->func = hook;
+ phook->data = data;
+
+ dis->pre_hooks[cmd] = g_slist_append (dis->pre_hooks[cmd], phook);
+}
+
+void
+mix_vm_cmd_dispatcher_post_hook (mix_vm_cmd_dispatcher_t *dis,
+ mix_vm_command_t cmd,
+ mix_vm_cmd_hook_t hook, gpointer data)
+{
+ hook_ *phook;
+
+ g_return_if_fail (dis != NULL);
+ g_return_if_fail (cmd < MIX_CMD_INVALID);
+
+ phook = g_new (hook_, 1);
+ phook->func = hook;
+ phook->data = data;
+
+ dis->post_hooks[cmd] = g_slist_append (dis->post_hooks[cmd], phook);
+}
+
+void
+mix_vm_cmd_dispatcher_global_pre_hook (mix_vm_cmd_dispatcher_t *dis,
+ mix_vm_cmd_global_hook_t hook,
+ gpointer data)
+{
+ global_hook_ *phook;
+
+ g_return_if_fail (dis != NULL);
+
+ phook = g_new (global_hook_, 1);
+ phook->func = hook;
+ phook->data = data;
+
+ dis->global_pre = g_slist_append (dis->global_pre, phook);
+}
+
+void
+mix_vm_cmd_dispatcher_global_post_hook (mix_vm_cmd_dispatcher_t *dis,
+ mix_vm_cmd_global_hook_t hook,
+ gpointer data)
+{
+ global_hook_ *phook;
+
+ g_return_if_fail (dis != NULL);
+
+ phook = g_new (global_hook_, 1);
+ phook->func = hook;
+ phook->data = data;
+
+ dis->global_post = g_slist_append (dis->global_post, phook);
+}
+
+/* dispatch a command */
+gboolean /* TRUE if success, FALSE otherwise */
+mix_vm_cmd_dispatcher_dispatch (mix_vm_cmd_dispatcher_t *dis,
+ mix_vm_command_t cmd, const gchar *arg)
+{
+ g_return_val_if_fail (dis != NULL, FALSE);
+
+ if (dis->global_pre)
+ exec_global_hook_list_ (dis->global_pre, dis, cmd, arg);
+
+ if (cmd < MIX_CMD_INVALID)
+ {
+ if (dis->pre_hooks[cmd])
+ exec_hook_list_ (dis->pre_hooks[cmd], dis, arg);
+
+ fflush (dis->out);
+ fflush (dis->err);
+
+ dis->result = (commands_[cmd].func)(dis, arg);
+
+ fflush (dis->out);
+ fflush (dis->err);
+
+ if (dis->post_hooks[cmd])
+ exec_hook_list_ (dis->post_hooks[cmd], dis, arg);
+
+ fflush (dis->out);
+ fflush (dis->err);
+ }
+ else
+ {
+ fprintf (dis->err, _("Unknown command. Try: help\n"));
+ }
+
+ if (dis->global_post)
+ exec_global_hook_list_ (dis->global_post, dis, cmd, arg);
+
+ fflush (dis->out);
+ fflush (dis->err);
+ return dis->result;
+}
+
+/* dispatch a command in text format */
+gboolean
+mix_vm_cmd_dispatcher_dispatch_text (mix_vm_cmd_dispatcher_t *dis,
+ const gchar *text)
+{
+ gchar *cp, *arg = "";
+ int k = 0;
+
+ g_return_val_if_fail (dis != NULL, FALSE);
+ g_return_val_if_fail (text != NULL, FALSE);
+
+ cp = g_strdup (text);
+ while (cp[k] && !isspace (cp[k])) ++k;
+ if (cp[k])
+ {
+ cp[k] = '\0'; ++k;
+ while (cp[k] && isspace (cp[k])) ++k;
+ arg = cp + k;
+ }
+
+ (void) mix_vm_cmd_dispatcher_dispatch_split_text (dis, cp, arg);
+
+ g_free (cp);
+
+ return dis->result;
+}
+
+/* dispatch a command in text format, with command and arg split */
+gboolean
+mix_vm_cmd_dispatcher_dispatch_split_text (mix_vm_cmd_dispatcher_t *dis,
+ const gchar *command,
+ const gchar *arg)
+{
+ mix_vm_command_info_t *info;
+
+ g_return_val_if_fail (dis, FALSE);
+
+ if (!command) return FALSE;
+ if (!arg) arg = "";
+
+ info = (mix_vm_command_info_t *)g_hash_table_lookup (dis->commands, command);
+
+ if (info)
+ {
+ if (dis->global_pre)
+ exec_global_hook_list_ (dis->global_pre, dis, MIX_CMD_LOCAL, arg);
+
+ fflush (dis->out);
+ fflush (dis->err);
+
+ dis->result = info->func (dis, arg);
+
+ fflush (dis->out);
+ fflush (dis->err);
+
+ if (dis->global_post)
+ exec_global_hook_list_ (dis->global_post, dis, MIX_CMD_LOCAL, arg);
+
+ fflush (dis->out);
+ fflush (dis->err);
+ }
+ else
+ dis->result =
+ mix_vm_cmd_dispatcher_dispatch (dis,
+ mix_vm_command_from_string (command),
+ arg);
+ return dis->result;
+}
+
+
+/* get the last dispatch's result */
+gboolean
+mix_vm_cmd_dispatcher_get_last_result (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, FALSE);
+ return dis->result;
+}
+
+/* get total uptime */
+mix_time_t
+mix_vm_cmd_dispatcher_get_uptime (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, 0);
+ return dis->uptime;
+}
+
+/* get program total time */
+mix_time_t
+mix_vm_cmd_dispatcher_get_progtime (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, 0);
+ return dis->progtime;
+}
+
+/* get time lapse */
+mix_time_t
+mix_vm_cmd_dispatcher_get_laptime (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, 0);
+ return dis->laptime;
+}
+
+/* toggle time printing */
+void
+mix_vm_cmd_dispatcher_print_time (mix_vm_cmd_dispatcher_t * dis, gboolean print)
+{
+ g_return_if_fail (dis != NULL);
+ dis->printtime = print;
+}
+
+/* get the mix vm */
+const mix_vm_t *
+mix_vm_cmd_dispatcher_get_vm (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, NULL);
+ return dis->vm;
+}
+
+/* src file info */
+gulong
+mix_vm_cmd_dispatcher_get_src_file_lineno (const mix_vm_cmd_dispatcher_t *dis)
+{
+ g_return_val_if_fail (dis != NULL, 0);
+ return mix_vm_get_break_lineno (dis->vm);
+}
+
+const gchar *
+mix_vm_cmd_dispatcher_get_src_file_line (const mix_vm_cmd_dispatcher_t *dis,
+ gulong line, gboolean cr)
+{
+ const mix_src_file_t *file;
+ g_return_val_if_fail (dis != NULL, NULL);
+
+ file = mix_vm_get_src_file (dis->vm);
+
+ if (line == 0 || file == NULL) return cr? "" : "\n";
+
+ if (cr)
+ return mix_src_file_get_line (file, line);
+ else
+ {
+ enum {BUFF_SIZE = 256};
+ static gchar BUFFER[BUFF_SIZE];
+ int len =
+ g_snprintf (BUFFER, BUFF_SIZE, "%s", mix_src_file_get_line (file, line));
+ if (len > 0 && BUFFER[len - 1] == '\n') BUFFER[len - 1] = '\0';
+ return BUFFER;
+ }
+}
+
+