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

4.104.114.84.95
Last change on this file since 62e080e was 62e080e, checked in by Joel Sherrill <joel.sherrill@…>, on 01/27/03 at 16:13:28

2003-01-27 Dan Smisko <dan@…>

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