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

4.104.114.95
Last change on this file since ee0c82f was ee0c82f, checked in by Ralf Corsepius <ralf.corsepius@…>, on 08/02/08 at 05:16:26

Add missing prototypes.

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