Revision 697dba3c core/src/aos_shell.c

View differences:

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
      }
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff