/* -*-c-*- -------------- mixvm_command.c : * Implementation of the functions declared in mixvm_command.h * ------------------------------------------------------------------ * Copyright (C) 2000, 2001 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #ifdef HAVE_LIBREADLINE # include # include #else typedef int Function (); #endif /* HAVE_LIBREADLINE */ #include #include #include #include #include #include "mixvm_command.h" /* The names of functions that actually do the manipulation. */ #define DEC_FUN(name) \ static int cmd_##name (char *arg) DEC_FUN (help_); DEC_FUN (shell_); DEC_FUN (compile_); DEC_FUN (edit_); DEC_FUN (quit_); /* A structure which contains information on the commands this program can understand. */ typedef struct { const char *name; /* User printable name of the function. */ Function *func; /* Function to call to do the job. */ const char *doc; /* Documentation for this function. */ const char *usage; /* Usage */ } COMMAND; COMMAND commands[] = { { "help", cmd_help_, N_("Display this text"), "help [COMMAND]" }, { "shell", cmd_shell_, N_("Execute shell command"), "shell COMMAND" }, { "compile", cmd_compile_, N_("Compile a source file"), "compile FILENAME"}, { "edit", cmd_edit_, N_("Edit a source file"), "edit FILENAME"}, { "quit", cmd_quit_, N_("Quit the program"), "quit" }, { (char *)NULL, (Function *)NULL, (char *)NULL } }; #define LOCAL_COMANDS_NO_ ((sizeof (commands) / sizeof (commands[0])) - 1) #define MIX_COMMANDS_NO_ MIX_CMD_INVALID+1 #define ALL_COMMANDS_NO_ LOCAL_COMANDS_NO_+MIX_COMMANDS_NO_ static const char *mix_commands_[ALL_COMMANDS_NO_] = {NULL}; #ifdef HAVE_LIBREADLINE /* readline functions */ static char * mixvm_cmd_generator_ (char *text, int state); /* Attempt to complete on the contents of TEXT. START and END bound the region of rl_line_buffer that contains the word to complete. TEXT is the word to complete. We can use the entire contents of rl_line_buffer in case we want to do some simple parsing. Return the array of matches, or NULL if there aren't any. */ static char ** mixvm_cmd_completion_ (char *text, int start, int end) { char **matches; matches = (char **)NULL; /* If this word is at the start of the line, then it is a command to complete. Otherwise it is the name of a file in the current directory. */ if (start == 0) matches = completion_matches (text, mixvm_cmd_generator_); return (matches); } /* Generator function for command completion. STATE lets us know whether to start from scratch; without any state (i.e. STATE == 0), then we start at the top of the list. */ static char * mixvm_cmd_generator_ (char *text, int state) { static int list_index, len; const char *name; /* If this is a new word to complete, initialize now. This includes saving the length of TEXT for efficiency, and initializing the index variable to 0. */ if (!state) { list_index = 0; len = strlen (text); } /* Return the next name which partially matches from the command list. */ while ((name = mix_commands_[list_index]) != NULL) { list_index++; if (strncmp (name, text, len) == 0) return (g_strdup (name)); } /* If no names matched, then return NULL. */ return ((char *)NULL); } #endif /* HAVE_LIBREADLINE */ /* command functions */ static COMMAND * find_command_ (const char *name) { int i; for (i = 0; commands[i].name; i++) if (strcmp (name, commands[i].name) == 0) return (&commands[i]); return ((COMMAND *)NULL); } /* mixvm dispatcher */ static mix_vm_cmd_dispatcher_t *dis_ = NULL; /* emacs interface */ static void emacs_output_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg, gpointer data) { /* pek: probably bad that we snag the src w/every emacs_output_, however when multiple files are supported then this will have to be done each time (but the info will be snagged from elsewhere...) */ const mix_vm_t *vm = mix_vm_cmd_dispatcher_get_vm (dis); const mix_src_file_t *src = mix_vm_get_src_file (vm); const gchar *path = mix_src_file_get_path (src); mix_address_t loc = mix_vm_get_prog_count (vm); guint lineno = mix_vm_get_address_lineno (vm, loc); printf ("\032\032mixvm:%s%s:%d\n", path, MIX_SRC_DEFEXT, lineno); return; } static int cmd_help_ (char *arg) { int i; int printed = 0; for (i = 0; commands[i].name; i++) { if (!*arg || (strcmp (arg, commands[i].name) == 0)) { printf (_("%s\t\t%s. Usage: %s\n"), commands[i].name, _(commands[i].doc), commands[i].usage); printed++; } } if (printed > 1) printf ("\n"); for (i = LOCAL_COMANDS_NO_ + 1 /* skip help cmd */; mix_commands_[i]; i++) { if (!*arg || (strcmp (arg, mix_commands_[i]) == 0)) { printf (_("%s\t\t%s. Usage: %s\n"), mix_commands_[i], mix_vm_command_help (i - LOCAL_COMANDS_NO_), mix_vm_command_usage (i - LOCAL_COMANDS_NO_)); printed++; } } if (!printed) printf ("Command \'%s\' not found\n", arg); return TRUE; } static int cmd_quit_ (char *arg) { puts ("Quitting ..."); if ( dis_ ) mix_vm_cmd_dispatcher_delete (dis_); /* pek: anything needed here to make the marker disappear??? */ return FALSE; } static int cmd_shell_ (char *arg) { system (arg); return TRUE; } static int cmd_compile_ (char *arg) { gchar *line; if ( strlen (arg) == 0 ) return cmd_help_ ("compile"); line = g_strconcat ("mixasm -g ", arg, NULL); if ( system (line) == EXIT_SUCCESS ) fputs (_("Successful compilation\n"), stderr); g_free (line); return TRUE; } static int cmd_edit_ (char *arg) { static const gchar * envars[] = { "MDK_EDITOR", "X_EDITOR", "EDITOR", "VISUAL" }; static const guint s = sizeof (envars) / sizeof (envars[0]); static const gchar *editor = NULL; if ( strlen (arg) == 0 ) return cmd_help_ ("edit"); if (!editor) { int k; for (k = 0; k < s; k++) if ( (editor = getenv (envars[k])) != NULL ) break; } if (!editor) { int k; fputs (_("Cannot find editor ("), stderr); for (k = 0; k < s; k++) fprintf (stderr, "%s ", envars[k]); fputs (_("undefined)\n"), stderr); } else { gchar *line = g_strconcat (editor, " ", arg, NULL); system (line); g_free (line); } return TRUE; } /* external interface */ void mixvm_cmd_init (char *arg, gboolean use_emacs) { int k; /* get local command names */ for (k = 0; k < LOCAL_COMANDS_NO_; ++k) mix_commands_[k] = commands[k].name; /* get external command names */ for (k = 0; k < MIX_CMD_INVALID; ++k) mix_commands_[k + LOCAL_COMANDS_NO_] = mix_vm_command_to_string (k); mix_commands_[ALL_COMMANDS_NO_ - 1] = NULL; #ifdef HAVE_LIBREADLINE /* Tell the completer that we want a crack first. */ rl_attempted_completion_function = (CPPFunction *)mixvm_cmd_completion_; #endif /* HAVE_LIBREADLINE */ /* initialise the dispatcher */ dis_ = mix_vm_cmd_dispatcher_new (stdout, stderr); if ( dis_ == NULL) g_error (_("Failed initialisation (no memory resources)")); /* install post hook for emacs interaction */ if (use_emacs) { mix_vm_cmd_dispatcher_post_hook (dis_, MIX_CMD_LOAD, emacs_output_, NULL); mix_vm_cmd_dispatcher_post_hook (dis_, MIX_CMD_RUN, emacs_output_, NULL); mix_vm_cmd_dispatcher_post_hook (dis_, MIX_CMD_NEXT, emacs_output_, NULL); } if (arg) mix_vm_cmd_dispatcher_dispatch (dis_, MIX_CMD_LOAD, arg); } gboolean mixvm_cmd_exec (char *line) { int i; COMMAND *command; char *cmd, *arg; mix_vm_command_t mix_cmd; if (!line) return cmd_quit_(NULL); /* strip white space */ line = g_strstrip(line); /* Isolate the command word. */ i = 0; while (line[i] && isspace (line[i])) i++; cmd = line + i; while (line[i] && !isspace (line[i])) i++; if (line[i]) line[i++] = '\0'; if (cmd == NULL || strlen (cmd) == 0) return TRUE; /* Get argument to command, if any. */ while (isspace (line[i])) i++; arg = line + i; /* try to find local command */ command = find_command_ (cmd); if (command) return ((*(command->func)) (arg)); /* try to find mix command */ mix_cmd = mix_vm_command_from_string (cmd); if (mix_cmd == MIX_CMD_INVALID) fprintf (stderr, _("%s: No such command. Try \'help\'\n"), cmd); else mix_vm_cmd_dispatcher_dispatch (dis_, mix_cmd, arg); return TRUE; }