Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:18

0001 /**
0002  * @file
0003  *
0004  * @brief Command line editor for RTEMS monitor.
0005  */
0006 
0007 /*
0008  * COPYRIGHT (c) 2000 Chris Johns <chrisj@rtems.org>
0009  * 
0010  * Redistribution and use in source and binary forms, with or without
0011  * modification, are permitted provided that the following conditions
0012  * are met:
0013  * 1. Redistributions of source code must retain the above copyright
0014  *    notice, this list of conditions and the following disclaimer.
0015  * 2. Redistributions in binary form must reproduce the above copyright
0016  *    notice, this list of conditions and the following disclaimer in the
0017  *    documentation and/or other materials provided with the distribution.
0018  *
0019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0020  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0022  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0023  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0024  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0025  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0026  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0027  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0028  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0029  * POSSIBILITY OF SUCH DAMAGE.
0030  */
0031 
0032 #ifdef HAVE_CONFIG_H
0033 #include "config.h"
0034 #endif
0035 
0036 #include <rtems.h>
0037 
0038 #include <rtems/monitor.h>
0039 
0040 #include <stdio.h>
0041 #include <string.h>
0042 #include <stdlib.h>
0043 #include <inttypes.h>
0044 #include <termios.h>
0045 #include <unistd.h>
0046 
0047 #ifndef MONITOR_PROMPT
0048 #define MONITOR_PROMPT "rtems"          /* will have '> ' appended */
0049 #endif
0050 
0051 /*
0052  * Some key labels to define special keys.
0053  */
0054 
0055 #define KEYS_EXTENDED    (0x8000)
0056 #define KEYS_NORMAL_MASK (0x00ff)
0057 #define KEYS_INS         (0)
0058 #define KEYS_DEL         (1)
0059 #define KEYS_UARROW      (2)
0060 #define KEYS_DARROW      (3)
0061 #define KEYS_LARROW      (4)
0062 #define KEYS_RARROW      (5)
0063 #define KEYS_HOME        (6)
0064 #define KEYS_END         (7)
0065 #define KEYS_F1          (8)
0066 #define KEYS_F2          (9)
0067 #define KEYS_F3          (10)
0068 #define KEYS_F4          (11)
0069 #define KEYS_F5          (12)
0070 #define KEYS_F6          (13)
0071 #define KEYS_F7          (14)
0072 #define KEYS_F8          (15)
0073 #define KEYS_F9          (16)
0074 #define KEYS_F10         (17)
0075 
0076 #define RTEMS_COMMAND_BUFFER_SIZE (75)
0077 
0078 static char monitor_prompt[32];
0079 static char buffer[RTEMS_COMMAND_BUFFER_SIZE];
0080 static int  pos;
0081 static int  logged_in;
0082 
0083 /*
0084  * History data.
0085  */
0086 
0087 #define RTEMS_COMMAND_HISTORIES (20)
0088 
0089 static char history_buffer[RTEMS_COMMAND_HISTORIES][RTEMS_COMMAND_BUFFER_SIZE];
0090 static int  history_pos[RTEMS_COMMAND_HISTORIES];
0091 static int  history;
0092 static int  history_next;
0093 
0094 /*
0095  * Translation tables. Not sure if this is the best way to
0096  * handle this, how-ever I wish to avoid the overhead of
0097  * including a more complete and standard environment such
0098  * as ncurses.
0099  */
0100 
0101 struct translation_table
0102 {
0103   char                     expecting;
0104   const struct translation_table *branch;
0105   unsigned int             key;
0106 };
0107 
0108 static const struct translation_table trans_two[] =
0109 {
0110   { '~', 0, KEYS_INS },
0111   { 0,   0, 0 }
0112 };
0113 
0114 static const struct translation_table trans_three[] =
0115 {
0116   { '~', 0, KEYS_DEL },
0117   { 0,   0, 0 }
0118 };
0119 
0120 static const struct translation_table trans_tab_csi[] =
0121 {
0122   { '2', trans_two,   0 },
0123   { '3', trans_three, 0 },
0124   { 'A', 0,           KEYS_UARROW },
0125   { 'B', 0,           KEYS_DARROW },
0126   { 'D', 0,           KEYS_LARROW },
0127   { 'C', 0,           KEYS_RARROW },
0128   { 'F', 0,           KEYS_END },
0129   { 'H', 0,           KEYS_HOME },
0130   { 0,   0,           0 }
0131 };
0132 
0133 static const struct translation_table trans_tab_O[] =
0134 {
0135   { '1', 0, KEYS_F1 },
0136   { '2', 0, KEYS_F2 },
0137   { '3', 0, KEYS_F3 },
0138   { '4', 0, KEYS_F4 },
0139   { '5', 0, KEYS_F5 },
0140   { '6', 0, KEYS_F6 },
0141   { '7', 0, KEYS_F7 },
0142   { '8', 0, KEYS_F8 },
0143   { '9', 0, KEYS_F9 },
0144   { ':', 0, KEYS_F10 },
0145   { 'P', 0, KEYS_F1 },
0146   { 'Q', 0, KEYS_F2 },
0147   { 'R', 0, KEYS_F3 },
0148   { 'S', 0, KEYS_F4 },
0149   { 'T', 0, KEYS_F5 },
0150   { 'U', 0, KEYS_F6 },
0151   { 'V', 0, KEYS_F7 },
0152   { 'W', 0, KEYS_F8 },
0153   { 'X', 0, KEYS_F9 },
0154   { 'Y', 0, KEYS_F10 },
0155   { 0,   0, 0 }
0156 };
0157 
0158 static const struct translation_table trans_tab[] =
0159 {
0160   { '[', trans_tab_csi, 0 },    /* CSI command sequences */
0161   { 'O', trans_tab_O,   0 },    /* O are the fuction keys */
0162   { 0,   0,             0 }
0163 };
0164 
0165 /*
0166  * Perform a basic translation for some ANSI/VT100 key codes.
0167  * This code could do with a timeout on the ESC as it is
0168  * now lost from the input stream. It is not* used by the
0169  * line editor below so considiered not worth the effort.
0170  */
0171 
0172 static unsigned int
0173 rtems_monitor_getchar (void)
0174 {
0175   const struct translation_table *translation = 0;
0176   for (;;)
0177   {
0178     char c = getchar ();
0179     if (c == 27)
0180       translation = trans_tab;
0181     else
0182     {
0183       /*
0184        * If no translation happing just pass through
0185        * and return the key.
0186        */
0187       if (translation)
0188       {
0189         /*
0190          * Scan the current table for the key, and if found
0191          * see if this key is a fork. If so follow it and
0192          * wait else return the extended key.
0193          */
0194         int index    = 0;
0195         int branched = 0;
0196         while ((translation[index].expecting != '\0') ||
0197                (translation[index].key != '\0'))
0198         {
0199           if (translation[index].expecting == c)
0200           {
0201             /*
0202              * A branch is take if more keys are to come.
0203              */
0204             if (translation[index].branch == 0)
0205               return KEYS_EXTENDED | translation[index].key;
0206             else
0207             {
0208               translation = translation[index].branch;
0209               branched    = 1;
0210               break;
0211             }
0212           }
0213           index++;
0214         }
0215         /*
0216          * Who knows what these keys are, just drop them.
0217          */
0218         if (!branched)
0219           translation = 0;
0220       }
0221       else
0222         return c;
0223     }
0224   }
0225 }
0226 
0227 /*
0228  * The line editor with history.
0229  */
0230 
0231 static int
0232 rtems_monitor_line_editor (
0233     char *command
0234 )
0235 {
0236   int repeating = 0;
0237 
0238   memset (buffer, 0, RTEMS_COMMAND_BUFFER_SIZE);
0239   history = history_next;
0240   pos     = 0;
0241 
0242   if (!logged_in)
0243     fprintf(stdout,"\nMonitor ready, press enter to login.\n\n");
0244   else
0245     fprintf(stdout,"%s $ ", monitor_prompt);
0246 
0247   while (1)
0248   {
0249     unsigned int extended_key;
0250     char         c;
0251 
0252     fflush (stdout);
0253 
0254     extended_key = rtems_monitor_getchar ();
0255     c = extended_key & KEYS_NORMAL_MASK;
0256 
0257     /*
0258      * Make the extended_key usable as a boolean.
0259      */
0260     extended_key &= ~KEYS_NORMAL_MASK;
0261 
0262     if (!extended_key && !logged_in)
0263     {
0264       if (c == '\n')
0265       {
0266         logged_in = 1;
0267         /*
0268          * The prompt has changed from `>' to `$' to help know
0269          * which version of the monitor code people are using.
0270          */
0271         fprintf(stdout,"%s $ ", monitor_prompt);
0272       }
0273     }
0274     else
0275     {
0276       if (extended_key)
0277       {
0278         switch (c)
0279         {
0280           case KEYS_END:
0281             fprintf(stdout, "%s", buffer + pos);
0282             pos = (int) strlen (buffer);
0283             break;
0284 
0285           case KEYS_HOME:
0286             fprintf(stdout,"\r%s $ ", monitor_prompt);
0287             pos = 0;
0288             break;
0289 
0290           case KEYS_LARROW:
0291             if (pos > 0)
0292             {
0293               pos--;
0294               putchar ('\b');
0295             }
0296             break;
0297 
0298           case KEYS_RARROW:
0299             if ((pos < RTEMS_COMMAND_BUFFER_SIZE) && (buffer[pos] != '\0'))
0300             {
0301               putchar (buffer[pos]);
0302               pos++;
0303             }
0304             break;
0305 
0306           case KEYS_UARROW:
0307             /*
0308              * If we are moving up the histories then we need to save the working
0309              * buffer.
0310              */
0311             if (history)
0312             {
0313               int end;
0314               int bs;
0315               if (history == history_next)
0316               {
0317                 memcpy (history_buffer[history_next], buffer,
0318                         RTEMS_COMMAND_BUFFER_SIZE);
0319                 history_pos[history_next] = pos;
0320               }
0321               history--;
0322               memcpy (buffer, history_buffer[history],
0323                       RTEMS_COMMAND_BUFFER_SIZE);
0324               pos = history_pos[history];
0325               fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' ');
0326               fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer);
0327               end = (int) strlen (buffer);
0328               for (bs = 0; bs < (end - pos); bs++)
0329                 putchar ('\b');
0330             }
0331             break;
0332 
0333           case KEYS_DARROW:
0334             if (history < history_next)
0335             {
0336               int end;
0337               int bs;
0338               history++;
0339               memcpy (buffer, history_buffer[history],
0340                       RTEMS_COMMAND_BUFFER_SIZE);
0341               pos = history_pos[history];
0342               fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' ');
0343               fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer);
0344               end = (int) strlen (buffer);
0345               for (bs = 0; bs < (end - pos); bs++)
0346                 putchar ('\b');
0347             }
0348             break;
0349 
0350           case KEYS_DEL:
0351             if (buffer[pos] != '\0')
0352             {
0353               int end;
0354               int bs;
0355               memmove (&buffer[pos], &buffer[pos + 1],
0356                        strlen (&buffer[pos + 1]) + 1);
0357               fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer);
0358               end = (int) strlen (buffer);
0359               for (bs = 0; bs < (end - pos); bs++)
0360                 putchar ('\b');
0361             }
0362             break;
0363         }
0364       }
0365       else
0366       {
0367         switch (c)
0368         {
0369           case '\b':
0370           case '\x7e':
0371           case '\x7f':
0372             if (pos > 0)
0373             {
0374               int bs;
0375               pos--;
0376 
0377               /*
0378                * Memory operation used here instead of string
0379                * method due the src and dest of buffer overlapping.
0380                */
0381               memmove(
0382                 buffer + pos,
0383                 buffer + pos + 1,
0384                 RTEMS_COMMAND_BUFFER_SIZE - pos - 1
0385               );
0386               buffer[RTEMS_COMMAND_BUFFER_SIZE - 1] = '\0';
0387               fprintf(stdout,"\b%s \b", buffer + pos);
0388               for (bs = 0; bs < ((int) strlen (buffer) - pos); bs++)
0389                 putchar ('\b');
0390             }
0391             break;
0392 
0393           case '\n':
0394             /*
0395              * Process the command.
0396              */
0397             fprintf(stdout,"\n");
0398             repeating = 1;
0399             /*
0400              * Only process the history if we have a command and
0401              *a history.
0402              */
0403             if (strlen (buffer))
0404             {
0405               if (history_next && (history == history_next))
0406               {
0407                 /*
0408                  * Do not place the last command into the history
0409                  *if the same.
0410                  */
0411                 if (strcmp (history_buffer[history_next - 1], buffer))
0412                   repeating = 0;
0413               }
0414               else
0415                 repeating = 0;
0416             }
0417             if (!repeating)
0418             {
0419               memcpy (history_buffer[history_next], buffer,
0420                       RTEMS_COMMAND_BUFFER_SIZE);
0421               history_pos[history_next] = pos;
0422               if (history_next < (RTEMS_COMMAND_HISTORIES - 1))
0423                 history_next++;
0424               else
0425               {
0426                 memmove (history_buffer[0], history_buffer[1],
0427                          RTEMS_COMMAND_BUFFER_SIZE * (RTEMS_COMMAND_HISTORIES - 1));
0428                 memmove (&history_pos[0], &history_pos[1],
0429                          sizeof (history_pos[0]) * (RTEMS_COMMAND_HISTORIES - 1));
0430               }
0431             }
0432             else
0433             {
0434 #ifdef ENABLE_ENTER_REPEATS
0435               if (history_next)
0436                 memcpy (buffer, history_buffer[history_next - 1],
0437                         RTEMS_COMMAND_BUFFER_SIZE);
0438 #endif
0439             }
0440             memmove (command, buffer, RTEMS_COMMAND_BUFFER_SIZE);
0441             return repeating;
0442             break;
0443 
0444           default:
0445             if ((pos < (RTEMS_COMMAND_BUFFER_SIZE - 1)) &&
0446                 (c >= ' ') && (c <= 'z'))
0447             {
0448               int end;
0449               end = strlen (buffer);
0450               if ((pos < end) && (end < RTEMS_COMMAND_BUFFER_SIZE))
0451               {
0452                 int ch, bs;
0453                 for (ch = end; ch > pos; ch--)
0454                   buffer[ch] = buffer[ch - 1];
0455                 fprintf(stdout, "%s", buffer + pos);
0456                 for (bs = 0; bs < (end - pos + 1); bs++)
0457                   putchar ('\b');
0458               }
0459               buffer[pos++] = c;
0460               if (pos > end)
0461                 buffer[pos] = '\0';
0462               putchar (c);
0463             }
0464             break;
0465         }
0466       }
0467     }
0468   }
0469 }
0470 
0471 /*
0472  * make_argv(cp): token-count
0473  *  Break up the command line in 'cp' into global argv[] and argc (return
0474  *  value).
0475  */
0476 
0477 int
0478 rtems_monitor_make_argv(
0479     char *cp,
0480     int  *argc_p,
0481     char **argv)
0482 {
0483   int argc = 0;
0484 
0485   while ((cp = strtok(cp, " \t\n\r")))
0486   {
0487     argv[argc++] = cp;
0488     cp = (char *) NULL;
0489   }
0490   argv[argc] = (char *) NULL;      /* end of argv */
0491 
0492   return *argc_p = argc;
0493 }
0494 
0495 
0496 /*
0497  * Read and break up a monitor command
0498  *
0499  * We have to loop on the gets call, since it will return NULL under UNIX
0500  *  RTEMS when we get a signal (eg: SIGALRM).
0501  */
0502 
0503 int
0504 rtems_monitor_command_read(char *command,
0505                            int  *argc,
0506                            char **argv)
0507 {
0508     char *env_prompt;
0509 
0510     env_prompt = getenv("RTEMS_MONITOR_PROMPT");
0511 
0512   /*
0513    * put node number in the prompt if we are multiprocessing
0514    */
0515 #if defined(RTEMS_MULTIPROCESSING)
0516   if (!rtems_configuration_get_user_multiprocessing_table ())
0517     snprintf (monitor_prompt, sizeof(monitor_prompt), "%s",
0518              (env_prompt == NULL) ? MONITOR_PROMPT: env_prompt);
0519   else /* .... */
0520 #endif
0521   if (rtems_monitor_default_node != rtems_monitor_node)
0522     snprintf (monitor_prompt, sizeof(monitor_prompt),
0523               "%" PRId32 "-%s-%" PRId32 "", rtems_monitor_node,
0524              (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt,
0525              rtems_monitor_default_node);
0526   else
0527     snprintf (monitor_prompt, sizeof(monitor_prompt),
0528              "%" PRId32 "-%s", rtems_monitor_node,
0529              (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt);
0530 
0531   rtems_monitor_line_editor (command);
0532 
0533   return rtems_monitor_make_argv (command, argc, argv);
0534 }
0535 
0536 /*
0537  * Main monitor command loop
0538  */
0539 
0540 void
0541 rtems_monitor_task(
0542     rtems_task_argument monitor_flags
0543 )
0544 {
0545     char command_buffer[513];
0546     int argc;
0547     char *argv[64];
0548     bool  verbose = false;
0549     struct termios term;
0550 
0551     /*
0552      * Make the stdin stream characte not line based.
0553      */
0554 
0555     if (tcgetattr (STDIN_FILENO, &term) < 0)
0556     {
0557       fprintf(stdout,"rtems-monitor: cannot get terminal attributes.\n");
0558     }
0559     else
0560     {
0561       /*
0562        * No echo, no canonical processing.
0563        */
0564 
0565       term.c_lflag &= ~(ECHO | ICANON | IEXTEN);
0566 
0567       /*
0568        * No sigint on BREAK, CR-to-NL off, input parity off,
0569        * don't strip 8th bit on input, output flow control off
0570        */
0571 
0572       term.c_lflag    &= ~(INPCK | ISTRIP | IXON);
0573       term.c_cc[VMIN]  = 1;
0574       term.c_cc[VTIME] = 0;
0575 
0576       if (tcsetattr (STDIN_FILENO, TCSANOW, &term) < 0)
0577       {
0578         fprintf(stdout,"cannot set terminal attributes\n");
0579       }
0580     }
0581 
0582     if (!(monitor_flags & RTEMS_MONITOR_NOSYMLOAD)) {
0583       rtems_monitor_symbols_loadup();
0584     }
0585 
0586     if (monitor_flags & RTEMS_MONITOR_SUSPEND)
0587         (void) rtems_monitor_suspend(RTEMS_NO_TIMEOUT);
0588 
0589     for (;;)
0590     {
0591         const rtems_monitor_command_entry_t *command;
0592 
0593         if (0 == rtems_monitor_command_read(command_buffer, &argc, argv))
0594             continue;
0595         if (argc < 1
0596           || (command = rtems_monitor_command_lookup(argv [0])) == 0) {
0597           /* no command */
0598           fprintf(stdout,"Unrecognised command; try 'help'\n");
0599           continue;
0600         }
0601 
0602         command->command_function(argc, argv, &command->command_arg, verbose);
0603 
0604         fflush(stdout);
0605     }
0606 }
0607 
0608 
0609 void
0610 rtems_monitor_kill(void)
0611 {
0612     if (rtems_monitor_task_id)
0613         rtems_task_delete(rtems_monitor_task_id);
0614     rtems_monitor_task_id = 0;
0615 
0616     rtems_monitor_server_kill();
0617 }
0618 
0619 void
0620 rtems_monitor_init(
0621     uint32_t   monitor_flags
0622 )
0623 {
0624     rtems_status_code status;
0625 
0626     rtems_monitor_kill();
0627 
0628     status = rtems_task_create(RTEMS_MONITOR_NAME,
0629                                1,
0630                                RTEMS_MINIMUM_STACK_SIZE * 2,
0631                                RTEMS_INTERRUPT_LEVEL(0),
0632                                RTEMS_DEFAULT_ATTRIBUTES,
0633                                &rtems_monitor_task_id);
0634     if (status != RTEMS_SUCCESSFUL)
0635     {
0636         rtems_error(status, "could not create monitor task");
0637         return;
0638     }
0639 
0640     rtems_monitor_node = rtems_object_id_get_node(rtems_monitor_task_id);
0641     rtems_monitor_default_node = rtems_monitor_node;
0642 
0643     rtems_monitor_server_init(monitor_flags);
0644 
0645     if (!(monitor_flags & RTEMS_MONITOR_NOTASK)) {
0646       /*
0647        * Start the monitor task itself
0648        */
0649       status = rtems_task_start(
0650         rtems_monitor_task_id, rtems_monitor_task, monitor_flags);
0651       if (status != RTEMS_SUCCESSFUL) {
0652         rtems_error(status, "could not start monitor");
0653         return;
0654       }
0655    }
0656 }