1 /*
2  * Copyright (c) 2006-2025, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2006-04-30     Bernard      the first version for FinSH
9  * 2006-05-08     Bernard      change finsh thread stack to 2048
10  * 2006-06-03     Bernard      add support for skyeye
11  * 2006-09-24     Bernard      remove the code related with hardware
12  * 2010-01-18     Bernard      fix down then up key bug.
13  * 2010-03-19     Bernard      fix backspace issue and fix device read in shell.
14  * 2010-04-01     Bernard      add prompt output when start and remove the empty history
15  * 2011-02-23     Bernard      fix variable section end issue of finsh shell
16  *                             initialization when use GNU GCC compiler.
17  * 2016-11-26     armink       add password authentication
18  * 2018-07-02     aozima       add custom prompt support.
19  */
20 
21 #include <rthw.h>
22 #include <string.h>
23 #include <stdio.h>
24 
25 #ifdef RT_USING_FINSH
26 
27 #include "shell.h"
28 #include "msh.h"
29 
30 #ifdef DFS_USING_POSIX
31 #include <unistd.h>
32 #include <fcntl.h>
33 #endif /* DFS_USING_POSIX */
34 
35 #ifdef RT_USING_POSIX_STDIO
36 #include <unistd.h>
37 #include <posix/stdio.h>
38 #endif /* RT_USING_POSIX_STDIO */
39 
40 /* finsh thread */
41 #ifndef RT_USING_HEAP
42     static struct rt_thread finsh_thread;
43     rt_align(RT_ALIGN_SIZE)
44     static char finsh_thread_stack[FINSH_THREAD_STACK_SIZE];
45     struct finsh_shell _shell;
46 #endif
47 
48 /* finsh symtab */
49 #ifdef FINSH_USING_SYMTAB
50     struct finsh_syscall *_syscall_table_begin  = NULL;
51     struct finsh_syscall *_syscall_table_end    = NULL;
52 #endif
53 
54 struct finsh_shell *shell;
55 static char *finsh_prompt_custom = RT_NULL;
56 
57 #if defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__))
finsh_syscall_next(struct finsh_syscall * call)58 struct finsh_syscall *finsh_syscall_next(struct finsh_syscall *call)
59 {
60     unsigned int *ptr;
61     ptr = (unsigned int *)(call + 1);
62     while ((*ptr == 0) && ((unsigned int *)ptr < (unsigned int *) _syscall_table_end))
63         ptr ++;
64 
65     return (struct finsh_syscall *)ptr;
66 }
67 
68 #endif /* defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__)) */
69 
70 #ifdef RT_USING_HEAP
finsh_set_prompt(const char * prompt)71 int finsh_set_prompt(const char *prompt)
72 {
73     if (finsh_prompt_custom)
74     {
75         rt_free(finsh_prompt_custom);
76         finsh_prompt_custom = RT_NULL;
77     }
78 
79     /* strdup */
80     if (prompt)
81     {
82         finsh_prompt_custom = (char *)rt_malloc(strlen(prompt) + 1);
83         if (finsh_prompt_custom)
84         {
85             strcpy(finsh_prompt_custom, prompt);
86         }
87     }
88 
89     return 0;
90 }
91 #endif /* RT_USING_HEAP */
92 
93 #define _MSH_PROMPT "msh "
94 
finsh_get_prompt(void)95 const char *finsh_get_prompt(void)
96 {
97     static char finsh_prompt[RT_CONSOLEBUF_SIZE + 1] = {0};
98 
99     /* check prompt mode */
100     if (!shell->prompt_mode)
101     {
102         finsh_prompt[0] = '\0';
103         return finsh_prompt;
104     }
105 
106     if (finsh_prompt_custom)
107     {
108         strncpy(finsh_prompt, finsh_prompt_custom, sizeof(finsh_prompt) - 1);
109     }
110     else
111     {
112         strcpy(finsh_prompt, _MSH_PROMPT);
113     }
114 
115 #if defined(DFS_USING_POSIX) && defined(DFS_USING_WORKDIR)
116     /* get current working directory */
117     getcwd(&finsh_prompt[rt_strlen(finsh_prompt)], RT_CONSOLEBUF_SIZE - rt_strlen(finsh_prompt));
118 #endif
119 
120     if (rt_strlen(finsh_prompt) + 2 < RT_CONSOLEBUF_SIZE)
121     {
122         finsh_prompt[rt_strlen(finsh_prompt)] = '>';
123         finsh_prompt[rt_strlen(finsh_prompt) + 1] = '\0';
124     }
125 
126     return finsh_prompt;
127 }
128 
129 /**
130  * @ingroup group_finsh
131  *
132  * This function get the prompt mode of finsh shell.
133  *
134  * @return prompt the prompt mode, 0 disable prompt mode, other values enable prompt mode.
135  */
finsh_get_prompt_mode(void)136 rt_uint32_t finsh_get_prompt_mode(void)
137 {
138     RT_ASSERT(shell != RT_NULL);
139     return shell->prompt_mode;
140 }
141 
142 /**
143  * @ingroup group_finsh
144  *
145  * This function set the prompt mode of finsh shell.
146  *
147  * The parameter 0 disable prompt mode, other values enable prompt mode.
148  *
149  * @param prompt_mode the prompt mode
150  */
finsh_set_prompt_mode(rt_uint32_t prompt_mode)151 void finsh_set_prompt_mode(rt_uint32_t prompt_mode)
152 {
153     RT_ASSERT(shell != RT_NULL);
154     shell->prompt_mode = prompt_mode;
155 }
156 
finsh_getchar(void)157 int finsh_getchar(void)
158 {
159 #ifdef RT_USING_DEVICE
160     char ch = 0;
161 #ifdef RT_USING_POSIX_STDIO
162     if(read(rt_posix_stdio_get_console(), &ch, 1) > 0)
163     {
164         return ch;
165     }
166     else
167     {
168         return -1; /* EOF */
169     }
170 #else
171     rt_device_t device;
172 
173     RT_ASSERT(shell != RT_NULL);
174 
175     device = shell->device;
176     if (device == RT_NULL)
177     {
178         return -1; /* EOF */
179     }
180 
181     while (rt_device_read(device, -1, &ch, 1) != 1)
182     {
183         rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER);
184         if (shell->device != device)
185         {
186             device = shell->device;
187             if (device == RT_NULL)
188             {
189                 return -1;
190             }
191         }
192     }
193     return ch;
194 #endif /* RT_USING_POSIX_STDIO */
195 #else
196     extern signed char rt_hw_console_getchar(void);
197     return rt_hw_console_getchar();
198 #endif /* RT_USING_DEVICE */
199 }
200 
201 #if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE)
finsh_rx_ind(rt_device_t dev,rt_size_t size)202 static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size)
203 {
204     RT_ASSERT(shell != RT_NULL);
205 
206     /* release semaphore to let finsh thread rx data */
207     rt_sem_release(&shell->rx_sem);
208 
209     return RT_EOK;
210 }
211 
212 /**
213  * @ingroup group_finsh
214  *
215  * This function sets the input device of finsh shell.
216  *
217  * @param device_name the name of new input device.
218  */
finsh_set_device(const char * device_name)219 void finsh_set_device(const char *device_name)
220 {
221     rt_device_t dev = RT_NULL;
222 
223     RT_ASSERT(shell != RT_NULL);
224     dev = rt_device_find(device_name);
225     if (dev == RT_NULL)
226     {
227         rt_kprintf("finsh: can not find device: %s\n", device_name);
228         return;
229     }
230 
231     /* check whether it's a same device */
232     if (dev == shell->device) return;
233     /* open this device and set the new device in finsh shell */
234     if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | \
235                        RT_DEVICE_FLAG_STREAM) == RT_EOK)
236     {
237         if (shell->device != RT_NULL)
238         {
239             /* close old finsh device */
240             rt_device_close(shell->device);
241             rt_device_set_rx_indicate(shell->device, RT_NULL);
242         }
243 
244         /* clear line buffer before switch to new device */
245         rt_memset(shell->line, 0, sizeof(shell->line));
246         shell->line_curpos = shell->line_position = 0;
247 
248         shell->device = dev;
249         rt_device_set_rx_indicate(dev, finsh_rx_ind);
250     }
251 }
252 
253 /**
254  * @ingroup group_finsh
255  *
256  * This function returns current finsh shell input device.
257  *
258  * @return the finsh shell input device name is returned.
259  */
finsh_get_device()260 const char *finsh_get_device()
261 {
262     RT_ASSERT(shell != RT_NULL);
263     return shell->device->parent.name;
264 }
265 #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */
266 
267 /**
268  * @ingroup group_finsh
269  *
270  * This function set the echo mode of finsh shell.
271  *
272  * FINSH_OPTION_ECHO=0x01 is echo mode, other values are none-echo mode.
273  *
274  * @param echo the echo mode
275  */
finsh_set_echo(rt_uint32_t echo)276 void finsh_set_echo(rt_uint32_t echo)
277 {
278     RT_ASSERT(shell != RT_NULL);
279     shell->echo_mode = (rt_uint8_t)echo;
280 }
281 
282 /**
283  * @ingroup group_finsh
284  *
285  * This function gets the echo mode of finsh shell.
286  *
287  * @return the echo mode
288  */
finsh_get_echo()289 rt_uint32_t finsh_get_echo()
290 {
291     RT_ASSERT(shell != RT_NULL);
292 
293     return shell->echo_mode;
294 }
295 
296 #ifdef FINSH_USING_AUTH
297 /**
298  * set a new password for finsh
299  *
300  * @param password new password
301  *
302  * @return result, RT_EOK on OK, -RT_ERROR on the new password length is less than
303  *  FINSH_PASSWORD_MIN or greater than FINSH_PASSWORD_MAX
304  */
finsh_set_password(const char * password)305 rt_err_t finsh_set_password(const char *password)
306 {
307     rt_base_t level;
308     rt_size_t pw_len = rt_strlen(password);
309 
310     if (pw_len < FINSH_PASSWORD_MIN || pw_len > FINSH_PASSWORD_MAX)
311         return -RT_ERROR;
312 
313     level = rt_hw_interrupt_disable();
314     rt_strncpy(shell->password, password, FINSH_PASSWORD_MAX);
315     rt_hw_interrupt_enable(level);
316 
317     return RT_EOK;
318 }
319 
320 /**
321  * get the finsh password
322  *
323  * @return password
324  */
finsh_get_password(void)325 const char *finsh_get_password(void)
326 {
327     return shell->password;
328 }
329 
finsh_wait_auth(void)330 static void finsh_wait_auth(void)
331 {
332     int ch;
333     rt_bool_t input_finish = RT_FALSE;
334     char password[FINSH_PASSWORD_MAX] = { 0 };
335     rt_size_t cur_pos = 0;
336     /* password not set */
337     if (rt_strlen(finsh_get_password()) == 0) return;
338 
339     while (1)
340     {
341         rt_kprintf("Password for login: ");
342         while (!input_finish)
343         {
344             while (1)
345             {
346                 /* read one character from device */
347                 ch = (int)finsh_getchar();
348                 if (ch < 0)
349                 {
350                     continue;
351                 }
352 
353                 if (ch >= ' ' && ch <= '~' && cur_pos < FINSH_PASSWORD_MAX)
354                 {
355                     /* change the printable characters to '*' */
356                     rt_kprintf("*");
357                     password[cur_pos++] = ch;
358                 }
359                 else if (ch == '\b' && cur_pos > 0)
360                 {
361                     /* backspace */
362                     cur_pos--;
363                     password[cur_pos] = '\0';
364                     rt_kprintf("\b \b");
365                 }
366                 else if (ch == '\r' || ch == '\n')
367                 {
368                     rt_kprintf("\n");
369                     input_finish = RT_TRUE;
370                     break;
371                 }
372             }
373         }
374         if (!rt_strncmp(shell->password, password, FINSH_PASSWORD_MAX)) return;
375         else
376         {
377             /* authentication failed, delay 2S for retry */
378             rt_thread_delay(2 * RT_TICK_PER_SECOND);
379             rt_kprintf("Sorry, try again.\n");
380             cur_pos = 0;
381             input_finish = RT_FALSE;
382             rt_memset(password, '\0', FINSH_PASSWORD_MAX);
383         }
384     }
385 }
386 #endif /* FINSH_USING_AUTH */
387 
shell_auto_complete(char * prefix)388 static void shell_auto_complete(char *prefix)
389 {
390     rt_kprintf("\n");
391     msh_auto_complete(prefix);
392 
393 #ifdef FINSH_USING_OPTION_COMPLETION
394     msh_opt_auto_complete(prefix);
395 #endif
396 
397     rt_kprintf("%s%s", FINSH_PROMPT, prefix);
398 }
399 
400 #ifdef FINSH_USING_HISTORY
shell_handle_history(struct finsh_shell * shell)401 static rt_bool_t shell_handle_history(struct finsh_shell *shell)
402 {
403 #if defined(_WIN32)
404     int i;
405     rt_kprintf("\r");
406 
407     for (i = 0; i <= 60; i++)
408         putchar(' ');
409     rt_kprintf("\r");
410 
411 #else
412     rt_kprintf("\033[2K\r");
413 #endif
414     rt_kprintf("%s%s", FINSH_PROMPT, shell->line);
415     return RT_FALSE;
416 }
417 
shell_push_history(struct finsh_shell * shell)418 static void shell_push_history(struct finsh_shell *shell)
419 {
420     if (shell->line_position != 0)
421     {
422         /* push history */
423         if (shell->history_count >= FINSH_HISTORY_LINES)
424         {
425             /* if current cmd is same as last cmd, don't push */
426             if (memcmp(&shell->cmd_history[FINSH_HISTORY_LINES - 1], shell->line, FINSH_CMD_SIZE))
427             {
428                 /* move history */
429                 int index;
430                 for (index = 0; index < FINSH_HISTORY_LINES - 1; index ++)
431                 {
432                     rt_memcpy(&shell->cmd_history[index][0],
433                            &shell->cmd_history[index + 1][0], FINSH_CMD_SIZE);
434                 }
435                 rt_memset(&shell->cmd_history[index][0], 0, FINSH_CMD_SIZE);
436                 rt_memcpy(&shell->cmd_history[index][0], shell->line, shell->line_position);
437 
438                 /* it's the maximum history */
439                 shell->history_count = FINSH_HISTORY_LINES;
440             }
441         }
442         else
443         {
444             /* if current cmd is same as last cmd, don't push */
445             if (shell->history_count == 0 || memcmp(&shell->cmd_history[shell->history_count - 1], shell->line, FINSH_CMD_SIZE))
446             {
447                 shell->current_history = shell->history_count;
448                 rt_memset(&shell->cmd_history[shell->history_count][0], 0, FINSH_CMD_SIZE);
449                 rt_memcpy(&shell->cmd_history[shell->history_count][0], shell->line, shell->line_position);
450 
451                 /* increase count and set current history position */
452                 shell->history_count ++;
453             }
454         }
455     }
456     shell->current_history = shell->history_count;
457 }
458 #endif
459 
460 #if defined(FINSH_USING_WORD_OPERATION)
find_prev_word_start(const char * line,int curpos)461 static int find_prev_word_start(const char *line, int curpos)
462 {
463     if (curpos <= 0) return 0;
464 
465     /* Skip whitespace */
466     while (--curpos > 0 && (line[curpos] == ' ' || line[curpos] == '\t'));
467 
468     /* Find word start */
469     while (curpos > 0 && !(line[curpos] == ' ' || line[curpos] == '\t'))
470         curpos--;
471 
472     return (curpos <= 0) ? 0 : curpos + 1;
473 }
474 
find_next_word_end(const char * line,int curpos,int max)475 static int find_next_word_end(const char *line, int curpos, int max)
476 {
477     if (curpos >= max) return max;
478 
479     /* Skip to next word */
480     while (curpos < max && (line[curpos] == ' ' || line[curpos] == '\t'))
481         curpos++;
482 
483     /* Find word end */
484     while (curpos < max && !(line[curpos] == ' ' || line[curpos] == '\t'))
485         curpos++;
486 
487     return curpos;
488 }
489 #endif /* defined(FINSH_USING_WORD_OPERATION) */
490 
491 #ifdef RT_USING_HOOK
492 static void (*_finsh_thread_entry_hook)(void);
493 
494 /**
495  * @ingroup group_finsh
496  *
497  * @brief This function set a hook function at the entry of finsh thread
498  *
499  * @param hook the function point to be called
500  */
finsh_thread_entry_sethook(void (* hook)(void))501 void finsh_thread_entry_sethook(void (*hook)(void))
502 {
503     _finsh_thread_entry_hook = hook;
504 }
505 #endif /* RT_USING_HOOK */
506 
finsh_thread_entry(void * parameter)507 static void finsh_thread_entry(void *parameter)
508 {
509     int ch;
510 
511     RT_OBJECT_HOOK_CALL(_finsh_thread_entry_hook, ());
512 
513     /* normal is echo mode */
514 #ifndef FINSH_ECHO_DISABLE_DEFAULT
515     shell->echo_mode = 1;
516 #else
517     shell->echo_mode = 0;
518 #endif
519 
520 #if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE)
521     /* set console device as shell device */
522     if (shell->device == RT_NULL)
523     {
524         rt_device_t console = rt_console_get_device();
525         if (console)
526         {
527             finsh_set_device(console->parent.name);
528         }
529     }
530 #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */
531 
532 #ifdef FINSH_USING_AUTH
533     /* set the default password when the password isn't setting */
534     if (rt_strlen(finsh_get_password()) == 0)
535     {
536         if (finsh_set_password(FINSH_DEFAULT_PASSWORD) != RT_EOK)
537         {
538             rt_kprintf("Finsh password set failed.\n");
539         }
540     }
541     /* waiting authenticate success */
542     finsh_wait_auth();
543 #endif
544 
545     rt_kprintf(FINSH_PROMPT);
546 
547     while (1)
548     {
549         ch = (int)finsh_getchar();
550         if (ch < 0)
551         {
552             continue;
553         }
554 
555         /*
556          * handle control key
557          * up key  : 0x1b 0x5b 0x41
558          * down key: 0x1b 0x5b 0x42
559          * right key:0x1b 0x5b 0x43
560          * left key: 0x1b 0x5b 0x44
561          */
562         if (ch == 0x1b)
563         {
564             shell->stat = WAIT_SPEC_KEY;
565             continue;
566         }
567         else if (shell->stat == WAIT_SPEC_KEY)
568         {
569             if (ch == 0x5b || ch == 0x41 || ch == 0x42 || ch == 0x43 || ch == 0x44)
570             {
571                 shell->stat = WAIT_FUNC_KEY;
572                 continue;
573             }
574 
575             shell->stat = WAIT_NORMAL;
576         }
577         else if (shell->stat == WAIT_FUNC_KEY)
578         {
579             shell->stat = WAIT_NORMAL;
580 
581             if (ch == 0x41) /* up key */
582             {
583 #ifdef FINSH_USING_HISTORY
584                 /* prev history */
585                 if (shell->current_history > 0)
586                     shell->current_history --;
587                 else
588                 {
589                     shell->current_history = 0;
590                     continue;
591                 }
592 
593                 /* copy the history command */
594                 rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
595                        FINSH_CMD_SIZE);
596                 shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
597                 shell_handle_history(shell);
598 #endif
599                 continue;
600             }
601             else if (ch == 0x42) /* down key */
602             {
603 #ifdef FINSH_USING_HISTORY
604                 /* next history */
605                 if (shell->current_history < shell->history_count - 1)
606                     shell->current_history ++;
607                 else
608                 {
609                     /* set to the end of history */
610                     if (shell->history_count != 0)
611                         shell->current_history = shell->history_count - 1;
612                     else
613                         continue;
614                 }
615 
616                 rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
617                        FINSH_CMD_SIZE);
618                 shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
619                 shell_handle_history(shell);
620 #endif
621                 continue;
622             }
623             else if (ch == 0x44) /* left key */
624             {
625                 if (shell->line_curpos)
626                 {
627                     rt_kprintf("\b");
628                     shell->line_curpos --;
629                 }
630 
631                 continue;
632             }
633             else if (ch == 0x43) /* right key */
634             {
635                 if (shell->line_curpos < shell->line_position)
636                 {
637                     rt_kprintf("%c", shell->line[shell->line_curpos]);
638                     shell->line_curpos ++;
639                 }
640 
641                 continue;
642             }
643 #if defined(FINSH_USING_WORD_OPERATION)
644             /* Add Ctrl+Left/Right handling */
645             else if (ch == '1')
646             {
647                 /* Read modifier sequence [1;5D/C] */
648                 int next_ch = finsh_getchar();
649                 if (next_ch == ';')
650                 {
651                     next_ch = finsh_getchar();
652                     if (next_ch == '5')
653                     {
654                         next_ch = finsh_getchar();
655                         if (next_ch == 'D') /* Ctrl+Left */
656                         {
657                             int new_pos = find_prev_word_start(shell->line, shell->line_curpos);
658                             if (new_pos != shell->line_curpos)
659                             {
660                                 rt_kprintf("\033[%dD", shell->line_curpos - new_pos);
661                                 shell->line_curpos = new_pos;
662                             }
663                             continue;
664                         }
665                         else if (next_ch == 'C') /* Ctrl+Right */
666                         {
667                             int new_pos = find_next_word_end(shell->line, shell->line_curpos, shell->line_position);
668                             if (new_pos != shell->line_curpos)
669                             {
670                                 rt_kprintf("\033[%dC", new_pos - shell->line_curpos);
671                                 shell->line_curpos = new_pos;
672                             }
673                             continue;
674                         }
675                     }
676                 }
677             }
678 #endif /*defined(FINSH_USING_WORD_OPERATION) */
679         }
680 
681         /* received null or error */
682         if (ch == '\0' || ch == 0xFF) continue;
683         /* handle tab key */
684         else if (ch == '\t')
685         {
686             int i;
687             /* move the cursor to the beginning of line */
688             for (i = 0; i < shell->line_curpos; i++)
689                 rt_kprintf("\b");
690 
691             /* auto complete */
692             shell_auto_complete(&shell->line[0]);
693             /* re-calculate position */
694             shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
695 
696             continue;
697         }
698         /* handle backspace key */
699         else if (ch == 0x7f || ch == 0x08)
700         {
701             /* note that shell->line_curpos >= 0 */
702             if (shell->line_curpos == 0)
703                 continue;
704 
705             shell->line_position--;
706             shell->line_curpos--;
707 
708             if (shell->line_position > shell->line_curpos)
709             {
710                 int i;
711 
712                 rt_memmove(&shell->line[shell->line_curpos],
713                            &shell->line[shell->line_curpos + 1],
714                            shell->line_position - shell->line_curpos);
715                 shell->line[shell->line_position] = 0;
716 
717                 rt_kprintf("\b%s  \b", &shell->line[shell->line_curpos]);
718 
719                 /* move the cursor to the origin position */
720                 for (i = shell->line_curpos; i <= shell->line_position; i++)
721                     rt_kprintf("\b");
722             }
723             else
724             {
725                 rt_kprintf("\b \b");
726                 shell->line[shell->line_position] = 0;
727             }
728 
729             continue;
730         }
731 #if defined(FINSH_USING_WORD_OPERATION)
732         /* Add Ctrl+Backspace handling */
733         else if (ch == 0x17) /* Ctrl+Backspace (typically ^W) */
734         {
735             if (shell->line_curpos == 0) continue;
736 
737             int start = find_prev_word_start(shell->line, shell->line_curpos);
738             int del_count = shell->line_curpos - start;
739             int new_len = shell->line_position - del_count;
740 
741             /* Delete characters and properly add RT_NULL termination */
742             rt_memmove(&shell->line[start],
743                        &shell->line[start + del_count],
744                        new_len - start + 1);
745 
746             /* Clear residual data */
747             rt_memset(&shell->line[new_len], 0, shell->line_position - new_len);
748 
749             /* Update positions */
750             shell->line_position = new_len;
751             shell->line_curpos = start;
752 
753             /* Redraw the affected line section */
754             rt_kprintf("\033[%dD", del_count);
755             /* Rewrite the remaining content */
756             rt_kprintf("%.*s", shell->line_position - start, &shell->line[start]);
757             /* Clear trailing artifacts */
758             rt_kprintf("\033[K");
759             if (shell->line_position > start)
760             {
761                 /* Reset cursor */
762                 rt_kprintf("\033[%dD", shell->line_position - start);
763             }
764 
765             continue;
766         }
767 #endif /*defined(FINSH_USING_WORD_OPERATION) */
768         /* handle end of line, break */
769         if (ch == '\r' || ch == '\n')
770         {
771 #ifdef FINSH_USING_HISTORY
772             shell_push_history(shell);
773 #endif
774             if (shell->echo_mode)
775                 rt_kprintf("\n");
776             msh_exec(shell->line, shell->line_position);
777 
778             rt_kprintf(FINSH_PROMPT);
779             rt_memset(shell->line, 0, sizeof(shell->line));
780             shell->line_curpos = shell->line_position = 0;
781             continue;
782         }
783 
784         /* it's a large line, discard it */
785         if (shell->line_position >= FINSH_CMD_SIZE)
786             shell->line_position = 0;
787 
788         /* normal character */
789         if (shell->line_curpos < shell->line_position)
790         {
791             int i;
792 
793             rt_memmove(&shell->line[shell->line_curpos + 1],
794                        &shell->line[shell->line_curpos],
795                        shell->line_position - shell->line_curpos);
796             shell->line[shell->line_curpos] = ch;
797             if (shell->echo_mode)
798                 rt_kprintf("%s", &shell->line[shell->line_curpos]);
799 
800             /* move the cursor to new position */
801             for (i = shell->line_curpos; i < shell->line_position; i++)
802                 rt_kprintf("\b");
803         }
804         else
805         {
806             shell->line[shell->line_position] = ch;
807             if (shell->echo_mode)
808                 rt_kprintf("%c", ch);
809         }
810 
811         ch = 0;
812         shell->line_position ++;
813         shell->line_curpos++;
814         if (shell->line_position >= FINSH_CMD_SIZE)
815         {
816             /* clear command line */
817             shell->line_position = 0;
818             shell->line_curpos = 0;
819         }
820     } /* end of device read */
821 }
822 
finsh_system_function_init(const void * begin,const void * end)823 static void finsh_system_function_init(const void *begin, const void *end)
824 {
825     _syscall_table_begin = (struct finsh_syscall *) begin;
826     _syscall_table_end = (struct finsh_syscall *) end;
827 }
828 
829 #if defined(__ICCARM__) || defined(__ICCRX__)               /* for IAR compiler */
830 #ifdef FINSH_USING_SYMTAB
831     #pragma section="FSymTab"
832 #endif
833 #elif defined(__ADSPBLACKFIN__) /* for VisaulDSP++ Compiler*/
834 #ifdef FINSH_USING_SYMTAB
835     extern "asm" int __fsymtab_start;
836     extern "asm" int __fsymtab_end;
837 #endif
838 #elif defined(_MSC_VER)
839 #pragma section("FSymTab$a", read)
840 const char __fsym_begin_name[] = "__start";
841 const char __fsym_begin_desc[] = "begin of finsh";
842 __declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin =
843 {
844     __fsym_begin_name,
845     __fsym_begin_desc,
846     NULL
847 };
848 
849 #pragma section("FSymTab$z", read)
850 const char __fsym_end_name[] = "__end";
851 const char __fsym_end_desc[] = "end of finsh";
852 __declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end =
853 {
854     __fsym_end_name,
855     __fsym_end_desc,
856     NULL
857 };
858 #endif
859 
860 /*
861  * @ingroup group_finsh
862  *
863  * This function will initialize finsh shell
864  */
finsh_system_init(void)865 int finsh_system_init(void)
866 {
867     rt_err_t result = RT_EOK;
868     rt_thread_t tid;
869 
870 #ifdef FINSH_USING_SYMTAB
871 #ifdef __ARMCC_VERSION  /* ARM C Compiler */
872     extern const int FSymTab$$Base;
873     extern const int FSymTab$$Limit;
874     finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit);
875 #elif defined (__ICCARM__) || defined(__ICCRX__)      /* for IAR Compiler */
876     finsh_system_function_init(__section_begin("FSymTab"),
877                                __section_end("FSymTab"));
878 #elif defined (__GNUC__) || defined(__TI_COMPILER_VERSION__) || defined(__TASKING__)
879     /* GNU GCC Compiler and TI CCS */
880     extern const int __fsymtab_start;
881     extern const int __fsymtab_end;
882     finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
883 #elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */
884     finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
885 #elif defined(_MSC_VER)
886     unsigned int *ptr_begin, *ptr_end;
887 
888     if (shell)
889     {
890         rt_kprintf("finsh shell already init.\n");
891         return RT_EOK;
892     }
893 
894     ptr_begin = (unsigned int *)&__fsym_begin;
895     ptr_begin += (sizeof(struct finsh_syscall) / sizeof(unsigned int));
896     while (*ptr_begin == 0) ptr_begin ++;
897 
898     ptr_end = (unsigned int *) &__fsym_end;
899     ptr_end --;
900     while (*ptr_end == 0) ptr_end --;
901 
902     finsh_system_function_init(ptr_begin, ptr_end);
903 #endif
904 #endif
905 
906 #ifdef RT_USING_HEAP
907     /* create or set shell structure */
908     shell = (struct finsh_shell *)rt_calloc(1, sizeof(struct finsh_shell));
909     if (shell == RT_NULL)
910     {
911         rt_kprintf("no memory for shell\n");
912         return -1;
913     }
914     tid = rt_thread_create(FINSH_THREAD_NAME,
915                            finsh_thread_entry, RT_NULL,
916                            FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10);
917 #else
918     shell = &_shell;
919     tid = &finsh_thread;
920     result = rt_thread_init(&finsh_thread,
921                             FINSH_THREAD_NAME,
922                             finsh_thread_entry, RT_NULL,
923                             &finsh_thread_stack[0], sizeof(finsh_thread_stack),
924                             FINSH_THREAD_PRIORITY, 10);
925 #endif /* RT_USING_HEAP */
926 
927     rt_sem_init(&(shell->rx_sem), "shrx", 0, 0);
928     finsh_set_prompt_mode(1);
929 
930     if (tid != NULL && result == RT_EOK)
931         rt_thread_startup(tid);
932     return 0;
933 }
934 INIT_APP_EXPORT(finsh_system_init);
935 
936 #endif /* RT_USING_FINSH */
937 
938