summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/monitor/mon-editor.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libmisc/monitor/mon-editor.c')
-rw-r--r--cpukit/libmisc/monitor/mon-editor.c643
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;
+ }
+ }
+}