Revision 697dba3c core/src/aos_shell.c
core/src/aos_shell.c | ||
---|---|---|
35 | 35 |
/******************************************************************************/ |
36 | 36 |
|
37 | 37 |
/** |
38 |
* @brief The character the input buffer is initialized with. |
|
39 |
*/ |
|
40 |
#define INBUF_INIT_CHAR '\x07' |
|
41 |
|
|
42 |
/** |
|
38 | 43 |
* @brief Event mask to be set on OS related events. |
39 | 44 |
*/ |
40 |
#define AOS_SHELL_EVENTMASK_OS EVENT_MASK(0)
|
|
45 |
#define EVENTMASK_OS EVENT_MASK(0)
|
|
41 | 46 |
|
42 | 47 |
/** |
43 | 48 |
* @brief Event mask to be set on a input event. |
44 | 49 |
*/ |
45 |
#define AOS_SHELL_EVENTMASK_INPUT EVENT_MASK(1) |
|
50 |
#define EVENTMASK_INPUT EVENT_MASK(1) |
|
51 |
|
|
52 |
/** |
|
53 |
* @brief String that defines the INSERT key as specified by VT100. |
|
54 |
*/ |
|
55 |
#define KEYSTRING_INSERT "\x1B\x5B\x32\x7E" |
|
56 |
|
|
57 |
/** |
|
58 |
* @brief String that defines the DEL key as specified by VT100. |
|
59 |
*/ |
|
60 |
#define KEYSTRING_DELETE "\x1B\x5B\x33\x7E" |
|
61 |
|
|
62 |
/** |
|
63 |
* @brief String that defines the HOME key as specified by VT100. |
|
64 |
*/ |
|
65 |
#define KEYSTRING_HOME "\x1B\x5B\x48" |
|
66 |
|
|
67 |
/** |
|
68 |
* @brief String that defines the END key as specified by VT100. |
|
69 |
*/ |
|
70 |
#define KEYSTRING_END "\x1B\x5B\x46" |
|
71 |
|
|
72 |
/** |
|
73 |
* @brief String that defines the PGUP key as specified by VT100. |
|
74 |
*/ |
|
75 |
#define KEYSTRING_PAGEUP "\x1B\x5B\x35\x7E" |
|
76 |
|
|
77 |
/** |
|
78 |
* @brief String that defines the PGUP key as specified by VT100. |
|
79 |
*/ |
|
80 |
#define KEYSTRING_PAGEDOWN "\x1B\x5B\x36\x7E" |
|
81 |
|
|
82 |
/** |
|
83 |
* @brief String that defines the 'arrow down' key as specified by VT100. |
|
84 |
*/ |
|
85 |
#define KEYSTRING_ARROWUP "\x1B\x5B\x41" |
|
86 |
|
|
87 |
/** |
|
88 |
* @brief String that defines the 'arrow up' key as specified by VT100. |
|
89 |
*/ |
|
90 |
#define KEYSTRING_ARROWDOWN "\x1B\x5B\x42" |
|
91 |
|
|
92 |
/** |
|
93 |
* @brief String that defines the 'arrow left' key as specified by VT100. |
|
94 |
*/ |
|
95 |
#define KEYSTRING_ARROWLEFT "\x1B\x5B\x44" |
|
96 |
|
|
97 |
/** |
|
98 |
* @brief String that defines the 'arrow right' key as specified by VT100. |
|
99 |
*/ |
|
100 |
#define KEYSTRING_ARROWRIGHT "\x1B\x5B\x43" |
|
101 |
|
|
102 |
/** |
|
103 |
* @brief String that defines the CRTL + 'arrow up' key combination as specified by VT100. |
|
104 |
*/ |
|
105 |
#define KEYSTRING_CTRL_ARROWUP "\x1B\x5B\x31\x3B\x35\x41" |
|
106 |
|
|
107 |
/** |
|
108 |
* @brief String that defines the CRTL + 'arrow down' key combination as specified by VT100. |
|
109 |
*/ |
|
110 |
#define KEYSTRING_CTRL_ARROWDOWN "\x1B\x5B\x31\x3B\x35\x42" |
|
111 |
|
|
112 |
/** |
|
113 |
* @brief String that defines the CRTL + 'arrow left' key combination as specified by VT100. |
|
114 |
*/ |
|
115 |
#define KEYSTRING_CTRL_ARROWLEFT "\x1B\x5B\x31\x3B\x35\x44" |
|
116 |
|
|
117 |
/** |
|
118 |
* @brief String that defines the CRTL + 'arrow right' key combination as specified by VT100. |
|
119 |
*/ |
|
120 |
#define KEYSTRING_CTRL_ARROWRIGHT "\x1B\x5B\x31\x3B\x35\x43" |
|
46 | 121 |
|
47 | 122 |
/******************************************************************************/ |
48 | 123 |
/* EXPORTED VARIABLES */ |
... | ... | |
97 | 172 |
KEY_UNKNOWN, /**< any/unknow key */ |
98 | 173 |
KEY_AMBIGUOUS, /**< key is ambiguous */ |
99 | 174 |
KEY_TAB, /**< tabulator key */ |
100 |
KEY_ESCAPE, /**< escape key */ |
|
101 | 175 |
KEY_BACKSPACE, /**< backspace key */ |
102 | 176 |
KEY_INSERT, /**< insert key */ |
103 | 177 |
KEY_DELETE, /**< delete key */ |
178 |
KEY_ESCAPE, /**< escape key */ |
|
104 | 179 |
KEY_HOME, /**< home key */ |
105 | 180 |
KEY_END, /**< end key */ |
106 |
KEY_PAGE_UP, /**< page up key */ |
|
107 |
KEY_PAGE_DOWN, /**< page down key */ |
|
108 |
KEY_ARROW_UP, /**< arrow up key */ |
|
109 |
KEY_ARROW_DOWN, /**< arrow down key */ |
|
110 |
KEY_ARROW_LEFT, /**< arrow left key */ |
|
111 |
KEY_ARROW_RIGHT, /**< arrow right key */ |
|
112 |
KEY_CTRL_ARROW_UP, /**< CTRL + arrow up key */ |
|
113 |
KEY_CTRL_ARROW_DOWN, /**< CTRL + arrow down key */ |
|
114 |
KEY_CTRL_ARROW_LEFT, /**< CTRL + arrow left key */ |
|
115 |
KEY_CTRL_ARROW_RIGHT, /**< CTRL + arrow right key */ |
|
181 |
KEY_PAGEUP, /**< page up key */ |
|
182 |
KEY_PAGEDOWN, /**< page down key */ |
|
183 |
KEY_ARROWUP, /**< arrow up key */ |
|
184 |
KEY_ARROWDOWN, /**< arrow down key */ |
|
185 |
KEY_ARROWLEFT, /**< arrow left key */ |
|
186 |
KEY_ARROWRIGHT, /**< arrow right key */ |
|
187 |
KEY_CTRL_ARROWUP, /**< CTRL + arrow up key */ |
|
188 |
KEY_CTRL_ARROWDOWN, /**< CTRL + arrow down key */ |
|
189 |
KEY_CTRL_ARROWLEFT, /**< CTRL + arrow left key */ |
|
190 |
KEY_CTRL_ARROWRIGHT, /**< CTRL + arrow right key */ |
|
191 |
KEY_CTRL_C, /**< CTRL + C key */ |
|
116 | 192 |
} special_key_t; |
117 | 193 |
|
118 | 194 |
/** |
... | ... | |
124 | 200 |
CHAR_MATCH_CASE = 2, /**< Characters do match with case. */ |
125 | 201 |
} charmatch_t; |
126 | 202 |
|
203 |
/** |
|
204 |
* @brief Enumerator to encode shell actions. |
|
205 |
*/ |
|
206 |
typedef enum aos_shellaction { |
|
207 |
ACTION_NONE, /**< No action at all. */ |
|
208 |
ACTION_READCHAR, /**< Read a printable character. */ |
|
209 |
ACTION_AUTOCOMPLETE, /**< Automatically comlete input by using available command. */ |
|
210 |
ACTION_SUGGEST, /**< Suggest matching available commands. */ |
|
211 |
ACTION_EXECUTE, /**< Execute input. */ |
|
212 |
ACTION_DELETEBACKWARD, /**< Delete a single character backwards. */ |
|
213 |
ACTION_DELETEFORWARD, /**< Delete a single character forwards. */ |
|
214 |
ACTION_CLEAR, /**< Clear the input. */ |
|
215 |
ACTION_RECALLPREVIOUS, /**< Recall the previous (older) entry in the history. */ |
|
216 |
ACTION_RECALLNEXT, /**< Recall the next (more recent) entry in the history. */ |
|
217 |
ACTION_RECALLOLDEST, /**< Recall the oldest entry in the history. */ |
|
218 |
ACTION_RECALLCURRENT, /**< Recall the current input. */ |
|
219 |
ACTION_CURSORLEFT, /**< Move cursor one character to the left. */ |
|
220 |
ACTION_CURSORRIGHT, /**< Move cursor one character to the right. */ |
|
221 |
ACTION_CURSORWORDLEFT, /**< Move cursor one word to the left. */ |
|
222 |
ACTION_CURSORWORDRIGHT, /**< Move cursor one word to the right. */ |
|
223 |
ACTION_CURSOR2END, /**< Move cursor to the very right. */ |
|
224 |
ACTION_CURSOR2START, /**< Move cursor to the very left. */ |
|
225 |
ACTION_RESET, /**< Reset the current input. */ |
|
226 |
ACTION_INSERTTOGGLE, /**< Toggle insertion mode. */ |
|
227 |
ACTION_ESCSTART, /**< Start an escape sequence (special keys). */ |
|
228 |
ACTION_PRINTUNKNOWNSEQUENCE, /**< Print an unknown escape sequence. */ |
|
229 |
} action_t; |
|
230 |
|
|
231 |
/** |
|
232 |
* @brief Struct that holds most important runtime data for the shell. |
|
233 |
* @details The structure is to be used by the shell thread main function as some kind of structured stack, which can be easily passed to other functions. |
|
234 |
*/ |
|
235 |
typedef struct runtimedata { |
|
236 |
/** |
|
237 |
* @brief Data related to the current input. |
|
238 |
*/ |
|
239 |
struct { |
|
240 |
/** |
|
241 |
* @brief Length of the input. |
|
242 |
*/ |
|
243 |
size_t length; |
|
244 |
|
|
245 |
/** |
|
246 |
* @brief Current position of the cursor in the input line. |
|
247 |
*/ |
|
248 |
size_t cursorpos; |
|
249 |
|
|
250 |
/** |
|
251 |
* @brief Buffer to store escape sequences, which describe special characters. |
|
252 |
*/ |
|
253 |
char escseq[AOS_SHELL_ESCSEQUENCE_LENGTH]; |
|
254 |
} input; |
|
255 |
|
|
256 |
/** |
|
257 |
* @brief Data related to the entry or history buffer. |
|
258 |
*/ |
|
259 |
struct { |
|
260 |
/** |
|
261 |
* @brief Current entry to be filled and executed. |
|
262 |
*/ |
|
263 |
size_t current; |
|
264 |
|
|
265 |
/** |
|
266 |
* @brief Selected entry in the 'history' as preview. |
|
267 |
* @details A value of 0 indicates, that the line is cleared as a preview. |
|
268 |
* A value of 1 indicates, that the current entry is selected. |
|
269 |
* A value of t>1 indicates, that the entry t-1 in the past is selected. |
|
270 |
* The value must never be greater than the number of entries available, of course. |
|
271 |
*/ |
|
272 |
size_t selected; |
|
273 |
|
|
274 |
/** |
|
275 |
* @brief Selected entry in the 'history' that has been edited by the user. |
|
276 |
* A value of 0 indicates, that there was no modification by the user yet (i.e. charcters, deletions or autofill). |
|
277 |
* A value of 1 indicates, that the current entry was edited. |
|
278 |
* A value of t>1 indicated, that a history entry was recalled and then edited. |
|
279 |
*/ |
|
280 |
size_t edited; |
|
281 |
} buffer; |
|
282 |
|
|
283 |
/** |
|
284 |
* @brief The last action executed by the shell. |
|
285 |
*/ |
|
286 |
action_t lastaction; |
|
287 |
} runtimedata_t; |
|
288 |
|
|
127 | 289 |
/******************************************************************************/ |
128 | 290 |
/* LOCAL VARIABLES */ |
129 | 291 |
/******************************************************************************/ |
... | ... | |
248 | 410 |
return MSG_OK; |
249 | 411 |
} |
250 | 412 |
|
413 |
/** |
|
414 |
* @brief Implementation of the BaseSequentialStream write() method. |
|
415 |
*/ |
|
251 | 416 |
static size_t _streamwrite(void *instance, const uint8_t *bp, size_t n) |
252 | 417 |
{ |
253 | 418 |
aosDbgCheck(instance != NULL); |
... | ... | |
267 | 432 |
return maxbytes; |
268 | 433 |
} |
269 | 434 |
|
435 |
/** |
|
436 |
* @brief Implementation of the BaseSequentialStream read() method. |
|
437 |
*/ |
|
270 | 438 |
static size_t _stremread(void *instance, uint8_t *bp, size_t n) |
271 | 439 |
{ |
272 | 440 |
(void)instance; |
... | ... | |
276 | 444 |
return 0; |
277 | 445 |
} |
278 | 446 |
|
447 |
/** |
|
448 |
* @brief Implementation of the BaseSequentialStream put() method. |
|
449 |
*/ |
|
279 | 450 |
static msg_t _streamput(void *instance, uint8_t b) |
280 | 451 |
{ |
281 | 452 |
aosDbgCheck(instance != NULL); |
... | ... | |
294 | 465 |
return ret; |
295 | 466 |
} |
296 | 467 |
|
468 |
/** |
|
469 |
* @brief Implementation of the BaseSequentialStream get() method. |
|
470 |
*/ |
|
297 | 471 |
static msg_t _streamget(void *instance) |
298 | 472 |
{ |
299 | 473 |
(void)instance; |
... | ... | |
302 | 476 |
} |
303 | 477 |
|
304 | 478 |
/** |
479 |
* @brief Retreive a pointer to the string buffer of a specified entry in the input buffer. |
|
480 |
* |
|
481 |
* @param[in] shell Pointer to a shell object. |
|
482 |
* @param[in] entry Entry to be retrieved. |
|
483 |
* |
|
484 |
* @return Pointer to the entry in the input buffer. |
|
485 |
*/ |
|
486 |
static inline char* _getAbsoluteEntry(const aos_shell_t* shell, size_t entry) |
|
487 |
{ |
|
488 |
aosDbgCheck(shell != NULL); |
|
489 |
aosDbgCheck(entry < shell->input.nentries); |
|
490 |
|
|
491 |
return &(shell->input.buffer[entry * shell->input.linewidth * sizeof(char)]); |
|
492 |
} |
|
493 |
|
|
494 |
/** |
|
495 |
* @brief Calculate absolute entry from history offset. |
|
496 |
* |
|
497 |
* @param[in] shell Pointer to a shell object. |
|
498 |
* @param[in] rdata Pointer to a runtime data object. |
|
499 |
* @param[in] offset Relative offset of the entry to be retreived. |
|
500 |
* |
|
501 |
* @return Absolute index of the historic entry. |
|
502 |
*/ |
|
503 |
static inline size_t _historyOffset2EntryIndex(const aos_shell_t* shell, const runtimedata_t* rdata, size_t offset) |
|
504 |
{ |
|
505 |
aosDbgCheck(shell != NULL); |
|
506 |
aosDbgCheck(rdata != NULL); |
|
507 |
aosDbgCheck(offset < shell->input.nentries); |
|
508 |
|
|
509 |
return ((shell->input.nentries + rdata->buffer.current - offset) % shell->input.nentries); |
|
510 |
} |
|
511 |
|
|
512 |
/** |
|
513 |
* @brief Retreive a pointer to the string buffer of a historic entry in the input buffer. |
|
514 |
* |
|
515 |
* @param[in] shell Pointer to a shell object. |
|
516 |
* @param[in] rdata Pointer to a runtime data object. |
|
517 |
* @param[in] offset Relative offset of the entry to be retreived. |
|
518 |
* |
|
519 |
* @return Pointer to the entry in the input buffer. |
|
520 |
*/ |
|
521 |
static inline char* _getRelativeEntry(const aos_shell_t* shell, const runtimedata_t* rdata, size_t offset) |
|
522 |
{ |
|
523 |
aosDbgCheck(shell != NULL); |
|
524 |
aosDbgCheck(rdata != NULL); |
|
525 |
aosDbgCheck(offset < shell->input.nentries); |
|
526 |
|
|
527 |
return _getAbsoluteEntry(shell, _historyOffset2EntryIndex(shell, rdata, offset)); |
|
528 |
} |
|
529 |
|
|
530 |
/** |
|
531 |
* @brief Retreive a pointer to the current entry string in the input buffer. |
|
532 |
* |
|
533 |
* @param[in] shell Pointer to a shell object. |
|
534 |
* @param[in] rdata Pointer to a runtime data object. |
|
535 |
* |
|
536 |
* @return Pointer to the string of the current entry in the input buffer. |
|
537 |
*/ |
|
538 |
static inline char* _getCurrentEntry(const aos_shell_t* shell, const runtimedata_t* rdata) |
|
539 |
{ |
|
540 |
aosDbgCheck(shell != NULL); |
|
541 |
aosDbgCheck(rdata != NULL); |
|
542 |
|
|
543 |
return _getAbsoluteEntry(shell, rdata->buffer.current); |
|
544 |
} |
|
545 |
|
|
546 |
/** |
|
547 |
* @brief Retreive a pointer to the currently selected entry. |
|
548 |
* |
|
549 |
* @param[in] shell Pointer to a shell object. |
|
550 |
* @param[in] rdata Pointer to a runtime data object. |
|
551 |
* |
|
552 |
* @return Pointer to the currently selected entry or NULL if no entry is selected (cleared preview). |
|
553 |
*/ |
|
554 |
static inline char* _getSelectedEntry(const aos_shell_t* shell, const runtimedata_t* rdata) |
|
555 |
{ |
|
556 |
aosDbgCheck(shell != NULL); |
|
557 |
aosDbgCheck(rdata != NULL); |
|
558 |
|
|
559 |
if (rdata->buffer.selected > 0) { |
|
560 |
return _getRelativeEntry(shell, rdata, rdata->buffer.selected - 1); |
|
561 |
} else { |
|
562 |
return NULL; |
|
563 |
} |
|
564 |
} |
|
565 |
|
|
566 |
/** |
|
567 |
* @brief Retreive the currently visualized entry. |
|
568 |
* |
|
569 |
* @param[in] shell Pointer to a shell object. |
|
570 |
* @param[in] rdata Pointer to a runtime data object. |
|
571 |
* |
|
572 |
* @return Pointer to the currently visualized entry or NULL if the input has been cleared (cleared preview). |
|
573 |
*/ |
|
574 |
static inline char* _getVisualisedEntry(const aos_shell_t* shell, const runtimedata_t* rdata) |
|
575 |
{ |
|
576 |
aosDbgCheck(shell != NULL); |
|
577 |
aosDbgCheck(rdata != NULL); |
|
578 |
|
|
579 |
if (rdata->buffer.selected == 0) { |
|
580 |
// cleared preview, nothing visualized |
|
581 |
return NULL; |
|
582 |
} else { |
|
583 |
if (rdata->buffer.selected == 1 || rdata->buffer.selected == rdata->buffer.edited) { |
|
584 |
// the current or a modified entry is selected |
|
585 |
return _getCurrentEntry(shell, rdata); |
|
586 |
} else { |
|
587 |
// a historic, unmodified entry is selected |
|
588 |
return _getRelativeEntry(shell, rdata, rdata->buffer.selected - 1); |
|
589 |
} |
|
590 |
} |
|
591 |
} |
|
592 |
|
|
593 |
/** |
|
305 | 594 |
* @brief Print the shell prompt |
306 | 595 |
* @details Depending on the configuration flags, the system uptime is printed before the prompt string. |
307 | 596 |
* |
... | ... | |
371 | 660 |
static special_key_t _interpreteEscapeSequence(const char seq[]) |
372 | 661 |
{ |
373 | 662 |
// local variables |
374 |
char str[AOS_SHELL_ESCSEQUENCE_LENGTH]; |
|
375 | 663 |
unsigned long strl = 0; |
376 | 664 |
const unsigned long seql = strlen(seq); |
377 | 665 |
bool ambiguous = false; |
378 | 666 |
|
379 | 667 |
// TAB |
380 |
/* not supported yet; use "\x09" instead */
|
|
668 |
/* not supported yet; use '\x09' instead */
|
|
381 | 669 |
|
382 | 670 |
// BACKSPACE |
383 |
/* not supported yet; use "\x08" instead */
|
|
671 |
/* not supported yet; use '\x08' instead */
|
|
384 | 672 |
|
385 | 673 |
// ESCAPE |
386 |
strncpy(str, "\x1B", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
387 |
strl = strlen(str); |
|
388 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
389 |
return KEY_ESCAPE; |
|
390 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
391 |
ambiguous = true; |
|
392 |
} |
|
674 |
/* not supported yes; use '\x1B' instead */ |
|
675 |
|
|
676 |
// CTRL + C |
|
677 |
/* not defined yet; use '\x03' instead */ |
|
393 | 678 |
|
394 | 679 |
// INSERT |
395 |
strncpy(str, "\x1B\x5B\x32\x7E", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
396 |
strl = strlen(str); |
|
397 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
398 |
return KEY_INSERT; |
|
399 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
400 |
ambiguous = true; |
|
680 |
if (strncmp(seq, KEYSTRING_INSERT, seql) == 0) { |
|
681 |
strl = strlen(KEYSTRING_INSERT); |
|
682 |
if (seql == strl) { |
|
683 |
return KEY_INSERT; |
|
684 |
} else if (seql < strl) { |
|
685 |
ambiguous = true; |
|
686 |
} |
|
401 | 687 |
} |
402 | 688 |
|
403 | 689 |
// DELETE |
404 |
strncpy(str, "\x1B\x5B\x33\x7E", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
405 |
strl = strlen(str); |
|
406 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
407 |
return KEY_DELETE; |
|
408 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
409 |
ambiguous = true; |
|
690 |
if (strncmp(seq, KEYSTRING_DELETE, seql) == 0) { |
|
691 |
strl = strlen(KEYSTRING_DELETE); |
|
692 |
if (seql == strl) { |
|
693 |
return KEY_DELETE; |
|
694 |
} else if (seql < strl) { |
|
695 |
ambiguous = true; |
|
696 |
} |
|
410 | 697 |
} |
411 | 698 |
|
412 | 699 |
// HOME |
413 |
strncpy(str, "\x1B\x5B\x48", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
414 |
strl = strlen(str); |
|
415 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
416 |
return KEY_HOME; |
|
417 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
418 |
ambiguous = true; |
|
700 |
if (strncmp(seq, KEYSTRING_HOME, seql) == 0) { |
|
701 |
strl = strlen(KEYSTRING_HOME); |
|
702 |
if (seql == strl) { |
|
703 |
return KEY_HOME; |
|
704 |
} else if (seql < strl) { |
|
705 |
ambiguous = true; |
|
706 |
} |
|
419 | 707 |
} |
420 | 708 |
|
421 | 709 |
// END |
422 |
strncpy(str, "\x1B\x5B\x46", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
423 |
strl = strlen(str); |
|
424 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
425 |
return KEY_END; |
|
426 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
427 |
ambiguous = true; |
|
710 |
if (strncmp(seq, KEYSTRING_END, seql) == 0) { |
|
711 |
strl = strlen(KEYSTRING_END); |
|
712 |
if (seql == strl) { |
|
713 |
return KEY_END; |
|
714 |
} else if (seql < strl) { |
|
715 |
ambiguous = true; |
|
716 |
} |
|
428 | 717 |
} |
429 | 718 |
|
430 | 719 |
// PAGE UP |
431 |
strncpy(str, "\x1B\x5B\x35\x7E", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
432 |
strl = strlen(str); |
|
433 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
434 |
return KEY_PAGE_UP; |
|
435 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
436 |
ambiguous = true; |
|
720 |
if (strncmp(seq, KEYSTRING_PAGEUP, seql) == 0) { |
|
721 |
strl = strlen(KEYSTRING_PAGEUP); |
|
722 |
if (seql == strl) { |
|
723 |
return KEY_PAGEUP; |
|
724 |
} else if (seql < strl) { |
|
725 |
ambiguous = true; |
|
726 |
} |
|
437 | 727 |
} |
438 | 728 |
|
439 | 729 |
// PAGE DOWN |
440 |
strncpy(str, "\x1B\x5B\x36\x7E", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
441 |
strl = strlen(str); |
|
442 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
443 |
return KEY_PAGE_DOWN; |
|
444 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
445 |
ambiguous = true; |
|
730 |
if (strncmp(seq, KEYSTRING_PAGEDOWN, seql) == 0) { |
|
731 |
strl = strlen(KEYSTRING_PAGEDOWN); |
|
732 |
if (seql == strl) { |
|
733 |
return KEY_PAGEDOWN; |
|
734 |
} else if (seql < strl) { |
|
735 |
ambiguous = true; |
|
736 |
} |
|
446 | 737 |
} |
447 | 738 |
|
448 | 739 |
// ARROW UP |
449 |
strncpy(str, "\x1B\x5B\x41", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
450 |
strl = strlen(str); |
|
451 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
452 |
return KEY_ARROW_UP; |
|
453 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
454 |
ambiguous = true; |
|
740 |
if (strncmp(seq, KEYSTRING_ARROWUP, seql) == 0) { |
|
741 |
strl = strlen(KEYSTRING_ARROWUP); |
|
742 |
if (seql == strl) { |
|
743 |
return KEY_ARROWUP; |
|
744 |
} else if (seql < strl) { |
|
745 |
ambiguous = true; |
|
746 |
} |
|
455 | 747 |
} |
456 | 748 |
|
457 | 749 |
// ARROW DOWN |
458 |
strncpy(str, "\x1B\x5B\x42", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
459 |
strl = strlen(str); |
|
460 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
461 |
return KEY_ARROW_DOWN; |
|
462 |
} else if (seql < strl && strncmp(seq, str, seql) == 0) { |
|
463 |
ambiguous = true; |
|
750 |
if (strncmp(seq, KEYSTRING_ARROWDOWN, seql) == 0) { |
|
751 |
strl = strlen(KEYSTRING_ARROWDOWN); |
|
752 |
if (seql == strl) { |
|
753 |
return KEY_ARROWDOWN; |
|
754 |
} else if (seql < strl) { |
|
755 |
ambiguous = true; |
|
756 |
} |
|
464 | 757 |
} |
465 | 758 |
|
466 | 759 |
// ARROW LEFT |
467 |
strncpy(str, "\x1B\x5B\x44", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
468 |
strl = strlen(str); |
|
469 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
470 |
return KEY_ARROW_LEFT; |
|
471 |
} else if (seql < strl && strncmp(seq, str, seql) == 0) { |
|
472 |
ambiguous = true; |
|
760 |
if (strncmp(seq, KEYSTRING_ARROWLEFT, seql) == 0) { |
|
761 |
strl = strlen(KEYSTRING_ARROWLEFT); |
|
762 |
if (seql == strl) { |
|
763 |
return KEY_ARROWLEFT; |
|
764 |
} else if (seql < strl) { |
|
765 |
ambiguous = true; |
|
766 |
} |
|
473 | 767 |
} |
474 | 768 |
|
475 | 769 |
// ARROW RIGHT |
476 |
strncpy(str, "\x1B\x5B\x43", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
477 |
strl = strlen(str); |
|
478 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
479 |
return KEY_ARROW_RIGHT; |
|
480 |
} else if (seql < strl && strncmp(seq, str, seql) == 0) { |
|
481 |
ambiguous = true; |
|
770 |
if (strncmp(seq, KEYSTRING_ARROWRIGHT, seql) == 0) { |
|
771 |
strl = strlen(KEYSTRING_ARROWRIGHT); |
|
772 |
if (seql == strl) { |
|
773 |
return KEY_ARROWRIGHT; |
|
774 |
} else if (seql < strl) { |
|
775 |
ambiguous = true; |
|
776 |
} |
|
482 | 777 |
} |
483 | 778 |
|
484 | 779 |
// CTRL + ARROW UP |
485 |
strncpy(str, "\x1B\x5B\x31\x3B\x35\x41", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
486 |
strl = strlen(str); |
|
487 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
488 |
return KEY_CTRL_ARROW_UP; |
|
489 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
490 |
ambiguous = true; |
|
780 |
if (strncmp(seq, KEYSTRING_CTRL_ARROWUP, seql) == 0) { |
|
781 |
strl = strlen(KEYSTRING_CTRL_ARROWUP); |
|
782 |
if (seql == strl) { |
|
783 |
return KEY_CTRL_ARROWUP; |
|
784 |
} else if (seql < strl) { |
|
785 |
ambiguous = true; |
|
786 |
} |
|
491 | 787 |
} |
492 | 788 |
|
493 | 789 |
// CTRL + ARROW DOWN |
494 |
strncpy(str, "\x1B\x5B\x31\x3B\x35\x42", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
495 |
strl = strlen(str); |
|
496 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
497 |
return KEY_CTRL_ARROW_DOWN; |
|
498 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
499 |
ambiguous = true; |
|
790 |
if (strncmp(seq, KEYSTRING_CTRL_ARROWDOWN, seql) == 0) { |
|
791 |
strl = strlen(KEYSTRING_CTRL_ARROWDOWN); |
|
792 |
if (seql == strl) { |
|
793 |
return KEY_CTRL_ARROWDOWN; |
|
794 |
} else if (seql < strl) { |
|
795 |
ambiguous = true; |
|
796 |
} |
|
500 | 797 |
} |
501 | 798 |
|
502 | 799 |
// CTRL + ARROW LEFT |
503 |
strncpy(str, "\x1B\x5B\x31\x3B\x35\x44", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
504 |
strl = strlen(str); |
|
505 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
506 |
return KEY_CTRL_ARROW_LEFT; |
|
507 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
508 |
ambiguous = true; |
|
800 |
if (strncmp(seq, KEYSTRING_CTRL_ARROWLEFT, seql) == 0) { |
|
801 |
strl = strlen(KEYSTRING_CTRL_ARROWLEFT); |
|
802 |
if (seql == strl) { |
|
803 |
return KEY_CTRL_ARROWLEFT; |
|
804 |
} else if (seql < strl) { |
|
805 |
ambiguous = true; |
|
806 |
} |
|
509 | 807 |
} |
510 | 808 |
|
511 | 809 |
// CTRL + ARROW RIGHT |
512 |
strncpy(str, "\x1B\x5B\x31\x3B\x35\x43", AOS_SHELL_ESCSEQUENCE_LENGTH); |
|
513 |
strl = strlen(str); |
|
514 |
if (seql == strl && strncmp(seq, str, seql) == 0) { |
|
515 |
return KEY_CTRL_ARROW_RIGHT; |
|
516 |
} else if(seql < strl && strncmp(seq, str, seql) == 0) { |
|
517 |
ambiguous = true; |
|
810 |
if (strncmp(seq, KEYSTRING_CTRL_ARROWRIGHT, seql) == 0) { |
|
811 |
strl = strlen(KEYSTRING_CTRL_ARROWRIGHT); |
|
812 |
if (seql == strl) { |
|
813 |
return KEY_CTRL_ARROWRIGHT; |
|
814 |
} else if (seql < strl) { |
|
815 |
ambiguous = true; |
|
816 |
} |
|
518 | 817 |
} |
519 | 818 |
|
520 | 819 |
return ambiguous ? KEY_AMBIGUOUS : KEY_UNKNOWN; |
521 | 820 |
} |
522 | 821 |
|
523 | 822 |
/** |
524 |
* @brief Move the cursor in the terminal |
|
823 |
* @brief Move the cursor in the terminal.
|
|
525 | 824 |
* |
526 | 825 |
* @param[in] shell Pointer to the shell object. |
826 |
* @param[in] line Pointer to the current content of the line. |
|
527 | 827 |
* @param[in] from Starting position of the cursor. |
528 | 828 |
* @param[in] to Target position to move the cursor to. |
529 | 829 |
* |
530 | 830 |
* @return The number of positions moved. |
531 | 831 |
*/ |
532 |
static int _moveCursor(aos_shell_t* shell, const size_t from, const size_t to)
|
|
832 |
static int _moveCursor(aos_shell_t* shell, const char* line, size_t from, size_t to)
|
|
533 | 833 |
{ |
534 | 834 |
aosDbgCheck(shell != NULL); |
835 |
aosDbgCheck(line != NULL || from >= to); |
|
836 |
aosDbgCheck(from <= shell->input.linewidth); |
|
837 |
aosDbgCheck(to <= shell->input.linewidth); |
|
535 | 838 |
|
536 | 839 |
// local variables |
537 | 840 |
size_t pos = from; |
... | ... | |
544 | 847 |
|
545 | 848 |
// move cursor right by printing line content |
546 | 849 |
while (pos < to) { |
547 |
streamPut(&shell->stream, (uint8_t)shell->input.line[pos]);
|
|
850 |
streamPut(&shell->stream, (uint8_t)line[pos]); |
|
548 | 851 |
++pos; |
549 | 852 |
} |
550 | 853 |
|
... | ... | |
552 | 855 |
} |
553 | 856 |
|
554 | 857 |
/** |
555 |
* @brief Print content of the shell line
|
|
858 |
* @brief Print content of a given string to the shell output stream.
|
|
556 | 859 |
* |
557 | 860 |
* @param[in] shell Pointer to the shell object. |
861 |
* @param[in] line Pointer to the line to be printed. |
|
558 | 862 |
* @param[in] from First position to start printing from. |
559 | 863 |
* @param[in] to Position after the last character to print. |
560 | 864 |
* |
561 | 865 |
* @return Number of characters printed. |
562 | 866 |
*/ |
563 |
static inline size_t _printLine(aos_shell_t* shell, const size_t from, const size_t to)
|
|
867 |
static size_t _printString(aos_shell_t* shell, const char* line, size_t from, size_t to)
|
|
564 | 868 |
{ |
565 | 869 |
aosDbgCheck(shell != NULL); |
870 |
aosDbgCheck(line != NULL || from >= to); |
|
871 |
aosDbgCheck(from < shell->input.linewidth); |
|
872 |
aosDbgCheck(to <= shell->input.linewidth); |
|
566 | 873 |
|
567 | 874 |
// local variables |
568 | 875 |
size_t cnt; |
569 | 876 |
|
570 | 877 |
for (cnt = 0; from + cnt < to; ++cnt) { |
571 |
streamPut(&shell->stream, (uint8_t)shell->input.line[from + cnt]);
|
|
878 |
streamPut(&shell->stream, (uint8_t)line[from + cnt]); |
|
572 | 879 |
} |
573 | 880 |
|
574 | 881 |
return cnt; |
575 | 882 |
} |
576 | 883 |
|
577 |
static int _readChar(aos_shell_t* shell, const char c) { |
|
884 |
/** |
|
885 |
* @brief Print a single character to the input buffer and to the output stream. |
|
886 |
* |
|
887 |
* @param[in] shell Pointer to the shell object. |
|
888 |
* @param[in] rdata Pointer to the runtim data object. |
|
889 |
* @param[in] c Character to print. |
|
890 |
* |
|
891 |
* @return Number of successfully handled characters. |
|
892 |
* The return value can be interpreted as boolean (1 = sucess; 0 = error). |
|
893 |
*/ |
|
894 |
static int _printChar(aos_shell_t* shell, runtimedata_t* rdata, char c) |
|
895 |
{ |
|
578 | 896 |
aosDbgCheck(shell != NULL); |
897 |
aosDbgCheck(rdata != NULL); |
|
579 | 898 |
|
580 | 899 |
// check whether input line is already full |
581 |
if (shell->inputdata.lineend + 1 >= shell->input.size) {
|
|
900 |
if (rdata->input.length + 1 >= shell->input.linewidth) {
|
|
582 | 901 |
return 0; |
583 |
} else { |
|
584 |
// clear old line content on first input |
|
585 |
if (shell->inputdata.noinput) { |
|
586 |
memset(shell->input.line, '\0', shell->input.size); |
|
587 |
shell->inputdata.noinput = false; |
|
588 |
} |
|
589 |
// overwrite content |
|
590 |
if (shell->config & AOS_SHELL_CONFIG_INPUT_OVERWRITE) { |
|
591 |
shell->input.line[shell->inputdata.cursorpos] = c; |
|
592 |
++shell->inputdata.cursorpos; |
|
593 |
shell->inputdata.lineend = (shell->inputdata.cursorpos > shell->inputdata.lineend) ? shell->inputdata.cursorpos : shell->inputdata.lineend; |
|
594 |
streamPut(&shell->stream, (uint8_t)c); |
|
595 |
} |
|
596 |
// insert character |
|
597 |
else { |
|
598 |
memmove(&(shell->input.line[shell->inputdata.cursorpos+1]), &(shell->input.line[shell->inputdata.cursorpos]), shell->inputdata.lineend - shell->inputdata.cursorpos); |
|
599 |
shell->input.line[shell->inputdata.cursorpos] = c; |
|
600 |
++shell->inputdata.lineend; |
|
601 |
_printLine(shell, shell->inputdata.cursorpos, shell->inputdata.lineend); |
|
602 |
++shell->inputdata.cursorpos; |
|
603 |
_moveCursor(shell, shell->inputdata.lineend, shell->inputdata.cursorpos); |
|
604 |
} |
|
902 |
} |
|
903 |
|
|
904 |
// retreive entry in the input buffer |
|
905 |
char* line = _getCurrentEntry(shell, rdata); |
|
906 |
|
|
907 |
// overwrite content |
|
908 |
if (shell->config & AOS_SHELL_CONFIG_INPUT_OVERWRITE) { |
|
909 |
line[rdata->input.cursorpos] = c; |
|
910 |
++rdata->input.cursorpos; |
|
911 |
rdata->input.length = (rdata->input.cursorpos > rdata->input.length) ? rdata->input.cursorpos : rdata->input.length; |
|
912 |
streamPut(&shell->stream, (uint8_t)c); |
|
913 |
return 1; |
|
914 |
} |
|
915 |
// insert character |
|
916 |
else { |
|
917 |
memmove(&line[rdata->input.cursorpos + 1], &line[rdata->input.cursorpos], rdata->input.length - rdata->input.cursorpos); |
|
918 |
line[rdata->input.cursorpos] = c; |
|
919 |
++rdata->input.length; |
|
920 |
_printString(shell, line, rdata->input.cursorpos, rdata->input.length); |
|
921 |
++rdata->input.cursorpos; |
|
922 |
_moveCursor(shell, line, rdata->input.length, rdata->input.cursorpos); |
|
605 | 923 |
return 1; |
606 | 924 |
} |
607 | 925 |
} |
608 | 926 |
|
609 | 927 |
/** |
928 |
* @brief Overwrite the current output with a given line. |
|
929 |
* @details If the current output is longer than the string, the additional characters are cleared. |
|
930 |
* |
|
931 |
* @param[in] shell Pointer to a shell object. |
|
932 |
* @param[in] rdata Pointer to a runtime data object. |
|
933 |
* @param[in] line The line to be printed. |
|
934 |
*/ |
|
935 |
static void _overwriteOutput(aos_shell_t* shell, runtimedata_t* rdata, const char* line) |
|
936 |
{ |
|
937 |
aosDbgCheck(shell != NULL); |
|
938 |
aosDbgCheck(rdata != NULL); |
|
939 |
aosDbgCheck(line != NULL); |
|
940 |
|
|
941 |
// local variables |
|
942 |
const size_t oldlength = rdata->input.length; |
|
943 |
|
|
944 |
// print line (overwrite current output) |
|
945 |
_moveCursor(shell, line, rdata->input.cursorpos, 0); |
|
946 |
rdata->input.length = strlen(line); |
|
947 |
_printString(shell, line, 0, rdata->input.length); |
|
948 |
|
|
949 |
// clear any remaining symbols |
|
950 |
if (oldlength > rdata->input.length) { |
|
951 |
for (rdata->input.cursorpos = rdata->input.length; rdata->input.cursorpos < oldlength; ++rdata->input.cursorpos) { |
|
952 |
streamPut(&shell->stream, ' '); |
|
953 |
} |
|
954 |
_moveCursor(shell, line, oldlength, rdata->input.length); |
|
955 |
} |
|
956 |
|
|
957 |
rdata->input.cursorpos = rdata->input.length; |
|
958 |
|
|
959 |
return; |
|
960 |
} |
|
961 |
|
|
962 |
/** |
|
610 | 963 |
* @brief Compare two characters. |
611 | 964 |
* |
612 | 965 |
* @param[in] lhs First character to compare. |
... | ... | |
642 | 995 |
* |
643 | 996 |
* @return The customly encoded character. |
644 | 997 |
*/ |
645 |
static inline char _mapAscii2Custom(const char c)
|
|
998 |
static inline char _mapAscii2Custom(char c) |
|
646 | 999 |
{ |
647 | 1000 |
if (c >= 'A' && c <= 'Z') { |
648 | 1001 |
return ((c - 'A') * 2) + 'A' + 1; |
... | ... | |
712 | 1065 |
} |
713 | 1066 |
|
714 | 1067 |
/** |
1068 |
* @brief Alters all intermediate NUL bytes in a string to spaces. |
|
1069 |
* |
|
1070 |
* @param[in] string The string to be handled. |
|
1071 |
* @param[in] length Length of the string. |
|
1072 |
* |
|
1073 |
* @return Detected Length of the actual content of the string. |
|
1074 |
*/ |
|
1075 |
static size_t _restoreWhitespace(char* string, size_t length) |
|
1076 |
{ |
|
1077 |
aosDbgCheck(string != NULL || length == 0); |
|
1078 |
|
|
1079 |
// local variables |
|
1080 |
size_t c = length; |
|
1081 |
|
|
1082 |
// seach for first non-NUL byte from the back |
|
1083 |
while (c > 0) { |
|
1084 |
--c; |
|
1085 |
if (string[c] != '\0') { |
|
1086 |
// store the detected length of the content |
|
1087 |
length = ++c; |
|
1088 |
break; |
|
1089 |
} |
|
1090 |
} |
|
1091 |
|
|
1092 |
// iterate further and replace all encountered NUL bytes by spaces |
|
1093 |
while (c > 0) { |
|
1094 |
--c; |
|
1095 |
if (string[c] == '\0') { |
|
1096 |
string[c] = ' '; |
|
1097 |
} |
|
1098 |
} |
|
1099 |
|
|
1100 |
return length; |
|
1101 |
} |
|
1102 |
|
|
1103 |
/** |
|
1104 |
* @brief Performs required actions before an imminent modiifcation (character input, deletion or autofill). |
|
1105 |
* @details This functions checks the current status and clears or copies entries in the input buffer as required. |
|
1106 |
* Status information (runtime data) is altered accordingly as well. |
|
1107 |
* |
|
1108 |
* @param[in] shell Pointer to a shell object. |
|
1109 |
* @param[in] rdata Pointer to a runtime data object. |
|
1110 |
* |
|
1111 |
* @return Pointer to the current entry in the input buffer. |
|
1112 |
*/ |
|
1113 |
static char* _prepare4Modification(aos_shell_t* shell, runtimedata_t* rdata) |
|
1114 |
{ |
|
1115 |
aosDbgCheck(shell != NULL); |
|
1116 |
aosDbgCheck(rdata != NULL); |
|
1117 |
|
|
1118 |
char* line = _getCurrentEntry(shell, rdata); |
|
1119 |
|
|
1120 |
// cleared preview |
|
1121 |
if (rdata->buffer.selected == 0) { |
|
1122 |
// reset the current entry if required |
|
1123 |
if (rdata->buffer.edited != 0) { |
|
1124 |
memset(line, '\0', shell->input.linewidth * sizeof(char)); |
|
1125 |
} |
|
1126 |
// set the current entry as the selected one and mark it as edited |
|
1127 |
rdata->buffer.selected = 1; |
|
1128 |
rdata->buffer.edited = 1; |
|
1129 |
} |
|
1130 |
// current entry |
|
1131 |
else if (rdata->buffer.selected == 1) { |
|
1132 |
// mark current entry as edited |
|
1133 |
rdata->buffer.edited = 1; |
|
1134 |
} |
|
1135 |
// preview of historic entry |
|
1136 |
else if (rdata->buffer.selected > 1) { |
|
1137 |
// copy the selected entry before modification if required |
|
1138 |
if (rdata->buffer.selected!= rdata->buffer.edited) { |
|
1139 |
memcpy(line, _getSelectedEntry(shell, rdata), shell->input.linewidth * sizeof(char)); |
|
1140 |
} |
|
1141 |
// mark the selected entry as edited |
|
1142 |
rdata->buffer.edited = rdata->buffer.selected; |
|
1143 |
} |
|
1144 |
|
|
1145 |
return line; |
|
1146 |
} |
|
1147 |
|
|
1148 |
/** |
|
715 | 1149 |
* @brief Read input from a channel as long as there is data available. |
716 | 1150 |
* |
717 |
* @param[in] shell Pointer to the shell object. |
|
718 |
* @param[in] channel The channel to read from.
|
|
719 |
* @param[out] exec Optional pointer to a flag, which indicates, whether a command shall be executed.
|
|
1151 |
* @param[in] shell Pointer to the shell object.
|
|
1152 |
* @param[in,out] rdata Pointer to a runtime data object.
|
|
1153 |
* @param[in] channel The channel to read from.
|
|
720 | 1154 |
* |
721 |
* @return Number of read characters.
|
|
1155 |
* @return Number of characters read.
|
|
722 | 1156 |
*/ |
723 |
static int _readChannel(aos_shell_t* shell, AosShellChannel* channel, bool* execute)
|
|
1157 |
static size_t _readChannel(aos_shell_t* shell, runtimedata_t* rdata, AosShellChannel* channel)
|
|
724 | 1158 |
{ |
725 | 1159 |
aosDbgCheck(shell != NULL); |
1160 |
aosDbgCheck(rdata != NULL); |
|
726 | 1161 |
aosDbgCheck(channel != NULL); |
727 | 1162 |
|
728 | 1163 |
// local variables |
729 |
aos_shellaction_t action = AOS_SHELL_ACTION_NONE;
|
|
1164 |
size_t bytes = 0;
|
|
730 | 1165 |
char c; |
731 | 1166 |
special_key_t key; |
732 |
int nchars = 0; |
|
733 |
bool exec = false; |
|
1167 |
action_t action; |
|
734 | 1168 |
|
735 | 1169 |
// read character by character from the channel |
736 | 1170 |
while (chnReadTimeout(channel, (uint8_t*)&c, 1, TIME_IMMEDIATE)) { |
737 |
key = KEY_UNKNOWN; |
|
1171 |
// increment byte counter |
|
1172 |
++bytes; |
|
738 | 1173 |
|
739 |
// drop any input after an execution request was detected |
|
740 |
if (exec) {
|
|
1174 |
// drop any further input after an execution request was detected
|
|
1175 |
if (rdata->lastaction == ACTION_EXECUTE && bytes > 1) {
|
|
741 | 1176 |
continue; |
742 | 1177 |
} |
743 | 1178 |
|
744 |
// incremet character counter |
|
745 |
++nchars; |
|
746 |
|
|
747 |
// parse escape sequence |
|
748 |
if (strlen(shell->inputdata.escseq) > 0) { |
|
749 |
shell->inputdata.escseq[strlen(shell->inputdata.escseq)] = c; |
|
750 |
key = _interpreteEscapeSequence(shell->inputdata.escseq); |
|
751 |
switch (key) { |
|
752 |
case KEY_AMBIGUOUS: |
|
753 |
// read next byte to resolve ambiguity |
|
754 |
continue; |
|
755 |
case KEY_UNKNOWN: |
|
756 |
// do nothing here, but handle the unknown sequence below |
|
757 |
break; |
|
758 |
default: |
|
759 |
// reset the sequence variable and buffer |
|
760 |
memset(shell->inputdata.escseq, '\0', sizeof(shell->inputdata.escseq)*sizeof(shell->inputdata.escseq[0])); |
|
761 |
break; |
|
1179 |
// try to interprete escape sequence |
|
1180 |
{ |
|
1181 |
// set default |
|
1182 |
key = KEY_UNKNOWN; |
|
1183 |
// only interprete, if there is an escape sequence at all |
|
1184 |
const size_t escl = strlen(rdata->input.escseq); |
|
1185 |
if (escl > 0) { |
|
1186 |
// append and 'consume' character |
|
1187 |
rdata->input.escseq[escl] = c; |
|
1188 |
c = '\0'; |
|
1189 |
// try to interprete sequence |
|
1190 |
key = _interpreteEscapeSequence(rdata->input.escseq); |
|
1191 |
switch (key) { |
|
1192 |
// ambiguous key due to incomplete sequence |
|
1193 |
case KEY_AMBIGUOUS: |
|
1194 |
// read next byte to resolve ambiguity |
|
1195 |
continue; |
|
1196 |
// an unknown sequence has been encountered |
|
1197 |
case KEY_UNKNOWN: |
|
1198 |
// increment number of inputs but handle this unknown sequence below |
|
1199 |
break; |
|
1200 |
// a key was identified successfully |
|
1201 |
default: |
|
1202 |
// reset the sequence buffer |
|
1203 |
memset(rdata->input.escseq, '\0', AOS_SHELL_ESCSEQUENCE_LENGTH * sizeof(char)); |
|
1204 |
break; |
|
1205 |
} |
|
762 | 1206 |
} |
763 | 1207 |
} |
764 | 1208 |
|
765 |
/* interprete keys or character */ |
|
766 |
{ |
|
767 |
// default |
|
768 |
action = AOS_SHELL_ACTION_NONE; |
|
1209 |
/* |
|
1210 |
* Derive action to be executed from keypress. |
|
1211 |
* This step handles all sanity checks, so any required prerequisites for the selected action are fulfilled. |
|
1212 |
*/ |
|
1213 |
// set default |
|
1214 |
action = ACTION_NONE; |
|
1215 |
// if there is no escape sequence pending |
|
1216 |
if (rdata->input.escseq[0] == '\0') { |
|
769 | 1217 |
|
770 | 1218 |
// printable character |
771 |
if (key == KEY_UNKNOWN && strlen(shell->inputdata.escseq) == 0 && c >= '\x20' && c <= '\x7E') {
|
|
772 |
action = AOS_SHELL_ACTION_READCHAR;
|
|
1219 |
if (c >= '\x20' && c <= '\x7E') { |
|
1220 |
action = ACTION_READCHAR; |
|
773 | 1221 |
} |
774 | 1222 |
|
775 | 1223 |
// tab key or character |
776 |
else if (key == KEY_TAB || c == '\x09') {
|
|
1224 |
else if (c == '\x09' || key == KEY_TAB) {
|
|
777 | 1225 |
/* |
778 | 1226 |
* pressing tab once applies auto fill |
779 |
* pressing tab a second time prints suggestions |
|
1227 |
* pressing tab a second time (or more) prints suggestions
|
|
780 | 1228 |
*/ |
781 |
if (shell->inputdata.lastaction == AOS_SHELL_ACTION_AUTOFILL || shell->inputdata.lastaction == AOS_SHELL_ACTION_SUGGEST) {
|
|
782 |
action = AOS_SHELL_ACTION_SUGGEST;
|
|
1229 |
if (rdata->lastaction == ACTION_AUTOCOMPLETE || rdata->lastaction == ACTION_SUGGEST) {
|
|
1230 |
action = ACTION_SUGGEST; |
|
783 | 1231 |
} else { |
784 |
action = AOS_SHELL_ACTION_AUTOFILL;
|
|
1232 |
action = ACTION_AUTOCOMPLETE;
|
|
785 | 1233 |
} |
786 | 1234 |
} |
787 | 1235 |
|
788 |
// INS key |
|
789 |
else if (key == KEY_INSERT) { |
|
790 |
action = AOS_SHELL_ACTION_INSERTTOGGLE; |
|
791 |
} |
|
792 |
|
|
793 |
// DEL key or character |
|
794 |
else if (key == KEY_DELETE || c == '\x7F') { |
|
795 |
// ignore if cursor is at very right |
|
796 |
if (shell->inputdata.cursorpos < shell->inputdata.lineend) { |
|
797 |
action = AOS_SHELL_ACTION_DELETEFORWARD; |
|
798 |
} |
|
1236 |
// carriage return ('\r') or line feed ('\n') character |
|
1237 |
else if (c == '\x0D' || c == '\x0A') { |
|
1238 |
action = ACTION_EXECUTE; |
|
799 | 1239 |
} |
800 | 1240 |
|
801 | 1241 |
// backspace key or character |
802 |
else if (key == KEY_BACKSPACE || c == '\x08') {
|
|
1242 |
else if (c == '\x08' || key == KEY_BACKSPACE) {
|
|
803 | 1243 |
// ignore if cursor is at very left |
804 |
if (shell->inputdata.cursorpos > 0) {
|
|
805 |
action = AOS_SHELL_ACTION_DELETEBACKWARD;
|
|
1244 |
if (rdata->input.cursorpos > 0) {
|
|
1245 |
action = ACTION_DELETEBACKWARD; |
|
806 | 1246 |
} |
807 | 1247 |
} |
808 | 1248 |
|
809 |
// 'page up', 'arrow up', or key or CTRL + 'arrow up' key combination
|
|
810 |
else if (key == KEY_PAGE_UP || key == KEY_ARROW_UP || key == KEY_CTRL_ARROW_UP) {
|
|
811 |
// ignore if there was some input
|
|
812 |
if (shell->inputdata.noinput) {
|
|
813 |
action = AOS_SHELL_ACTION_RECALLLAST;
|
|
1249 |
// DEL key or character
|
|
1250 |
else if (c == '\x7F' || key == KEY_DELETE) {
|
|
1251 |
// ignore if cursor is at very right
|
|
1252 |
if (rdata->input.cursorpos < rdata->input.length) {
|
|
1253 |
action = ACTION_DELETEFORWARD;
|
|
814 | 1254 |
} |
815 | 1255 |
} |
816 | 1256 |
|
817 |
// 'page down' key, 'arrow down' key, 'end of test' character or 'end of transmission' character, or CTRL + 'arrow down' key combination |
|
818 |
else if (key == KEY_PAGE_DOWN || key == KEY_ARROW_DOWN || c == '\x03' || c == '\x03' || key == KEY_CTRL_ARROW_DOWN) { |
|
819 |
// ignore if line is empty |
|
820 |
if (shell->inputdata.lineend > 0) { |
|
821 |
action = AOS_SHELL_ACTION_CLEAR; |
|
1257 |
// 'arrow up' key |
|
1258 |
else if (key == KEY_ARROWUP) { |
|
1259 |
// recall previous input from history only if |
|
1260 |
// not the oldest entry is already selected and |
|
1261 |
// the previous entry has been set. |
|
1262 |
if (rdata->buffer.selected < shell->input.nentries && |
|
1263 |
(_getRelativeEntry(shell, rdata, rdata->buffer.selected))[0] != INBUF_INIT_CHAR) { |
|
1264 |
action = ACTION_RECALLPREVIOUS; |
|
822 | 1265 |
} |
823 | 1266 |
} |
824 | 1267 |
|
825 |
// 'home' key |
|
826 |
else if (key == KEY_HOME) { |
|
827 |
// ignore if cursor is very left |
|
828 |
if (shell->inputdata.cursorpos > 0) { |
|
829 |
action = AOS_SHELL_ACTION_CURSOR2START; |
|
1268 |
// 'arrow down' key |
|
1269 |
else if (key == KEY_ARROWDOWN) { |
|
1270 |
// clear the line if |
|
1271 |
// no historic entry is selected or |
|
1272 |
// the most recent entry is selected, but the current one is occupied by a moodfied version of a historic entry |
|
1273 |
if ((rdata->buffer.selected == 1) || |
|
1274 |
(rdata->buffer.selected == 2 && rdata->buffer.edited > 1)) { |
|
1275 |
action = ACTION_CLEAR; |
|
830 | 1276 |
} |
831 |
} |
|
832 |
|
|
833 |
// 'end' key |
|
834 |
else if (key == KEY_END) { |
|
835 |
// ignore if cursos is very right |
|
836 |
if (shell->inputdata.cursorpos < shell->inputdata.lineend) { |
|
837 |
action = AOS_SHELL_ACTION_CURSOR2END; |
|
1277 |
// if a historic entry is selected, recall the next input from history |
|
1278 |
else if (rdata->buffer.selected > 1) { |
|
1279 |
action = ACTION_RECALLNEXT; |
|
838 | 1280 |
} |
839 | 1281 |
} |
840 | 1282 |
|
841 | 1283 |
// 'arrow left' key |
842 |
else if (key == KEY_ARROW_LEFT) {
|
|
1284 |
else if (key == KEY_ARROWLEFT) { |
|
843 | 1285 |
// ignore if cursor is very left |
844 |
if (shell->inputdata.cursorpos > 0) {
|
|
845 |
action = AOS_SHELL_ACTION_CURSORLEFT;
|
|
1286 |
if (rdata->input.cursorpos > 0) {
|
|
1287 |
action = ACTION_CURSORLEFT; |
|
846 | 1288 |
} |
847 | 1289 |
} |
848 | 1290 |
|
849 | 1291 |
// 'arrow right' key |
850 |
else if (key == KEY_ARROW_RIGHT) {
|
|
1292 |
else if (key == KEY_ARROWRIGHT) { |
|
851 | 1293 |
// ignore if cursor is very right |
852 |
if (shell->inputdata.cursorpos < shell->inputdata.lineend) { |
|
853 |
action = AOS_SHELL_ACTION_CURSORRIGHT; |
|
1294 |
if (rdata->input.cursorpos < rdata->input.length) { |
|
1295 |
action = ACTION_CURSORRIGHT; |
|
1296 |
} |
|
1297 |
} |
|
1298 |
|
|
1299 |
// CTRL + 'arrow up' key combination or 'page up' key |
|
1300 |
else if (key == KEY_CTRL_ARROWUP || key == KEY_PAGEUP) { |
|
1301 |
// recall oldest input from history only if |
|
1302 |
// not the oldest entry is already selected and |
|
1303 |
// there is at least one history entry set |
|
1304 |
if (rdata->buffer.selected < shell->input.nentries && |
|
1305 |
(_getRelativeEntry(shell, rdata, (rdata->buffer.selected > 0) ? 1 : 0))[0] != INBUF_INIT_CHAR) { |
|
1306 |
action = ACTION_RECALLOLDEST; |
|
1307 |
} |
|
1308 |
} |
|
1309 |
|
|
1310 |
// CTRL + 'arrow down' key combination or 'page down' key |
|
1311 |
else if (key == KEY_CTRL_ARROWDOWN || key == KEY_PAGEDOWN) { |
|
1312 |
// clear the line if |
|
1313 |
// no historic entry is selected or |
|
1314 |
// the most recent entry is selected, but the current one is occupied by a moodfied version of a historic entry |
|
1315 |
if ((rdata->buffer.selected == 1) || |
|
1316 |
(rdata->buffer.selected > 1 && rdata->buffer.edited > 1)) { |
|
1317 |
action = ACTION_CLEAR; |
|
1318 |
} |
|
1319 |
// if a historic entry is selected, reset to the current input |
|
1320 |
else if (rdata->buffer.selected > 1) { |
|
1321 |
action = ACTION_RECALLCURRENT; |
|
854 | 1322 |
} |
855 | 1323 |
} |
856 | 1324 |
|
857 | 1325 |
// CTRL + 'arrow left' key combination |
858 |
else if (key == KEY_CTRL_ARROW_LEFT) {
|
|
1326 |
else if (key == KEY_CTRL_ARROWLEFT) { |
|
859 | 1327 |
// ignore if cursor is very left |
860 |
if (shell->inputdata.cursorpos > 0) {
|
|
861 |
action = AOS_SHELL_ACTION_CURSORWORDLEFT;
|
|
1328 |
if (rdata->input.cursorpos > 0) {
|
|
1329 |
action = ACTION_CURSORWORDLEFT; |
|
862 | 1330 |
} |
863 | 1331 |
} |
864 | 1332 |
|
865 | 1333 |
// CTRL + 'arrow right' key combination |
866 |
else if (key == KEY_CTRL_ARROW_RIGHT) {
|
|
1334 |
else if (key == KEY_CTRL_ARROWRIGHT) { |
|
867 | 1335 |
// ignore if cursor is very right |
868 |
if (shell->inputdata.cursorpos < shell->inputdata.lineend) {
|
|
869 |
action = AOS_SHELL_ACTION_CURSORWORDRIGHT;
|
|
1336 |
if (rdata->input.cursorpos < rdata->input.length) {
|
|
1337 |
action = ACTION_CURSORWORDRIGHT; |
|
870 | 1338 |
} |
871 | 1339 |
} |
872 | 1340 |
|
873 |
// carriage return ('\r') or line feed ('\n') character |
|
874 |
else if (c == '\x0D' || c == '\x0A') { |
|
875 |
action = AOS_SHELL_ACTION_EXECUTE; |
|
1341 |
// 'end' key |
|
1342 |
else if (key == KEY_END) { |
|
1343 |
// ignore if cursos is very right |
|
1344 |
if (rdata->input.cursorpos < rdata->input.length) { |
|
1345 |
action = ACTION_CURSOR2END; |
|
1346 |
} |
|
876 | 1347 |
} |
877 | 1348 |
|
878 |
// ESC key or [ESCAPE] character |
|
879 |
else if (key == KEY_ESCAPE || c == '\x1B') { |
|
880 |
action = AOS_SHELL_ACTION_ESCSTART; |
|
1349 |
// 'home' key |
|
1350 |
else if (key == KEY_HOME) { |
|
1351 |
// ignore if cursor is very left |
|
1352 |
if (rdata->input.cursorpos > 0) { |
|
1353 |
action = ACTION_CURSOR2START; |
|
1354 |
} |
|
881 | 1355 |
} |
882 | 1356 |
|
883 |
// unknown escape sequence |
|
884 |
else if (key == KEY_UNKNOWN && strlen(shell->inputdata.escseq) > 0) { |
|
885 |
action = AOS_SHELL_ACTION_PRINTUNKNOWNSEQUENCE; |
|
1357 |
// CTRL + C key combination |
|
1358 |
else if (c == '\x03' || key == KEY_CTRL_C) { |
|
1359 |
action = ACTION_RESET; |
|
1360 |
} |
|
1361 |
|
|
1362 |
// INS key |
|
1363 |
else if (key == KEY_INSERT) { |
|
1364 |
action = ACTION_INSERTTOGGLE; |
|
1365 |
} |
|
1366 |
|
|
1367 |
// ESC key or [ESCAPE] character |
|
1368 |
else if (c == '\x1B' || key == KEY_ESCAPE) { |
|
1369 |
action = ACTION_ESCSTART; |
|
886 | 1370 |
} |
887 | 1371 |
} |
1372 |
// ongoing escape sequence or interpretation failed |
|
1373 |
else /* if (rdata->input.escseq[0] != '\0') */ { |
|
1374 |
// unknown escape sequence (interpretation failed) |
|
1375 |
if (key == KEY_UNKNOWN) { |
|
1376 |
action = ACTION_PRINTUNKNOWNSEQUENCE; |
|
1377 |
} |
|
1378 |
} /* end of action selection */ |
|
888 | 1379 |
|
889 |
/* handle function */ |
|
1380 |
/* |
|
1381 |
* execute action |
|
1382 |
*/ |
|
890 | 1383 |
switch (action) { |
891 |
case AOS_SHELL_ACTION_READCHAR: |
|
1384 |
case ACTION_NONE: |
|
1385 |
{ |
|
1386 |
// do nothing (ignore input) and read next byte |
|
1387 |
break; |
|
1388 |
} |
|
1389 |
|
|
1390 |
case ACTION_READCHAR: |
|
892 | 1391 |
{ |
893 |
if (_readChar(shell, c) == 0) { |
|
1392 |
char* line = _prepare4Modification(shell, rdata); |
|
1393 |
if (_printChar(shell, rdata, c) == 0) { |
|
894 | 1394 |
// line is full |
895 |
_moveCursor(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
|
|
1395 |
_moveCursor(shell, line, rdata->input.cursorpos, rdata->input.length);
|
|
896 | 1396 |
chprintf((BaseSequentialStream*)&shell->stream, "\n\tmaximum line width reached\n"); |
897 | 1397 |
_printPrompt(shell); |
898 |
_printLine(shell, 0, shell->inputdata.lineend);
|
|
899 |
_moveCursor(shell, shell->inputdata.lineend, shell->inputdata.cursorpos);
|
|
1398 |
_printString(shell, line, 0, rdata->input.length);
|
|
1399 |
_moveCursor(shell, line, rdata->input.length, rdata->input.cursorpos);
|
|
900 | 1400 |
} |
901 | 1401 |
break; |
902 | 1402 |
} |
903 | 1403 |
|
904 |
case AOS_SHELL_ACTION_AUTOFILL:
|
|
1404 |
case ACTION_AUTOCOMPLETE:
|
|
905 | 1405 |
{ |
906 |
const char* fill = shell->input.line; |
|
907 |
size_t cmatch = shell->inputdata.cursorpos; |
|
1406 |
// local variables |
|
1407 |
char* line = _getVisualisedEntry(shell, rdata); |
|
1408 |
const char* fill = line; |
|
1409 |
size_t cmatch = rdata->input.cursorpos; |
|
908 | 1410 |
charmatch_t matchlevel = CHAR_MATCH_NOT; |
909 | 1411 |
size_t n; |
910 |
// iterate through command list |
|
911 |
for (aos_shellcommand_t* cmd = shell->commands; cmd != NULL; cmd = cmd->next) { |
|
912 |
// compare current match with command |
|
913 |
n = cmatch; |
|
914 |
charmatch_t mlvl = CHAR_MATCH_NOT; |
|
915 |
_strccmp(fill, cmd->name, shell->config & AOS_SHELL_CONFIG_MATCH_CASE, (n == 0) ? NULL : &n, &mlvl); |
|
916 |
const int cmp = (n < cmatch) ? |
|
917 |
((int)n - (int)cmatch) : |
|
918 |
(cmd->name[n] != '\0') ? |
|
919 |
(int)strlen(cmd->name) - (int)n : |
|
920 |
0; |
|
921 |
// if an exact match was found |
|
922 |
if ((size_t)((int)cmatch + cmp) == shell->inputdata.cursorpos) { |
|
923 |
cmatch = shell->inputdata.cursorpos; |
|
924 |
fill = cmd->name; |
|
925 |
// break the loop only if there are no case mismatches with the input |
|
926 |
n = shell->inputdata.cursorpos; |
|
927 |
_strccmp(fill, shell->input.line, false, &n, &mlvl); |
|
928 |
if (mlvl == CHAR_MATCH_CASE) { |
|
929 |
break; |
|
930 |
} |
|
931 |
} |
|
932 |
// if a not exact match was found |
|
933 |
else if ((size_t)((int)cmatch + cmp) > shell->inputdata.cursorpos) { |
|
934 |
// if this is the first one |
|
935 |
if (fill == shell->input.line) { |
|
936 |
cmatch = (size_t)((int)cmatch + cmp); |
|
1412 |
|
|
1413 |
// only execute autofill if the line is valid |
|
1414 |
if (line) { |
|
1415 |
_prepare4Modification(shell, rdata); |
|
1416 |
|
|
1417 |
// iterate through command list |
|
1418 |
for (aos_shellcommand_t* cmd = shell->commands; cmd != NULL; cmd = cmd->next) { |
|
1419 |
// compare current match with command |
|
1420 |
n = cmatch; |
|
1421 |
charmatch_t mlvl = CHAR_MATCH_NOT; |
|
1422 |
_strccmp(fill, cmd->name, shell->config & AOS_SHELL_CONFIG_MATCH_CASE, (n == 0) ? NULL : &n, &mlvl); |
|
1423 |
const int cmp = (n < cmatch) ? |
|
1424 |
((int)n - (int)cmatch) : |
|
1425 |
(cmd->name[n] != '\0') ? |
|
1426 |
(int)strlen(cmd->name) - (int)n : |
|
1427 |
0; |
|
1428 |
// if an exact match was found |
|
1429 |
if ((size_t)((int)cmatch + cmp) == rdata->input.cursorpos) { |
|
1430 |
cmatch = rdata->input.cursorpos; |
|
937 | 1431 |
fill = cmd->name; |
1432 |
// break the loop only if there are no case mismatches with the input |
|
1433 |
n = rdata->input.cursorpos; |
|
1434 |
_strccmp(fill, line, false, &n, &mlvl); |
|
1435 |
if (mlvl == CHAR_MATCH_CASE) { |
|
1436 |
break; |
|
1437 |
} |
|
938 | 1438 |
} |
939 |
// if this is a worse one |
|
940 |
else if ((cmp < 0) || (cmp == 0 && mlvl == CHAR_MATCH_CASE)) { |
|
941 |
cmatch = (size_t)((int)cmatch + cmp); |
|
1439 |
// if a not exact match was found |
|
1440 |
else if ((size_t)((int)cmatch + cmp) > rdata->input.cursorpos) { |
|
1441 |
// if this is the first one |
|
1442 |
if (fill == line) { |
|
1443 |
cmatch = (size_t)((int)cmatch + cmp); |
|
1444 |
fill = cmd->name; |
|
1445 |
} |
|
1446 |
// if this is a worse one |
|
1447 |
else if ((cmp < 0) || (cmp == 0 && mlvl == CHAR_MATCH_CASE)) { |
|
1448 |
cmatch = (size_t)((int)cmatch + cmp); |
|
1449 |
} |
|
942 | 1450 |
} |
1451 |
// non matching commands are ignored |
|
1452 |
else {} |
|
943 | 1453 |
} |
944 |
// non matching commands are ignored |
|
945 |
else {} |
|
946 |
} |
|
947 |
// evaluate if there are case mismatches |
|
948 |
n = cmatch; |
|
949 |
_strccmp(shell->input.line, fill, shell->config & AOS_SHELL_CONFIG_MATCH_CASE, &n, &matchlevel); |
|
950 |
// print the auto fill if any |
|
951 |
if (cmatch > shell->inputdata.cursorpos || (cmatch == shell->inputdata.cursorpos && matchlevel == CHAR_MATCH_NCASE)) { |
|
952 |
shell->inputdata.noinput = false; |
|
953 |
// limit auto fill so it will not overflow the line width |
|
954 |
if (shell->inputdata.lineend + (cmatch - shell->inputdata.cursorpos) > shell->input.size) { |
|
955 |
cmatch = shell->input.size - shell->inputdata.lineend + shell->inputdata.cursorpos; |
|
956 |
} |
|
957 |
// move trailing memory further in the line |
|
958 |
memmove(&(shell->input.line[cmatch]), &(shell->input.line[shell->inputdata.cursorpos]), shell->inputdata.lineend - shell->inputdata.cursorpos); |
|
959 |
shell->inputdata.lineend += cmatch - shell->inputdata.cursorpos; |
|
960 |
// if there was no incorrect case when matching |
|
961 |
if (matchlevel == CHAR_MATCH_CASE) { |
|
962 |
// insert fill command name to line |
|
963 |
memcpy(&(shell->input.line[shell->inputdata.cursorpos]), &(fill[shell->inputdata.cursorpos]), cmatch - shell->inputdata.cursorpos); |
|
964 |
// print the output |
|
965 |
_printLine(shell, shell->inputdata.cursorpos, shell->inputdata.lineend); |
|
966 |
} else { |
|
967 |
// overwrite line with fill command name |
|
968 |
memcpy(shell->input.line, fill, cmatch); |
|
969 |
// reprint the whole line |
|
970 |
_moveCursor(shell, shell->inputdata.cursorpos, 0); |
|
971 |
_printLine(shell, 0, shell->inputdata.lineend); |
|
1454 |
|
|
1455 |
// evaluate if there are case mismatches |
|
1456 |
n = cmatch; |
|
1457 |
_strccmp(line, fill, shell->config & AOS_SHELL_CONFIG_MATCH_CASE, &n, &matchlevel); |
|
1458 |
// print the auto fill if any |
|
1459 |
if (cmatch > rdata->input.cursorpos || (cmatch == rdata->input.cursorpos && matchlevel == CHAR_MATCH_NCASE)) { |
|
1460 |
// limit auto fill so it will not overflow the line width |
|
1461 |
if (rdata->input.length + (cmatch - rdata->input.cursorpos) > shell->input.linewidth) { |
|
1462 |
cmatch = shell->input.linewidth - rdata->input.length + rdata->input.cursorpos; |
|
1463 |
} |
|
1464 |
// move trailing memory further in the line |
|
1465 |
memmove(&line[cmatch], &line[rdata->input.cursorpos], (rdata->input.length - rdata->input.cursorpos) * sizeof(char)); |
|
1466 |
rdata->input.length += cmatch - rdata->input.cursorpos; |
|
1467 |
// if there was no incorrect case when matching |
|
1468 |
if (matchlevel == CHAR_MATCH_CASE) { |
|
1469 |
// insert fill command name to line |
|
1470 |
memcpy(&line[rdata->input.cursorpos], &fill[rdata->input.cursorpos], (cmatch - rdata->input.cursorpos) * sizeof(char)); |
|
1471 |
// print the output |
|
1472 |
_printString(shell, line, rdata->input.cursorpos, rdata->input.length); |
|
1473 |
} else { |
|
1474 |
// overwrite line with fill command name |
|
1475 |
memcpy(line, fill, cmatch * sizeof(char)); |
|
1476 |
// reprint the whole line |
|
1477 |
_moveCursor(shell, line, rdata->input.cursorpos, 0); |
|
1478 |
_printString(shell, line, 0, rdata->input.length); |
|
1479 |
} |
|
1480 |
// move cursor to the end of the matching sequence |
|
1481 |
rdata->input.cursorpos = cmatch; |
|
1482 |
_moveCursor(shell, line, rdata->input.length, rdata->input.cursorpos); |
|
972 | 1483 |
} |
973 |
// move cursor to the end of the matching sequence |
|
974 |
shell->inputdata.cursorpos = cmatch; |
|
975 |
_moveCursor(shell, shell->inputdata.lineend, shell->inputdata.cursorpos); |
|
976 | 1484 |
} |
977 | 1485 |
break; |
978 | 1486 |
} |
979 | 1487 |
|
980 |
case AOS_SHELL_ACTION_SUGGEST:
|
|
1488 |
case ACTION_SUGGEST: |
|
981 | 1489 |
{ |
1490 |
// local variables |
|
1491 |
const char* line = _getVisualisedEntry(shell, rdata); |
|
982 | 1492 |
unsigned int matches = 0; |
1493 |
|
|
983 | 1494 |
// iterate through command list |
984 | 1495 |
for (aos_shellcommand_t* cmd = shell->commands; cmd != NULL; cmd = cmd->next) { |
985 |
// compare line content with command, excpet if cursorpos=0
|
|
986 |
size_t i = shell->inputdata.cursorpos;
|
|
987 |
if (shell->inputdata.cursorpos > 0) {
|
|
988 |
_strccmp(shell->input.line, cmd->name, true, &i, NULL);
|
|
1496 |
// compare line content with command, except if cursorpos is 0
|
|
1497 |
size_t i = rdata->input.cursorpos;
|
|
1498 |
if (rdata->input.cursorpos > 0) {
|
|
1499 |
_strccmp(line, cmd->name, true, &i, NULL); |
|
989 | 1500 |
} |
990 |
const int cmp = (i < shell->inputdata.cursorpos) ?
|
|
991 |
(int)(i - shell->inputdata.cursorpos) :
|
|
992 |
(cmd->name[i] != '\0') ?
|
|
993 |
(int)strlen(cmd->name) - (int)i :
|
|
994 |
0;
|
|
1501 |
const int cmp = (i < rdata->input.cursorpos) ?
|
|
1502 |
((int)i - (int)rdata->input.cursorpos) :
|
|
1503 |
(cmd->name[i] != '\0') ? |
|
1504 |
(int)strlen(cmd->name) - (int)i : |
|
1505 |
0; |
|
995 | 1506 |
// if a match was found |
996 | 1507 |
if (cmp > 0) { |
997 | 1508 |
// if this is the first one |
998 | 1509 |
if (matches == 0) { |
999 |
_moveCursor(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
|
|
1510 |
_moveCursor(shell, line, rdata->input.cursorpos, rdata->input.length);
|
|
1000 | 1511 |
streamPut(&shell->stream, '\n'); |
1001 | 1512 |
} |
1002 | 1513 |
// print the command |
... | ... | |
1007 | 1518 |
// reprint the prompt and line if any matches have been found |
1008 | 1519 |
if (matches > 0) { |
1009 | 1520 |
_printPrompt(shell); |
1010 |
_printLine(shell, 0, shell->inputdata.lineend); |
|
1011 |
_moveCursor(shell, shell->inputdata.lineend, shell->inputdata.cursorpos); |
|
1012 |
shell->inputdata.noinput = false; |
|
1521 |
_printString(shell, line, 0, rdata->input.length); |
|
1522 |
_moveCursor(shell, line, rdata->input.length, rdata->input.cursorpos); |
|
1013 | 1523 |
} |
1014 | 1524 |
break; |
1015 | 1525 |
} |
1016 | 1526 |
|
1017 |
case AOS_SHELL_ACTION_INSERTTOGGLE:
|
|
1527 |
case ACTION_EXECUTE:
|
|
1018 | 1528 |
{ |
1019 |
if (shell->config & AOS_SHELL_CONFIG_INPUT_OVERWRITE) { |
|
1020 |
shell->config &= ~AOS_SHELL_CONFIG_INPUT_OVERWRITE; |
|
1021 |
} else { |
|
1022 |
shell->config |= AOS_SHELL_CONFIG_INPUT_OVERWRITE; |
|
1529 |
// if the input buffer can hold historic entries |
|
1530 |
if (shell->input.nentries > 1) { |
|
1531 |
_prepare4Modification(shell, rdata); |
|
1023 | 1532 |
} |
1024 | 1533 |
break; |
1025 | 1534 |
} |
1026 | 1535 |
|
1027 |
case AOS_SHELL_ACTION_DELETEFORWARD:
|
|
1536 |
case ACTION_DELETEBACKWARD:
|
|
1028 | 1537 |
{ |
1029 |
--shell->inputdata.lineend; |
|
1030 |
memmove(&(shell->input.line[shell->inputdata.cursorpos]), &(shell->input.line[shell->inputdata.cursorpos+1]), shell->inputdata.lineend - shell->inputdata.cursorpos); |
|
1031 |
_printLine(shell, shell->inputdata.cursorpos, shell->inputdata.lineend); |
|
1538 |
char* line = _prepare4Modification(shell, rdata); |
|
1539 |
--rdata->input.cursorpos; |
|
1540 |
memmove(&line[rdata->input.cursorpos], &line[rdata->input.cursorpos + 1], (rdata->input.length - rdata->input.cursorpos) * sizeof(char)); |
|
1541 |
--rdata->input.length; |
|
1542 |
line[rdata->input.length] = '\0'; |
|
1543 |
_moveCursor(shell, line, rdata->input.cursorpos + 1, rdata->input.cursorpos); |
|
1544 |
_printString(shell, line, rdata->input.cursorpos, rdata->input.length); |
|
1032 | 1545 |
streamPut(&shell->stream, ' '); |
1033 |
_moveCursor(shell, shell->inputdata.lineend + 1, shell->inputdata.cursorpos);
|
|
1546 |
_moveCursor(shell, line, rdata->input.length + 1, rdata->input.cursorpos);
|
|
1034 | 1547 |
break; |
1035 | 1548 |
} |
Also available in: Unified diff