diff options
Diffstat (limited to 'cpukit/libmisc/monitor/mon-editor.c')
-rw-r--r-- | cpukit/libmisc/monitor/mon-editor.c | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/cpukit/libmisc/monitor/mon-editor.c b/cpukit/libmisc/monitor/mon-editor.c new file mode 100644 index 0000000000..1104b50185 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-editor.c @@ -0,0 +1,643 @@ +/** + * @file + * + * @brief Command line editor for RTEMS monitor. + */ + +/* + * $Id$ + * + * 2001-01-30 KJO (vac4050@cae597.rsc.raytheon.com): + * Fixed rtems_monitor_command_lookup() to accept partial + * commands to uniqeness. Added support for setting + * the monitor prompt via an environment variable: + * RTEMS_MONITOR_PROMPT + * + * CCJ: 26-3-2000, adding command history and command line + * editing. This code is donated from My Right Boot and not + * covered by GPL, only the RTEMS license. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <rtems/monitor.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <termios.h> +#include <unistd.h> + +#ifndef MONITOR_PROMPT +#define MONITOR_PROMPT "rtems" /* will have '> ' appended */ +#endif + +/* + * Some key labels to define special keys. + */ + +#define KEYS_EXTENDED (0x8000) +#define KEYS_NORMAL_MASK (0x00ff) +#define KEYS_INS (0) +#define KEYS_DEL (1) +#define KEYS_UARROW (2) +#define KEYS_DARROW (3) +#define KEYS_LARROW (4) +#define KEYS_RARROW (5) +#define KEYS_HOME (6) +#define KEYS_END (7) +#define KEYS_F1 (8) +#define KEYS_F2 (9) +#define KEYS_F3 (10) +#define KEYS_F4 (11) +#define KEYS_F5 (12) +#define KEYS_F6 (13) +#define KEYS_F7 (14) +#define KEYS_F8 (15) +#define KEYS_F9 (16) +#define KEYS_F10 (17) + +#define RTEMS_COMMAND_BUFFER_SIZE (75) + +static char monitor_prompt[32]; +static char buffer[RTEMS_COMMAND_BUFFER_SIZE]; +static int pos; +static int logged_in; + +/* + * History data. + */ + +#define RTEMS_COMMAND_HISTORIES (20) + +static char history_buffer[RTEMS_COMMAND_HISTORIES][RTEMS_COMMAND_BUFFER_SIZE]; +static int history_pos[RTEMS_COMMAND_HISTORIES]; +static int history; +static int history_next; + +/* + * Translation tables. Not sure if this is the best way to + * handle this, how-ever I wish to avoid the overhead of + * including a more complete and standard environment such + * as ncurses. + */ + +struct translation_table +{ + char expecting; + const struct translation_table *branch; + unsigned int key; +}; + +static const struct translation_table trans_two[] = +{ + { '~', 0, KEYS_INS }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_three[] = +{ + { '~', 0, KEYS_DEL }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab_csi[] = +{ + { '2', trans_two, 0 }, + { '3', trans_three, 0 }, + { 'A', 0, KEYS_UARROW }, + { 'B', 0, KEYS_DARROW }, + { 'D', 0, KEYS_LARROW }, + { 'C', 0, KEYS_RARROW }, + { 'F', 0, KEYS_END }, + { 'H', 0, KEYS_HOME }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab_O[] = +{ + { '1', 0, KEYS_F1 }, + { '2', 0, KEYS_F2 }, + { '3', 0, KEYS_F3 }, + { '4', 0, KEYS_F4 }, + { '5', 0, KEYS_F5 }, + { '6', 0, KEYS_F6 }, + { '7', 0, KEYS_F7 }, + { '8', 0, KEYS_F8 }, + { '9', 0, KEYS_F9 }, + { ':', 0, KEYS_F10 }, + { 'P', 0, KEYS_F1 }, + { 'Q', 0, KEYS_F2 }, + { 'R', 0, KEYS_F3 }, + { 'S', 0, KEYS_F4 }, + { 'T', 0, KEYS_F5 }, + { 'U', 0, KEYS_F6 }, + { 'V', 0, KEYS_F7 }, + { 'W', 0, KEYS_F8 }, + { 'X', 0, KEYS_F9 }, + { 'Y', 0, KEYS_F10 }, + { 0, 0, 0 } +}; + +static const struct translation_table trans_tab[] = +{ + { '[', trans_tab_csi, 0 }, /* CSI command sequences */ + { 'O', trans_tab_O, 0 }, /* O are the fuction keys */ + { 0, 0, 0 } +}; + +/* + * Perform a basic translation for some ANSI/VT100 key codes. + * This code could do with a timeout on the ESC as it is + * now lost from the input stream. It is not* used by the + * line editor below so considiered not worth the effort. + */ + +static unsigned int +rtems_monitor_getchar (void) +{ + const struct translation_table *translation = 0; + for (;;) + { + char c = getchar (); + if (c == 27) + translation = trans_tab; + else + { + /* + * If no translation happing just pass through + * and return the key. + */ + if (translation) + { + /* + * Scan the current table for the key, and if found + * see if this key is a fork. If so follow it and + * wait else return the extended key. + */ + int index = 0; + int branched = 0; + while ((translation[index].expecting != '\0') || + (translation[index].key != '\0')) + { + if (translation[index].expecting == c) + { + /* + * A branch is take if more keys are to come. + */ + if (translation[index].branch == 0) + return KEYS_EXTENDED | translation[index].key; + else + { + translation = translation[index].branch; + branched = 1; + break; + } + } + index++; + } + /* + * Who knows what these keys are, just drop them. + */ + if (!branched) + translation = 0; + } + else + return c; + } + } +} + +/* + * The line editor with history. + */ + +static int +rtems_monitor_line_editor ( + char *command +) +{ + int repeating = 0; + + memset (buffer, 0, RTEMS_COMMAND_BUFFER_SIZE); + history = history_next; + pos = 0; + + if (!logged_in) + fprintf(stdout,"\nMonitor ready, press enter to login.\n\n"); + else + fprintf(stdout,"%s $ ", monitor_prompt); + + while (1) + { + unsigned int extended_key; + char c; + + fflush (stdout); + + extended_key = rtems_monitor_getchar (); + c = extended_key & KEYS_NORMAL_MASK; + + /* + * Make the extended_key usable as a boolean. + */ + extended_key &= ~KEYS_NORMAL_MASK; + + if (!extended_key && !logged_in) + { + if (c == '\n') + { + logged_in = 1; + /* + * The prompt has changed from `>' to `$' to help know + * which version of the monitor code people are using. + */ + fprintf(stdout,"%s $ ", monitor_prompt); + } + } + else + { + if (extended_key) + { + switch (c) + { + case KEYS_END: + fprintf(stdout,buffer + pos); + pos = (int) strlen (buffer); + break; + + case KEYS_HOME: + fprintf(stdout,"\r%s $ ", monitor_prompt); + pos = 0; + break; + + case KEYS_LARROW: + if (pos > 0) + { + pos--; + putchar ('\b'); + } + break; + + case KEYS_RARROW: + if ((pos < RTEMS_COMMAND_BUFFER_SIZE) && (buffer[pos] != '\0')) + { + putchar (buffer[pos]); + pos++; + } + break; + + case KEYS_UARROW: + /* + * If we are moving up the histories then we need to save the working + * buffer. + */ + if (history) + { + int end; + int bs; + if (history == history_next) + { + memcpy (history_buffer[history_next], buffer, + RTEMS_COMMAND_BUFFER_SIZE); + history_pos[history_next] = pos; + } + history--; + memcpy (buffer, history_buffer[history], + RTEMS_COMMAND_BUFFER_SIZE); + pos = history_pos[history]; + fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' '); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + + case KEYS_DARROW: + if (history < history_next) + { + int end; + int bs; + history++; + memcpy (buffer, history_buffer[history], + RTEMS_COMMAND_BUFFER_SIZE); + pos = history_pos[history]; + fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' '); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + + case KEYS_DEL: + if (buffer[pos] != '\0') + { + int end; + int bs; + strcpy (&buffer[pos], &buffer[pos + 1]); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + } + } + else + { + switch (c) + { + case '\b': + case '\x7e': + case '\x7f': + if (pos > 0) + { + int bs; + pos--; + strcpy (buffer + pos, buffer + pos + 1); + fprintf(stdout,"\b%s \b", buffer + pos); + for (bs = 0; bs < ((int) strlen (buffer) - pos); bs++) + putchar ('\b'); + } + break; + + case '\n': + /* + * Process the command. + */ + fprintf(stdout,"\n"); + repeating = 1; + /* + * Only process the history if we have a command and + *a history. + */ + if (strlen (buffer)) + { + if (history_next && (history == history_next)) + { + /* + * Do not place the last command into the history + *if the same. + */ + if (strcmp (history_buffer[history_next - 1], buffer)) + repeating = 0; + } + else + repeating = 0; + } + if (!repeating) + { + memcpy (history_buffer[history_next], buffer, + RTEMS_COMMAND_BUFFER_SIZE); + history_pos[history_next] = pos; + if (history_next < (RTEMS_COMMAND_HISTORIES - 1)) + history_next++; + else + { + memmove (history_buffer[0], history_buffer[1], + RTEMS_COMMAND_BUFFER_SIZE * (RTEMS_COMMAND_HISTORIES - 1)); + memmove (&history_pos[0], &history_pos[1], + sizeof (history_pos[0]) * (RTEMS_COMMAND_HISTORIES - 1)); + } + } + else + { +#ifdef ENABLE_ENTER_REPEATS + if (history_next) + memcpy (buffer, history_buffer[history_next - 1], + RTEMS_COMMAND_BUFFER_SIZE); +#endif + } + memmove (command, buffer, RTEMS_COMMAND_BUFFER_SIZE); + return repeating; + break; + + default: + if ((pos < (RTEMS_COMMAND_BUFFER_SIZE - 1)) && + (c >= ' ') && (c <= 'z')) + { + int end; + end = strlen (buffer); + if ((pos < end) && (end < RTEMS_COMMAND_BUFFER_SIZE)) + { + int ch, bs; + for (ch = end; ch > pos; ch--) + buffer[ch] = buffer[ch - 1]; + fprintf(stdout,buffer + pos); + for (bs = 0; bs < (end - pos + 1); bs++) + putchar ('\b'); + } + buffer[pos++] = c; + if (pos > end) + buffer[pos] = '\0'; + putchar (c); + } + break; + } + } + } + } +} + +/* + * make_argv(cp): token-count + * Break up the command line in 'cp' into global argv[] and argc (return + * value). + */ + +int +rtems_monitor_make_argv( + char *cp, + int *argc_p, + char **argv) +{ + int argc = 0; + + while ((cp = strtok(cp, " \t\n\r"))) + { + argv[argc++] = cp; + cp = (char *) NULL; + } + argv[argc] = (char *) NULL; /* end of argv */ + + return *argc_p = argc; +} + + +/* + * Read and break up a monitor command + * + * We have to loop on the gets call, since it will return NULL under UNIX + * RTEMS when we get a signal (eg: SIGALRM). + */ + +int +rtems_monitor_command_read(char *command, + int *argc, + char **argv) +{ + char *env_prompt; + + env_prompt = getenv("RTEMS_MONITOR_PROMPT"); + + /* + * put node number in the prompt if we are multiprocessing + */ +#if defined(RTEMS_MULTIPROCESSING) + if (!rtems_configuration_get_user_multiprocessing_table ()) + sprintf (monitor_prompt, "%s", + (env_prompt == NULL) ? MONITOR_PROMPT: env_prompt); + else /* .... */ +#endif + if (rtems_monitor_default_node != rtems_monitor_node) + sprintf (monitor_prompt, "%" PRId32 "-%s-%" PRId32 "", rtems_monitor_node, + (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt, + rtems_monitor_default_node); + else + sprintf (monitor_prompt, "%" PRId32 "-%s", rtems_monitor_node, + (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt); + + rtems_monitor_line_editor (command); + + return rtems_monitor_make_argv (command, argc, argv); +} + +/* + * Main monitor command loop + */ + +void +rtems_monitor_task( + rtems_task_argument monitor_flags +) +{ + rtems_tcb *debugee = 0; + rtems_context *rp; +#if (CPU_HARDWARE_FP == TRUE) || (CPU_SOFTWARE_FP == TRUE) + rtems_context_fp *fp; +#endif + char command_buffer[513]; + int argc; + char *argv[64]; + bool verbose = false; + struct termios term; + + /* + * Make the stdin stream characte not line based. + */ + + if (tcgetattr (STDIN_FILENO, &term) < 0) + { + fprintf(stdout,"rtems-monitor: cannot get terminal attributes.\n"); + } + else + { + /* + * No echo, no canonical processing. + */ + + term.c_lflag &= ~(ECHO | ICANON | IEXTEN); + + /* + * No sigint on BREAK, CR-to-NL off, input parity off, + * don't strip 8th bit on input, output flow control off + */ + + term.c_lflag &= ~(INPCK | ISTRIP | IXON); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; + + if (tcsetattr (STDIN_FILENO, TCSANOW, &term) < 0) + { + fprintf(stdout,"cannot set terminal attributes\n"); + } + } + + if (!(monitor_flags & RTEMS_MONITOR_NOSYMLOAD)) { + rtems_monitor_symbols_loadup(); + } + + if (monitor_flags & RTEMS_MONITOR_SUSPEND) + (void) rtems_monitor_suspend(RTEMS_NO_TIMEOUT); + + for (;;) + { + const rtems_monitor_command_entry_t *command; + + debugee = _Thread_Executing; + rp = &debugee->Registers; +#if (CPU_HARDWARE_FP == TRUE) || (CPU_SOFTWARE_FP == TRUE) + fp = debugee->fp_context; /* possibly 0 */ +#endif + + if (0 == rtems_monitor_command_read(command_buffer, &argc, argv)) + continue; + if (argc < 1 + || (command = rtems_monitor_command_lookup(argv [0])) == 0) { + /* no command */ + fprintf(stdout,"Unrecognised command; try 'help'\n"); + continue; + } + + command->command_function(argc, argv, &command->command_arg, verbose); + + fflush(stdout); + } +} + + +void +rtems_monitor_kill(void) +{ + if (rtems_monitor_task_id) + rtems_task_delete(rtems_monitor_task_id); + rtems_monitor_task_id = 0; + + rtems_monitor_server_kill(); +} + +void +rtems_monitor_init( + uint32_t monitor_flags +) +{ + rtems_status_code status; + + rtems_monitor_kill(); + + status = rtems_task_create(RTEMS_MONITOR_NAME, + 1, + RTEMS_MINIMUM_STACK_SIZE * 2, + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_DEFAULT_ATTRIBUTES, + &rtems_monitor_task_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor task"); + return; + } + + rtems_monitor_node = rtems_object_id_get_node(rtems_monitor_task_id); + rtems_monitor_default_node = rtems_monitor_node; + + rtems_monitor_server_init(monitor_flags); + + if (!(monitor_flags & RTEMS_MONITOR_NOTASK)) { + /* + * Start the monitor task itself + */ + status = rtems_task_start( + rtems_monitor_task_id, rtems_monitor_task, monitor_flags); + if (status != RTEMS_SUCCESSFUL) { + rtems_error(status, "could not start monitor"); + return; + } + } +} |