source: rtems/cpukit/libmisc/monitor/mon-command.c @ ecb5f95

4.104.114.84.95
Last change on this file since ecb5f95 was ecb5f95, checked in by Joel Sherrill <joel.sherrill@…>, on 08/09/01 at 21:39:34

2001-08-09 Keith Outwater <vac4050@…>

  • monitor/mon-command.c: Add support for partial command matching. The monitor used to have this functionality before it was overhauled to support addition of user commands.
  • Property mode set to 100644
File size: 17.0 KB
Line 
1/*
2 * Command parsing routines for RTEMS monitor
3 *
4 * TODO:
5 *
6 *  $Id$
7 */
8
9#include <rtems.h>
10
11#include <rtems/monitor.h>
12
13#include <stdio.h>
14#include <string.h>
15#include <stdlib.h>
16
17/*
18 * 2001-01-30 KJO (vac4050@cae597.rsc.raytheon.com):
19 *  Fixed rtems_monitor_command_lookup() to accept partial
20 *  commands to uniqeness.  Added support for setting
21 *  the monitor prompt via an environment variable:
22 *  RTEMS_MONITOR_PROMPT
23 *
24 * CCJ: 26-3-2000, adding command history and command line
25 * editing. This code is donated from My Right Boot and not
26 * covered by GPL, only the RTEMS license.
27 */
28
29/*
30 * Some key labels to define special keys.
31 */
32
33#define KEYS_EXTENDED    (0x8000)
34#define KEYS_NORMAL_MASK (0x00ff)
35#define KEYS_INS         (0)
36#define KEYS_DEL         (1)
37#define KEYS_UARROW      (2)
38#define KEYS_DARROW      (3)
39#define KEYS_LARROW      (4)
40#define KEYS_RARROW      (5)
41#define KEYS_HOME        (6)
42#define KEYS_END         (7)
43#define KEYS_F1          (8)
44#define KEYS_F2          (9)
45#define KEYS_F3          (10)
46#define KEYS_F4          (11)
47#define KEYS_F5          (12)
48#define KEYS_F6          (13)
49#define KEYS_F7          (14)
50#define KEYS_F8          (15)
51#define KEYS_F9          (16)
52#define KEYS_F10         (17)
53
54#define RTEMS_COMMAND_BUFFER_SIZE (75)
55
56static char monitor_prompt[32];
57static char buffer[RTEMS_COMMAND_BUFFER_SIZE];
58static int  pos;
59static int  logged_in;
60
61/*
62 * History data.
63 */
64
65#define RTEMS_COMMAND_HISTORIES (20)
66
67static char history_buffer[RTEMS_COMMAND_HISTORIES][RTEMS_COMMAND_BUFFER_SIZE];
68static int  history_pos[RTEMS_COMMAND_HISTORIES];
69static int  history;
70static int  history_next;
71
72/*
73 * Translation tables. Not sure if this is the best way to
74 * handle this, how-ever I wish to avoid the overhead of
75 * including a more complete and standard environment such
76 * as ncurses.
77 */
78
79struct translation_table
80{
81  char                     expecting;
82  struct translation_table *branch;
83  unsigned int             key;
84};
85
86static struct translation_table trans_two[] =
87{
88  { '~', 0, KEYS_INS },
89  { 0,   0, 0 }
90};
91
92static struct translation_table trans_three[] =
93{
94  { '~', 0, KEYS_DEL },
95  { 0,   0, 0 }
96};
97
98static struct translation_table trans_tab_csi[] =
99{
100  { '2', trans_two,   0 },
101  { '3', trans_three, 0 },
102  { 'A', 0,           KEYS_UARROW },
103  { 'B', 0,           KEYS_DARROW },
104  { 'D', 0,           KEYS_LARROW },
105  { 'C', 0,           KEYS_RARROW },
106  { 'F', 0,           KEYS_END },
107  { 'H', 0,           KEYS_HOME },
108  { 0,   0,           0 }
109};
110
111static struct translation_table trans_tab_O[] =
112{
113  { '1', 0, KEYS_F1 },
114  { '2', 0, KEYS_F2 },
115  { '3', 0, KEYS_F3 },
116  { '4', 0, KEYS_F4 },
117  { '5', 0, KEYS_F5 },
118  { '6', 0, KEYS_F6 },
119  { '7', 0, KEYS_F7 },
120  { '8', 0, KEYS_F8 },
121  { '9', 0, KEYS_F9 },
122  { ':', 0, KEYS_F10 },
123  { 'P', 0, KEYS_F1 },
124  { 'Q', 0, KEYS_F2 },
125  { 'R', 0, KEYS_F3 },
126  { 'S', 0, KEYS_F4 },
127  { 'T', 0, KEYS_F5 },
128  { 'U', 0, KEYS_F6 },
129  { 'V', 0, KEYS_F7 },
130  { 'W', 0, KEYS_F8 },
131  { 'X', 0, KEYS_F9 },
132  { 'Y', 0, KEYS_F10 },
133  { 0,   0, 0 }
134};
135
136static struct translation_table trans_tab[] =
137{
138  { '[', trans_tab_csi, 0 },    /* CSI command sequences */
139  { 'O', trans_tab_O,   0 },    /* O are the fuction keys */
140  { 0,   0,             0 }
141};
142
143/*
144 * Perform a basic tranlation for some ANSI/VT100 key codes.
145 * This code could do with a timeout on the ESC as it is
146 * now lost from the input stream. It is not* used by the
147 * line editor below so considiered not worth the effort.
148 */
149
150static unsigned int
151rtems_monitor_getchar (
152)
153{
154  struct translation_table *translation = 0;
155  for (;;)
156  {
157    char c = getchar ();
158    if (c == 27)
159      translation = trans_tab;
160    else
161    {
162      /*
163       * If no translation happing just pass through
164       * and return the key.
165       */
166      if (translation)
167      {
168        /*
169         * Scan the current table for the key, and if found
170         * see if this key is a fork. If so follow it and
171         * wait else return the extended key.
172         */
173        int index    = 0;
174        int branched = 0;
175        while ((translation[index].expecting != '\0') ||
176               (translation[index].key != '\0'))
177        {
178          if (translation[index].expecting == c)
179          {
180            /*
181             * A branch is take if more keys are to come.
182             */
183            if (translation[index].branch == 0)
184              return KEYS_EXTENDED | translation[index].key;
185            else
186            {
187              translation = translation[index].branch;
188              branched    = 1;
189              break;
190            }
191          }
192          index++;
193        }
194        /*
195         * Who knows what these keys are, just drop them.
196         */
197        if (!branched)
198          translation = 0;
199      }
200      else
201        return c;
202    }
203  }
204}
205
206/*
207 * The line editor with history.
208 */
209
210static int
211rtems_monitor_line_editor (
212    char *command
213)
214{
215  int repeating = 0;
216
217  memset (buffer, 0, RTEMS_COMMAND_BUFFER_SIZE);
218  history = history_next;
219  pos     = 0;
220         
221  if (!logged_in)
222    printf ("\nMonitor ready, press enter to login.\n\n");
223  else
224    printf ("%s $ ", monitor_prompt);
225
226  while (1)
227  {
228    unsigned int extended_key = rtems_monitor_getchar ();
229    char         c = extended_key & KEYS_NORMAL_MASK;
230
231    /*
232     * Make the extended_key usable as a boolean.
233     */
234    extended_key &= ~KEYS_NORMAL_MASK;
235   
236    if (!extended_key && !logged_in)
237    {
238      if (c == '\n')
239      {
240        logged_in = 1;
241        /*
242         * The prompt has changed from `>' to `$' to help know
243         * which version of the monitor code people are using.
244         */
245        printf("%s $ ", monitor_prompt);
246      }
247    }
248    else
249    {
250      if (extended_key)
251      {
252        switch (c)
253        {
254          case KEYS_END:
255            printf (buffer + pos);
256            pos = (int) strlen (buffer);
257            break;
258
259          case KEYS_HOME:
260            printf ("\r%s $ ", monitor_prompt);
261            pos = 0;
262            break;
263       
264          case KEYS_LARROW:
265            if (pos > 0)
266            {
267              pos--;
268              putchar ('\b');
269            }
270            break;
271
272          case KEYS_RARROW:
273            if ((pos < RTEMS_COMMAND_BUFFER_SIZE) && (buffer[pos] != '\0'))
274            {
275              putchar (buffer[pos]);
276              pos++;
277            }
278            break;
279
280          case KEYS_UARROW:
281            /*
282             * If we are moving up the histories then we need to save the working
283             * buffer.
284             */
285            if (history)
286            {
287              int end;
288              int bs;
289              if (history == history_next)
290              {
291                memcpy (history_buffer[history_next], buffer,
292                        RTEMS_COMMAND_BUFFER_SIZE);
293                history_pos[history_next] = pos;
294              }
295              history--;
296              memcpy (buffer, history_buffer[history],
297                      RTEMS_COMMAND_BUFFER_SIZE);
298              pos = history_pos[history];
299              printf ("\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' ');
300              printf ("\r%s $ %s", monitor_prompt, buffer);
301              end = (int) strlen (buffer);
302              for (bs = 0; bs < (end - pos); bs++)
303                putchar ('\b');
304            }
305            break;
306                 
307          case KEYS_DARROW:
308            if (history < history_next)
309            {
310              int end;
311              int bs;
312              history++;
313              memcpy (buffer, history_buffer[history],
314                      RTEMS_COMMAND_BUFFER_SIZE);
315              pos = history_pos[history];
316              printf ("\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' ');
317              printf ("\r%s $ %s", monitor_prompt, buffer);
318              end = (int) strlen (buffer);
319              for (bs = 0; bs < (end - pos); bs++)
320                putchar ('\b');
321            }
322            break;
323
324          case KEYS_DEL:
325            if (buffer[pos] != '\0')
326            {
327              int end;
328              int bs;
329              strcpy (&buffer[pos], &buffer[pos + 1]);
330              printf ("\r%s $ %s", monitor_prompt, buffer);
331              end = (int) strlen (buffer);
332              for (bs = 0; bs < (end - pos); bs++)
333                putchar ('\b');
334            }
335            break;
336        }
337      }
338      else
339      {
340        switch (c)
341        {
342          case '\b':
343          case '\x7e':
344          case '\x7f':
345            if (pos > 0)
346            {
347              int bs;
348              pos--;
349              strcpy (buffer + pos, buffer + pos + 1);
350              printf ("\b%s \b", buffer + pos);
351              for (bs = 0; bs < ((int) strlen (buffer) - pos); bs++)
352                putchar ('\b');
353            }
354            break;
355
356          case '\n':
357            /*
358             * Process the command.
359             */
360            printf ("\n");
361            repeating = 1;
362            /*
363             * Only process the history if we have a command and
364             *a history.
365             */
366            if (strlen (buffer))
367            {
368              if (history_next && (history == history_next))
369              {
370                /*
371                 * Do not place the last command into the history
372                 *if the same.
373                 */
374                if (strcmp (history_buffer[history_next - 1], buffer))
375                  repeating = 0;
376              }
377              else
378                repeating = 0;
379            }
380            if (!repeating)
381            {
382              memcpy (history_buffer[history_next], buffer,
383                      RTEMS_COMMAND_BUFFER_SIZE);
384              history_pos[history_next] = pos;
385              if (history_next < (RTEMS_COMMAND_HISTORIES - 1))
386                history_next++;
387              else
388              {
389                memmove (history_buffer[0], history_buffer[1],
390                         RTEMS_COMMAND_BUFFER_SIZE * (RTEMS_COMMAND_HISTORIES - 1));
391                memmove (&history_pos[0], &history_pos[1],
392                         sizeof (history_pos[0]) * (RTEMS_COMMAND_HISTORIES - 1));
393              }
394            }
395            else
396            {
397              if (history_next)
398                memcpy (buffer, history_buffer[history_next - 1],
399                        RTEMS_COMMAND_BUFFER_SIZE);
400            }
401            memmove (command, buffer, RTEMS_COMMAND_BUFFER_SIZE);
402            return repeating;
403            break;
404
405          default:
406            if ((pos < (RTEMS_COMMAND_BUFFER_SIZE - 1)) &&
407                (c >= ' ') && (c <= 'z'))
408            {
409              int end;
410              if (c >= 'A' && c <= 'Z')
411                c += 'a' - 'A';
412              end = strlen (buffer);
413              if ((pos < end) && (end < RTEMS_COMMAND_BUFFER_SIZE))
414              {
415                int ch, bs;
416                for (ch = end + 1; ch > pos; ch--)
417                  buffer[ch] = buffer[ch - 1];
418                printf (buffer + pos);
419                for (bs = 0; bs < (end - pos + 1); bs++)
420                  putchar ('\b');
421              }
422              buffer[pos++] = c;
423              if (pos > end)
424                buffer[pos] = '\0';
425              putchar (c);
426            }
427            break;
428        }
429      }
430    }
431  }
432}
433
434/*
435 * make_argv(cp): token-count
436 *  Break up the command line in 'cp' into global argv[] and argc (return
437 *  value).
438 */
439
440int
441rtems_monitor_make_argv(
442    char *cp,
443    int  *argc_p,
444    char **argv)
445{
446  int argc = 0;
447
448  while ((cp = strtok(cp, " \t\n\r")))
449  {
450    argv[argc++] = cp;
451    cp = (char *) NULL;
452  }
453  argv[argc] = (char *) NULL;      /* end of argv */
454
455  return *argc_p = argc;
456}
457
458
459/*
460 * Read and break up a monitor command
461 *
462 * We have to loop on the gets call, since it will return NULL under UNIX
463 *  RTEMS when we get a signal (eg: SIGALRM).
464 */
465
466int
467rtems_monitor_command_read(char *command,
468                           int  *argc,
469                           char **argv)
470{
471        char *env_prompt;
472
473        env_prompt = getenv("RTEMS_MONITOR_PROMPT");
474
475  /*
476   * put node number in the prompt if we are multiprocessing
477   */
478  if (!rtems_configuration_get_user_multiprocessing_table ())
479    sprintf (monitor_prompt, "%s", 
480             (env_prompt == NULL) ? MONITOR_PROMPT: env_prompt);
481  else if (rtems_monitor_default_node != rtems_monitor_node)
482    sprintf (monitor_prompt, "%d-%s-%d", rtems_monitor_node,
483             (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt,
484             rtems_monitor_default_node);
485  else
486    sprintf (monitor_prompt, "%d-%s", rtems_monitor_node,
487             (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt);
488
489#if defined(RTEMS_UNIX)
490  /* RTEMS on unix gets so many interrupt system calls this is hosed */
491  printf ("%s> ", monitor_prompt);
492  fflush (stdout);
493  while (gets(command) == (char *) 0)
494    ;
495#else
496  rtems_monitor_line_editor (command);
497#endif
498
499  return rtems_monitor_make_argv (command, argc, argv);
500}
501
502/*
503 * Look up a command in a command table
504 *
505 */
506
507rtems_monitor_command_entry_t *
508rtems_monitor_command_lookup(
509    rtems_monitor_command_entry_t *table,
510    int                            argc,
511    char                          **argv
512)
513{
514  int command_length;
515  rtems_monitor_command_entry_t *found_it = NULL;
516
517  command_length = strlen (argv[0]);
518
519  if ((table == 0) || (argv[0] == 0))
520    return 0;
521   
522  while (table)
523  {
524    if (table->command)
525    {
526
527      /*
528       * Check for ambiguity
529       */
530      if (!strncmp (table->command, argv[0], command_length))
531      {
532        if (found_it)
533        {
534          return 0;
535        }
536       
537        else
538          found_it = table;
539      }
540    }
541    table = table->next;
542  }
543
544  /*
545   * No ambiguity (the possible partial command was unique after all)
546   */
547  if (found_it)
548  {
549    if (table->command_function == 0)
550      return 0;
551
552    return found_it;
553  }
554
555  return 0;
556}
557
558void
559rtems_monitor_show_help (
560  rtems_monitor_command_entry_t *help_cmd,
561  int                           max_cmd_len
562)
563{
564#define MAX_HELP_LINE_LENGTH (75 - max_cmd_len - 2)
565
566  if (help_cmd && help_cmd->command)
567  {
568    const char *help = help_cmd->usage;
569    int         help_len = strlen (help);
570    int         spaces = max_cmd_len - strlen (help_cmd->command);
571    int         show_this_line = 0;
572    int         line_one = 1;
573    int         c;
574
575    printf ("%s", help_cmd->command);
576
577    if (help_len == 0)
578    {
579      printf (" - No help associated.\n");
580      return;
581    }
582 
583    while (help_len)
584    {
585      printf ("%*c", spaces, ' ');
586     
587      if (line_one)
588        printf (" - ");
589
590      spaces   = max_cmd_len + 2;
591      line_one = 0;
592
593      /*
594       * See if greater then the line length if so, work back
595       * from the end for a space, tab or lf or cr.
596       */
597     
598      if (help_len > MAX_HELP_LINE_LENGTH)
599      {
600        for (show_this_line = MAX_HELP_LINE_LENGTH - 1;
601             show_this_line;
602             show_this_line--)
603          if ((help[show_this_line] == ' ') ||
604              (help[show_this_line] == '\n') ||
605              (help[show_this_line] == '\r'))
606            break;
607
608        /*
609         * If show_this_line is 0, it is a very long word !!
610         */
611     
612        if (show_this_line == 0)
613          show_this_line = MAX_HELP_LINE_LENGTH - 1;
614      }
615      else
616        show_this_line = help_len;
617
618      for (c = 0; c < show_this_line; c++)
619        if ((help[c] == '\r') || (help[c] == '\n'))
620          show_this_line = c;
621        else
622          putchar (help[c]);
623
624      printf ("\n");
625                 
626      help     += show_this_line;
627      help_len -= show_this_line;
628
629      /*
630       * Move past the line feeds or what ever else is being skipped.
631       */
632   
633      while (help_len)
634      {
635        if ((*help != '\r') && (*help != '\n'))
636          break;
637
638        if (*help != ' ')
639        {
640          help++;
641          help_len--;
642          break;
643        }
644        help++;
645        help_len--;
646      }
647    }
648  }
649}
650
651void
652rtems_monitor_command_usage(
653  rtems_monitor_command_entry_t *table,
654  char                          *command_string
655)
656{
657  rtems_monitor_command_entry_t *command = table;
658  int                           max_cmd_len = 0;
659   
660  /* if first entry in table is a usage, then print it out */
661
662  if (command_string && (*command_string != '\0'))
663  {
664    char *argv[2];
665   
666    argv[0] = command_string;
667    argv[1] = 0;
668   
669    command = rtems_monitor_command_lookup (table, 1, argv);
670
671    if (command)
672      rtems_monitor_show_help (command, strlen (command_string));
673    else
674      printf ("Unrecognised command; try just 'help'\n");
675    return;
676  }
677 
678  /*
679   * Find the largest command size.
680   */
681 
682  while (command)
683  {
684    int len = strlen (command->command);
685
686    if (len > max_cmd_len)
687      max_cmd_len = len;
688
689    command = command->next;
690  }
691
692  max_cmd_len++;
693
694  command = table;
695
696  /*
697   * Now some nice formatting for the help.
698   */
699
700  while (command)
701  {
702    rtems_monitor_show_help (command, max_cmd_len);
703    command = command->next;
704  }
705}
706
707
708void
709rtems_monitor_help_cmd(
710    int          argc,
711    char       **argv,
712    unsigned32   command_arg,
713    boolean verbose
714)
715{
716  int arg;
717  rtems_monitor_command_entry_t *command;
718
719  command = (rtems_monitor_command_entry_t *) command_arg;
720   
721  if (argc == 1)
722    rtems_monitor_command_usage(command, 0);
723  else
724  {
725    for (arg = 1; argv[arg]; arg++)
726      rtems_monitor_command_usage(command, argv[arg]);
727  }
728}
Note: See TracBrowser for help on using the repository browser.