commit 697dba3c7c3a97df0437fe352f25bc2181a2d9d7
Author: Thomas Schöpping <tschoepp@cit-ec.uni-bielefeld.de>
Date:   Fri Jan 17 10:58:18 2020 +0100

    Major overhaul of the shell.
    A new configuration option allows to set the shell history length.

diff --git a/core/inc/aos_confcheck.h b/core/inc/aos_confcheck.h
index 1dde0cb..c5b9df1 100644
--- a/core/inc/aos_confcheck.h
+++ b/core/inc/aos_confcheck.h
@@ -164,6 +164,10 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
     #error "AMIROOS_CFG_SHELL_MAXARGS not defined in aosconf.h"
   #endif /* !defined(AMIROOS_CFG_SHELL_MAXARGS) */
 
+  #if !defined(AMIROOS_CFG_SHELL_HISTLENGTH)
+    #error "AMIROOS_CFG_SHELL_HISTLENGTH not defined in aosconf.h"
+  #endif /* !defined(AMIROOS_CFG_SHELL_HISTLENGTH) */
+
 #else /* (AMIROOS_CFG_SHELL_ENABLE == true) */
 
   #if (AMIROOS_CFG_TESTS_ENABLE == true)
diff --git a/core/inc/aos_shell.h b/core/inc/aos_shell.h
index b008b2c..cd2b2e1 100644
--- a/core/inc/aos_shell.h
+++ b/core/inc/aos_shell.h
@@ -50,7 +50,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 /**
  * @brief   Shell event flag that is emitted when a command is executed.
  */
-#define AOS_SHELL_EVTFLAG_EXEC                    ((eventflags_t)(1 << 1))
+#define AOS_SHELL_EVTFLAG_EXECUTE                 ((eventflags_t)(1 << 1))
 
 /**
  * @brief   Shell event flag that is emitted when a command execution finished.
@@ -239,30 +239,6 @@ typedef struct aos_shellexecstatus {
 } aos_shellexecstatus_t;
 
 /**
- * @brief   Enumerator to encode shell actions.
- */
-typedef enum aos_shellaction {
-  AOS_SHELL_ACTION_NONE,
-  AOS_SHELL_ACTION_READCHAR,
-  AOS_SHELL_ACTION_AUTOFILL,
-  AOS_SHELL_ACTION_SUGGEST,
-  AOS_SHELL_ACTION_INSERTTOGGLE,
-  AOS_SHELL_ACTION_DELETEFORWARD,
-  AOS_SHELL_ACTION_DELETEBACKWARD,
-  AOS_SHELL_ACTION_RECALLLAST,
-  AOS_SHELL_ACTION_CLEAR,
-  AOS_SHELL_ACTION_CURSOR2START,
-  AOS_SHELL_ACTION_CURSOR2END,
-  AOS_SHELL_ACTION_CURSORLEFT,
-  AOS_SHELL_ACTION_CURSORRIGHT,
-  AOS_SHELL_ACTION_CURSORWORDLEFT,
-  AOS_SHELL_ACTION_CURSORWORDRIGHT,
-  AOS_SHELL_ACTION_EXECUTE,
-  AOS_SHELL_ACTION_ESCSTART,
-  AOS_SHELL_ACTION_PRINTUNKNOWNSEQUENCE,
-} aos_shellaction_t;
-
-/**
  * @brief   Shell structure.
  */
 typedef struct aos_shell {
@@ -277,19 +253,9 @@ typedef struct aos_shell {
   event_source_t eventSource;
 
   /**
-   * @brief   Struct for OS related events
+   * @brief   Listener for OS related events.
    */
-  struct {
-    /**
-     * @brief   Pointer to the OS' event source.
-     */
-    event_source_t* eventSource;
-
-    /**
-     * @brief   Listener for OS related events.
-     */
-    event_listener_t eventListener;
-  } os;
+  event_listener_t osEventListener;
 
   /**
      * @brief   Pointer to the first I/O channel.
@@ -318,51 +284,29 @@ typedef struct aos_shell {
   struct {
     /**
      * @brief   Input buffer.
+     * @details This buffer is interpreted as two dimensional array.
+     *          It contains @p nentries elements of @p linewidth size each.
      */
-    char* line;
-
-    /**
-     * @brief   Size of the input buffer.
-     */
-    size_t length;
-
-    /**
-     * @brief   Size of the argument buffer.
-     */
-    size_t nargs;
-  } input;
-
-  /**
-   * @brief   Structure containing data for internal input parsing.
-   */
-  struct {
-    /**
-     * @brief   The last action executed by the shell.
-     */
-    aos_shellaction_t lastaction;
+    char* buffer;
 
     /**
-     * @brief   Buffer to store an escape sequence.
+     * @brief   Number of entries in the buffer.
+     * @note    Must be >0.
      */
-    char escseq[AOS_SHELL_ESCSEQUENCE_LENGTH];
+    size_t nentries;
 
     /**
-     * @brief   Current curso position.
+     * @brief   Width of each input line.
+     * @brief   Must be >0.
      */
-    size_t cursorpos;
+    size_t linewidth;
 
     /**
-     * @brief   Current line width.
-     */
-    size_t lineend;
-
-    /**
-     * @brief   Flag whether there was input since the prompt was printed the last time.
+     * @brief   Size of the argument buffer.
+     * @note    Must be >0.
      */
-    bool noinput;
-  } inputdata;
-
-
+    size_t nargs;
+  } input;
 
   /**
    * @brief   Configuration flags.
@@ -394,7 +338,7 @@ typedef struct aos_shell {
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
-  void aosShellInit(aos_shell_t* shell, event_source_t* oseventsource, const char* prompt, char* line, size_t linesize, size_t numargs);
+  void aosShellInit(aos_shell_t* shell, const char* prompt, char inbuf[], size_t entries, size_t linewidth, size_t numargs);
   void aosShellStreamInit(AosShellStream* stream);
   void aosShellChannelInit(AosShellChannel* channel, BaseAsynchronousChannel* asyncchannel);
   aos_status_t aosShellAddCommand(aos_shell_t* shell, aos_shellcommand_t* cmd);
diff --git a/core/src/aos_shell.c b/core/src/aos_shell.c
index 2fb9cb4..d3974e4 100644
--- a/core/src/aos_shell.c
+++ b/core/src/aos_shell.c
@@ -35,14 +35,89 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 /******************************************************************************/
 
 /**
+ * @brief   The character the input buffer is initialized with.
+ */
+#define INBUF_INIT_CHAR                         '\x07'
+
+/**
  * @brief   Event mask to be set on OS related events.
  */
-#define AOS_SHELL_EVENTMASK_OS                  EVENT_MASK(0)
+#define EVENTMASK_OS                            EVENT_MASK(0)
 
 /**
  * @brief   Event mask to be set on a input event.
  */
-#define AOS_SHELL_EVENTMASK_INPUT               EVENT_MASK(1)
+#define EVENTMASK_INPUT                         EVENT_MASK(1)
+
+/**
+ * @brief   String that defines the INSERT key as specified by VT100.
+ */
+#define KEYSTRING_INSERT                        "\x1B\x5B\x32\x7E"
+
+/**
+ * @brief   String that defines the DEL key as specified by VT100.
+ */
+#define KEYSTRING_DELETE                        "\x1B\x5B\x33\x7E"
+
+/**
+ * @brief   String that defines the HOME key as specified by VT100.
+ */
+#define KEYSTRING_HOME                          "\x1B\x5B\x48"
+
+/**
+ * @brief   String that defines the END key as specified by VT100.
+ */
+#define KEYSTRING_END                           "\x1B\x5B\x46"
+
+/**
+ * @brief   String that defines the PGUP key as specified by VT100.
+ */
+#define KEYSTRING_PAGEUP                        "\x1B\x5B\x35\x7E"
+
+/**
+ * @brief   String that defines the PGUP key as specified by VT100.
+ */
+#define KEYSTRING_PAGEDOWN                      "\x1B\x5B\x36\x7E"
+
+/**
+ * @brief   String that defines the 'arrow down' key as specified by VT100.
+ */
+#define KEYSTRING_ARROWUP                       "\x1B\x5B\x41"
+
+/**
+ * @brief   String that defines the 'arrow up' key as specified by VT100.
+ */
+#define KEYSTRING_ARROWDOWN                     "\x1B\x5B\x42"
+
+/**
+ * @brief   String that defines the 'arrow left' key as specified by VT100.
+ */
+#define KEYSTRING_ARROWLEFT                     "\x1B\x5B\x44"
+
+/**
+ * @brief   String that defines the 'arrow right' key as specified by VT100.
+ */
+#define KEYSTRING_ARROWRIGHT                    "\x1B\x5B\x43"
+
+/**
+ * @brief   String that defines the CRTL + 'arrow up' key combination as specified by VT100.
+ */
+#define KEYSTRING_CTRL_ARROWUP                  "\x1B\x5B\x31\x3B\x35\x41"
+
+/**
+ * @brief   String that defines the CRTL + 'arrow down' key combination as specified by VT100.
+ */
+#define KEYSTRING_CTRL_ARROWDOWN                "\x1B\x5B\x31\x3B\x35\x42"
+
+/**
+ * @brief   String that defines the CRTL + 'arrow left' key combination as specified by VT100.
+ */
+#define KEYSTRING_CTRL_ARROWLEFT                "\x1B\x5B\x31\x3B\x35\x44"
+
+/**
+ * @brief   String that defines the CRTL + 'arrow right' key combination as specified by VT100.
+ */
+#define KEYSTRING_CTRL_ARROWRIGHT               "\x1B\x5B\x31\x3B\x35\x43"
 
 /******************************************************************************/
 /* EXPORTED VARIABLES                                                         */
@@ -97,22 +172,23 @@ typedef enum special_key {
   KEY_UNKNOWN,          /**< any/unknow key */
   KEY_AMBIGUOUS,        /**< key is ambiguous */
   KEY_TAB,              /**< tabulator key */
-  KEY_ESCAPE,           /**< escape key */
   KEY_BACKSPACE,        /**< backspace key */
   KEY_INSERT,           /**< insert key */
   KEY_DELETE,           /**< delete key */
+  KEY_ESCAPE,           /**< escape key */
   KEY_HOME,             /**< home key */
   KEY_END,              /**< end key */
-  KEY_PAGE_UP,          /**< page up key */
-  KEY_PAGE_DOWN,        /**< page down key */
-  KEY_ARROW_UP,         /**< arrow up key */
-  KEY_ARROW_DOWN,       /**< arrow down key */
-  KEY_ARROW_LEFT,       /**< arrow left key */
-  KEY_ARROW_RIGHT,      /**< arrow right key */
-  KEY_CTRL_ARROW_UP,    /**< CTRL + arrow up key */
-  KEY_CTRL_ARROW_DOWN,  /**< CTRL + arrow down key */
-  KEY_CTRL_ARROW_LEFT,  /**< CTRL + arrow left key */
-  KEY_CTRL_ARROW_RIGHT, /**< CTRL + arrow right key */
+  KEY_PAGEUP,           /**< page up key */
+  KEY_PAGEDOWN,         /**< page down key */
+  KEY_ARROWUP,          /**< arrow up key */
+  KEY_ARROWDOWN,        /**< arrow down key */
+  KEY_ARROWLEFT,        /**< arrow left key */
+  KEY_ARROWRIGHT,       /**< arrow right key */
+  KEY_CTRL_ARROWUP,     /**< CTRL + arrow up key */
+  KEY_CTRL_ARROWDOWN,   /**< CTRL + arrow down key */
+  KEY_CTRL_ARROWLEFT,   /**< CTRL + arrow left key */
+  KEY_CTRL_ARROWRIGHT,  /**< CTRL + arrow right key */
+  KEY_CTRL_C,           /**< CTRL + C key */
 } special_key_t;
 
 /**
@@ -124,6 +200,92 @@ typedef enum charmatch {
   CHAR_MATCH_CASE   = 2,  /**< Characters do match with case. */
 } charmatch_t;
 
+/**
+ * @brief   Enumerator to encode shell actions.
+ */
+typedef enum aos_shellaction {
+  ACTION_NONE,                  /**< No action at all. */
+  ACTION_READCHAR,              /**< Read a printable character. */
+  ACTION_AUTOCOMPLETE,          /**< Automatically comlete input by using available command. */
+  ACTION_SUGGEST,               /**< Suggest matching available commands. */
+  ACTION_EXECUTE,               /**< Execute input. */
+  ACTION_DELETEBACKWARD,        /**< Delete a single character backwards. */
+  ACTION_DELETEFORWARD,         /**< Delete a single character forwards. */
+  ACTION_CLEAR,                 /**< Clear the input. */
+  ACTION_RECALLPREVIOUS,        /**< Recall the previous (older) entry in the history. */
+  ACTION_RECALLNEXT,            /**< Recall the next (more recent) entry in the history. */
+  ACTION_RECALLOLDEST,          /**< Recall the oldest entry in the history. */
+  ACTION_RECALLCURRENT,         /**< Recall the current input. */
+  ACTION_CURSORLEFT,            /**< Move cursor one character to the left. */
+  ACTION_CURSORRIGHT,           /**< Move cursor one character to the right. */
+  ACTION_CURSORWORDLEFT,        /**< Move cursor one word to the left. */
+  ACTION_CURSORWORDRIGHT,       /**< Move cursor one word to the right. */
+  ACTION_CURSOR2END,            /**< Move cursor to the very right. */
+  ACTION_CURSOR2START,          /**< Move cursor to the very left. */
+  ACTION_RESET,                 /**< Reset the current input. */
+  ACTION_INSERTTOGGLE,          /**< Toggle insertion mode. */
+  ACTION_ESCSTART,              /**< Start an escape sequence (special keys). */
+  ACTION_PRINTUNKNOWNSEQUENCE,  /**< Print an unknown escape sequence. */
+} action_t;
+
+/**
+ * @brief   Struct that holds most important runtime data for the shell.
+ * @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.
+ */
+typedef struct runtimedata {
+  /**
+   * @brief   Data related to the current input.
+   */
+  struct {
+    /**
+     * @brief   Length of the input.
+     */
+    size_t length;
+
+    /**
+     * @brief   Current position of the cursor in the input line.
+     */
+    size_t cursorpos;
+
+    /**
+     * @brief   Buffer to store escape sequences, which describe special characters.
+     */
+    char escseq[AOS_SHELL_ESCSEQUENCE_LENGTH];
+  } input;
+
+  /**
+   * @brief   Data related to the entry or history buffer.
+   */
+  struct {
+    /**
+     * @brief   Current entry to be filled and executed.
+     */
+    size_t current;
+
+    /**
+     * @brief   Selected entry in the 'history' as preview.
+     * @details A value of 0 indicates, that the line is cleared as a preview.
+     *          A value of 1 indicates, that the current entry is selected.
+     *          A value of t>1 indicates, that the entry t-1 in the past is selected.
+     *          The value must never be greater than the number of entries available, of course.
+     */
+    size_t selected;
+
+    /**
+     * @brief   Selected entry in the 'history' that has been edited by the user.
+     *          A value of 0 indicates, that there was no modification by the user yet (i.e. charcters, deletions or autofill).
+     *          A value of 1 indicates, that the current entry was edited.
+     *          A value of t>1 indicated, that a history entry was recalled and then edited.
+     */
+    size_t edited;
+  } buffer;
+
+  /**
+   * @brief   The last action executed by the shell.
+   */
+  action_t lastaction;
+} runtimedata_t;
+
 /******************************************************************************/
 /* LOCAL VARIABLES                                                            */
 /******************************************************************************/
@@ -248,6 +410,9 @@ static msg_t _channelctl(void *instance, unsigned int operation, void *arg)
   return MSG_OK;
 }
 
+/**
+ * @brief   Implementation of the BaseSequentialStream write() method.
+ */
 static size_t _streamwrite(void *instance, const uint8_t *bp, size_t n)
 {
   aosDbgCheck(instance != NULL);
@@ -267,6 +432,9 @@ static size_t _streamwrite(void *instance, const uint8_t *bp, size_t n)
   return maxbytes;
 }
 
+/**
+ * @brief   Implementation of the BaseSequentialStream read() method.
+ */
 static size_t _stremread(void *instance, uint8_t *bp, size_t n)
 {
   (void)instance;
@@ -276,6 +444,9 @@ static size_t _stremread(void *instance, uint8_t *bp, size_t n)
   return 0;
 }
 
+/**
+ * @brief   Implementation of the BaseSequentialStream put() method.
+ */
 static msg_t _streamput(void *instance, uint8_t b)
 {
   aosDbgCheck(instance != NULL);
@@ -294,6 +465,9 @@ static msg_t _streamput(void *instance, uint8_t b)
   return ret;
 }
 
+/**
+ * @brief   Implementation of the BaseSequentialStream get() method.
+ */
 static msg_t _streamget(void *instance)
 {
   (void)instance;
@@ -302,6 +476,121 @@ static msg_t _streamget(void *instance)
 }
 
 /**
+ * @brief   Retreive a pointer to the string buffer of a specified entry in the input buffer.
+ *
+ * @param[in] shell   Pointer to a shell object.
+ * @param[in] entry   Entry to be retrieved.
+ *
+ * @return  Pointer to the entry in the input buffer.
+ */
+static inline char* _getAbsoluteEntry(const aos_shell_t* shell, size_t entry)
+{
+  aosDbgCheck(shell != NULL);
+  aosDbgCheck(entry < shell->input.nentries);
+
+  return &(shell->input.buffer[entry * shell->input.linewidth * sizeof(char)]);
+}
+
+/**
+ * @brief   Calculate absolute entry from history offset.
+ *
+ * @param[in] shell   Pointer to a shell object.
+ * @param[in] rdata   Pointer to a runtime data object.
+ * @param[in] offset  Relative offset of the entry to be retreived.
+ *
+ * @return  Absolute index of the historic entry.
+ */
+static inline size_t _historyOffset2EntryIndex(const aos_shell_t* shell, const runtimedata_t* rdata, size_t offset)
+{
+  aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
+  aosDbgCheck(offset < shell->input.nentries);
+
+  return ((shell->input.nentries + rdata->buffer.current - offset) % shell->input.nentries);
+}
+
+/**
+ * @brief   Retreive a pointer to the string buffer of a historic entry in the input buffer.
+ *
+ * @param[in] shell   Pointer to a shell object.
+ * @param[in] rdata   Pointer to a runtime data object.
+ * @param[in] offset  Relative offset of the entry to be retreived.
+ *
+ * @return  Pointer to the entry in the input buffer.
+ */
+static inline char* _getRelativeEntry(const aos_shell_t* shell, const runtimedata_t* rdata, size_t offset)
+{
+  aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
+  aosDbgCheck(offset < shell->input.nentries);
+
+  return _getAbsoluteEntry(shell, _historyOffset2EntryIndex(shell, rdata, offset));
+}
+
+/**
+ * @brief   Retreive a pointer to the current entry string in the input buffer.
+ *
+ * @param[in] shell   Pointer to a shell object.
+ * @param[in] rdata   Pointer to a runtime data object.
+ *
+ * @return  Pointer to the string of the current entry in the input buffer.
+ */
+static inline char* _getCurrentEntry(const aos_shell_t* shell, const runtimedata_t* rdata)
+{
+  aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
+
+  return _getAbsoluteEntry(shell, rdata->buffer.current);
+}
+
+/**
+ * @brief   Retreive a pointer to the currently selected entry.
+ *
+ * @param[in] shell   Pointer to a shell object.
+ * @param[in] rdata   Pointer to a runtime data object.
+ *
+ * @return  Pointer to the currently selected entry or NULL if no entry is selected (cleared preview).
+ */
+static inline char* _getSelectedEntry(const aos_shell_t* shell, const runtimedata_t* rdata)
+{
+  aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
+
+  if (rdata->buffer.selected > 0) {
+    return _getRelativeEntry(shell, rdata, rdata->buffer.selected - 1);
+  } else {
+    return NULL;
+  }
+}
+
+/**
+ * @brief   Retreive the currently visualized entry.
+ *
+ * @param[in] shell   Pointer to a shell object.
+ * @param[in] rdata   Pointer to a runtime data object.
+ *
+ * @return  Pointer to the currently visualized entry or NULL if the input has been cleared (cleared preview).
+ */
+static inline char* _getVisualisedEntry(const aos_shell_t* shell, const runtimedata_t* rdata)
+{
+  aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
+
+  if (rdata->buffer.selected == 0) {
+    // cleared preview, nothing visualized
+    return NULL;
+  } else {
+    if (rdata->buffer.selected == 1 || rdata->buffer.selected == rdata->buffer.edited) {
+      // the current or a modified entry is selected
+      return _getCurrentEntry(shell, rdata);
+    } else {
+      // a historic, unmodified entry is selected
+      return _getRelativeEntry(shell, rdata, rdata->buffer.selected - 1);
+    }
+  }
+}
+
+/**
  * @brief   Print the shell prompt
  * @details Depending on the configuration flags, the system uptime is printed before the prompt string.
  *
@@ -371,167 +660,181 @@ static void _printPrompt(aos_shell_t* shell)
 static special_key_t _interpreteEscapeSequence(const char seq[])
 {
   // local variables
-  char str[AOS_SHELL_ESCSEQUENCE_LENGTH];
   unsigned long strl = 0;
   const unsigned long seql = strlen(seq);
   bool ambiguous = false;
 
   // TAB
-  /* not supported yet; use "\x09" instead */
+  /* not supported yet; use '\x09' instead */
 
   // BACKSPACE
-  /* not supported yet; use "\x08" instead */
+  /* not supported yet; use '\x08' instead */
 
   // ESCAPE
-  strncpy(str, "\x1B", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_ESCAPE;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
-  }
+  /* not supported yes; use '\x1B' instead */
+
+  // CTRL + C
+  /* not defined yet; use '\x03' instead */
 
   // INSERT
-  strncpy(str, "\x1B\x5B\x32\x7E", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_INSERT;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_INSERT, seql) == 0) {
+    strl = strlen(KEYSTRING_INSERT);
+    if (seql == strl) {
+      return KEY_INSERT;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // DELETE
-  strncpy(str, "\x1B\x5B\x33\x7E", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_DELETE;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_DELETE, seql) == 0) {
+    strl = strlen(KEYSTRING_DELETE);
+    if (seql == strl) {
+      return KEY_DELETE;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // HOME
-  strncpy(str, "\x1B\x5B\x48", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_HOME;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_HOME, seql) == 0) {
+    strl = strlen(KEYSTRING_HOME);
+    if (seql == strl) {
+      return KEY_HOME;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // END
-  strncpy(str, "\x1B\x5B\x46", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_END;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_END, seql) == 0) {
+    strl = strlen(KEYSTRING_END);
+    if (seql == strl) {
+      return KEY_END;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // PAGE UP
-  strncpy(str, "\x1B\x5B\x35\x7E", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_PAGE_UP;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_PAGEUP, seql) == 0) {
+    strl = strlen(KEYSTRING_PAGEUP);
+    if (seql == strl) {
+      return KEY_PAGEUP;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // PAGE DOWN
-  strncpy(str, "\x1B\x5B\x36\x7E", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_PAGE_DOWN;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_PAGEDOWN, seql) == 0) {
+    strl = strlen(KEYSTRING_PAGEDOWN);
+    if (seql == strl) {
+      return KEY_PAGEDOWN;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // ARROW UP
-  strncpy(str, "\x1B\x5B\x41", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_ARROW_UP;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_ARROWUP, seql) == 0) {
+    strl = strlen(KEYSTRING_ARROWUP);
+    if (seql == strl) {
+      return KEY_ARROWUP;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // ARROW DOWN
-  strncpy(str, "\x1B\x5B\x42", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_ARROW_DOWN;
-  } else if (seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_ARROWDOWN, seql) == 0) {
+    strl = strlen(KEYSTRING_ARROWDOWN);
+    if (seql == strl) {
+      return KEY_ARROWDOWN;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // ARROW LEFT
-  strncpy(str, "\x1B\x5B\x44", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_ARROW_LEFT;
-  } else if (seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_ARROWLEFT, seql) == 0) {
+    strl = strlen(KEYSTRING_ARROWLEFT);
+    if (seql == strl) {
+      return KEY_ARROWLEFT;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // ARROW RIGHT
-  strncpy(str, "\x1B\x5B\x43", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_ARROW_RIGHT;
-  } else if (seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_ARROWRIGHT, seql) == 0) {
+    strl = strlen(KEYSTRING_ARROWRIGHT);
+    if (seql == strl) {
+      return KEY_ARROWRIGHT;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // CTRL + ARROW UP
-  strncpy(str, "\x1B\x5B\x31\x3B\x35\x41", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_CTRL_ARROW_UP;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_CTRL_ARROWUP, seql) == 0) {
+    strl = strlen(KEYSTRING_CTRL_ARROWUP);
+    if (seql == strl) {
+      return KEY_CTRL_ARROWUP;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // CTRL + ARROW DOWN
-  strncpy(str, "\x1B\x5B\x31\x3B\x35\x42", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_CTRL_ARROW_DOWN;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_CTRL_ARROWDOWN, seql) == 0) {
+    strl = strlen(KEYSTRING_CTRL_ARROWDOWN);
+    if (seql == strl) {
+      return KEY_CTRL_ARROWDOWN;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // CTRL + ARROW LEFT
-  strncpy(str, "\x1B\x5B\x31\x3B\x35\x44", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_CTRL_ARROW_LEFT;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_CTRL_ARROWLEFT, seql) == 0) {
+    strl = strlen(KEYSTRING_CTRL_ARROWLEFT);
+    if (seql == strl) {
+      return KEY_CTRL_ARROWLEFT;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   // CTRL + ARROW RIGHT
-  strncpy(str, "\x1B\x5B\x31\x3B\x35\x43", AOS_SHELL_ESCSEQUENCE_LENGTH);
-  strl = strlen(str);
-  if (seql == strl && strncmp(seq, str, seql) == 0) {
-    return KEY_CTRL_ARROW_RIGHT;
-  } else if(seql < strl && strncmp(seq, str, seql) == 0) {
-    ambiguous = true;
+  if (strncmp(seq, KEYSTRING_CTRL_ARROWRIGHT, seql) == 0) {
+    strl = strlen(KEYSTRING_CTRL_ARROWRIGHT);
+    if (seql == strl) {
+      return KEY_CTRL_ARROWRIGHT;
+    } else if (seql < strl) {
+      ambiguous = true;
+    }
   }
 
   return ambiguous ? KEY_AMBIGUOUS : KEY_UNKNOWN;
 }
 
 /**
- * @brief   Move the cursor in the terminal
+ * @brief   Move the cursor in the terminal.
  *
  * @param[in] shell   Pointer to the shell object.
+ * @param[in] line    Pointer to the current content of the line.
  * @param[in] from    Starting position of the cursor.
  * @param[in] to      Target position to move the cursor to.
  *
  * @return            The number of positions moved.
  */
-static int _moveCursor(aos_shell_t* shell, const size_t from, const size_t to)
+static int _moveCursor(aos_shell_t* shell, const char* line, size_t from, size_t to)
 {
   aosDbgCheck(shell != NULL);
+  aosDbgCheck(line !=  NULL || from >= to);
+  aosDbgCheck(from <= shell->input.linewidth);
+  aosDbgCheck(to <= shell->input.linewidth);
 
   // local variables
   size_t pos = from;
@@ -544,7 +847,7 @@ static int _moveCursor(aos_shell_t* shell, const size_t from, const size_t to)
 
   // move cursor right by printing line content
   while (pos < to) {
-    streamPut(&shell->stream, (uint8_t)shell->input.line[pos]);
+    streamPut(&shell->stream, (uint8_t)line[pos]);
     ++pos;
   }
 
@@ -552,61 +855,111 @@ static int _moveCursor(aos_shell_t* shell, const size_t from, const size_t to)
 }
 
 /**
- * @brief   Print content of the shell line
+ * @brief   Print content of a given string to the shell output stream.
  *
  * @param[in] shell   Pointer to the shell object.
+ * @param[in] line    Pointer to the line to be printed.
  * @param[in] from    First position to start printing from.
  * @param[in] to      Position after the last character to print.
  *
  * @return            Number of characters printed.
  */
-static inline size_t _printLine(aos_shell_t* shell, const size_t from, const size_t to)
+static size_t _printString(aos_shell_t* shell, const char* line, size_t from, size_t to)
 {
   aosDbgCheck(shell != NULL);
+  aosDbgCheck(line != NULL || from >= to);
+  aosDbgCheck(from < shell->input.linewidth);
+  aosDbgCheck(to <= shell->input.linewidth);
 
   // local variables
   size_t cnt;
 
   for (cnt = 0; from + cnt < to; ++cnt) {
-    streamPut(&shell->stream, (uint8_t)shell->input.line[from + cnt]);
+    streamPut(&shell->stream, (uint8_t)line[from + cnt]);
   }
 
   return cnt;
 }
 
-static int _readChar(aos_shell_t* shell, const char c) {
+/**
+ * @brief   Print a single character to the input buffer and to the output stream.
+ *
+ * @param[in] shell   Pointer to the shell object.
+ * @param[in] rdata   Pointer to the runtim data object.
+ * @param[in] c       Character to print.
+ *
+ * @return  Number of successfully handled characters.
+ *          The return value can be interpreted as boolean (1 = sucess; 0 = error).
+ */
+static int _printChar(aos_shell_t* shell, runtimedata_t* rdata, char c)
+{
   aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
 
   // check whether input line is already full
-  if (shell->inputdata.lineend + 1 >= shell->input.size) {
+  if (rdata->input.length + 1 >= shell->input.linewidth) {
     return 0;
-  } else {
-    // clear old line content on first input
-    if (shell->inputdata.noinput) {
-      memset(shell->input.line, '\0', shell->input.size);
-      shell->inputdata.noinput = false;
-    }
-    // overwrite content
-    if (shell->config & AOS_SHELL_CONFIG_INPUT_OVERWRITE) {
-      shell->input.line[shell->inputdata.cursorpos] = c;
-      ++shell->inputdata.cursorpos;
-      shell->inputdata.lineend = (shell->inputdata.cursorpos > shell->inputdata.lineend) ? shell->inputdata.cursorpos : shell->inputdata.lineend;
-      streamPut(&shell->stream, (uint8_t)c);
-    }
-    // insert character
-    else {
-      memmove(&(shell->input.line[shell->inputdata.cursorpos+1]), &(shell->input.line[shell->inputdata.cursorpos]), shell->inputdata.lineend - shell->inputdata.cursorpos);
-      shell->input.line[shell->inputdata.cursorpos] = c;
-      ++shell->inputdata.lineend;
-      _printLine(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
-      ++shell->inputdata.cursorpos;
-      _moveCursor(shell, shell->inputdata.lineend, shell->inputdata.cursorpos);
-    }
+  }
+
+  // retreive entry in the input buffer
+  char* line = _getCurrentEntry(shell, rdata);
+
+  // overwrite content
+  if (shell->config & AOS_SHELL_CONFIG_INPUT_OVERWRITE) {
+    line[rdata->input.cursorpos] = c;
+    ++rdata->input.cursorpos;
+    rdata->input.length = (rdata->input.cursorpos > rdata->input.length) ? rdata->input.cursorpos : rdata->input.length;
+    streamPut(&shell->stream, (uint8_t)c);
+    return 1;
+  }
+  // insert character
+  else {
+    memmove(&line[rdata->input.cursorpos + 1], &line[rdata->input.cursorpos], rdata->input.length - rdata->input.cursorpos);
+    line[rdata->input.cursorpos] = c;
+    ++rdata->input.length;
+    _printString(shell, line, rdata->input.cursorpos, rdata->input.length);
+    ++rdata->input.cursorpos;
+    _moveCursor(shell, line, rdata->input.length, rdata->input.cursorpos);
     return 1;
   }
 }
 
 /**
+ * @brief   Overwrite the current output with a given line.
+ * @details If the current output is longer than the string, the additional characters are cleared.
+ *
+ * @param[in] shell   Pointer to a shell object.
+ * @param[in] rdata   Pointer to a runtime data object.
+ * @param[in] line    The line to be printed.
+ */
+static void _overwriteOutput(aos_shell_t* shell, runtimedata_t* rdata, const char* line)
+{
+  aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
+  aosDbgCheck(line != NULL);
+
+  // local variables
+  const size_t oldlength = rdata->input.length;
+
+  // print line (overwrite current output)
+  _moveCursor(shell, line, rdata->input.cursorpos, 0);
+  rdata->input.length = strlen(line);
+  _printString(shell, line, 0, rdata->input.length);
+
+  // clear any remaining symbols
+  if (oldlength > rdata->input.length) {
+    for (rdata->input.cursorpos = rdata->input.length; rdata->input.cursorpos < oldlength; ++rdata->input.cursorpos) {
+      streamPut(&shell->stream, ' ');
+    }
+    _moveCursor(shell, line, oldlength, rdata->input.length);
+  }
+
+  rdata->input.cursorpos = rdata->input.length;
+
+  return;
+}
+
+/**
  * @brief   Compare two characters.
  *
  * @param[in] lhs       First character to compare.
@@ -642,7 +995,7 @@ static inline charmatch_t _charcmp(char lhs, char rhs)
  *
  * @return    The customly encoded character.
  */
-static inline char _mapAscii2Custom(const char c)
+static inline char _mapAscii2Custom(char c)
 {
   if (c >= 'A' && c <= 'Z') {
     return ((c - 'A') * 2) + 'A' + 1;
@@ -712,291 +1065,449 @@ static int _strccmp(const char *str1, const char *str2, bool cs, size_t* n, char
 }
 
 /**
+ * @brief   Alters all intermediate NUL bytes in a string to spaces.
+ *
+ * @param[in] string  The string to be handled.
+ * @param[in] length  Length of the string.
+ *
+ * @return  Detected Length of the actual content of the string.
+ */
+static size_t _restoreWhitespace(char* string, size_t length)
+{
+  aosDbgCheck(string != NULL || length == 0);
+
+  // local variables
+  size_t c = length;
+
+  // seach for first non-NUL byte from the back
+  while (c > 0) {
+    --c;
+    if (string[c] != '\0') {
+      // store the detected length of the content
+      length = ++c;
+      break;
+    }
+  }
+
+  // iterate further and replace all encountered NUL bytes by spaces
+  while (c > 0) {
+    --c;
+    if (string[c] == '\0') {
+      string[c] = ' ';
+    }
+  }
+
+  return length;
+}
+
+/**
+ * @brief   Performs required actions before an imminent modiifcation (character input, deletion or autofill).
+ * @details This functions checks the current status and clears or copies entries in the input buffer as required.
+ *          Status information (runtime data) is altered accordingly as well.
+ *
+ * @param[in] shell   Pointer to a shell object.
+ * @param[in] rdata   Pointer to a runtime data object.
+ *
+ * @return    Pointer to the current entry in the input buffer.
+ */
+static char* _prepare4Modification(aos_shell_t* shell, runtimedata_t* rdata)
+{
+  aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
+
+  char* line = _getCurrentEntry(shell, rdata);
+
+  // cleared preview
+  if (rdata->buffer.selected == 0) {
+    // reset the current entry if required
+    if (rdata->buffer.edited != 0) {
+      memset(line, '\0', shell->input.linewidth * sizeof(char));
+    }
+    // set the current entry as the selected one and mark it as edited
+    rdata->buffer.selected = 1;
+    rdata->buffer.edited = 1;
+  }
+  // current entry
+  else if (rdata->buffer.selected == 1) {
+    // mark current entry as edited
+    rdata->buffer.edited = 1;
+  }
+  // preview of historic entry
+  else if (rdata->buffer.selected > 1) {
+    // copy the selected entry before modification if required
+    if (rdata->buffer.selected!= rdata->buffer.edited) {
+      memcpy(line, _getSelectedEntry(shell, rdata), shell->input.linewidth * sizeof(char));
+    }
+    // mark the selected entry as edited
+    rdata->buffer.edited = rdata->buffer.selected;
+  }
+
+  return line;
+}
+
+/**
  * @brief   Read input from a channel as long as there is data available.
  *
- * @param[in]   shell     Pointer to the shell object.
- * @param[in]   channel   The channel to read from.
- * @param[out]  exec      Optional pointer to a flag, which indicates, whether a command shall be executed.
+ * @param[in]     shell     Pointer to the shell object.
+ * @param[in,out] rdata     Pointer to a runtime data object.
+ * @param[in]     channel   The channel to read from.
  *
- * @return  Number of read characters.
+ * @return  Number of characters read.
  */
-static int _readChannel(aos_shell_t* shell, AosShellChannel* channel, bool* execute)
+static size_t _readChannel(aos_shell_t* shell, runtimedata_t* rdata, AosShellChannel* channel)
 {
   aosDbgCheck(shell != NULL);
+  aosDbgCheck(rdata != NULL);
   aosDbgCheck(channel != NULL);
 
   // local variables
-  aos_shellaction_t action = AOS_SHELL_ACTION_NONE;
+  size_t bytes = 0;
   char c;
   special_key_t key;
-  int nchars = 0;
-  bool exec = false;
+  action_t action;
 
   // read character by character from the channel
   while (chnReadTimeout(channel, (uint8_t*)&c, 1, TIME_IMMEDIATE)) {
-    key = KEY_UNKNOWN;
+    // increment byte counter
+    ++bytes;
 
-    // drop any input after an execution request was detected
-    if (exec) {
+    // drop any further input after an execution request was detected
+    if (rdata->lastaction == ACTION_EXECUTE && bytes > 1) {
       continue;
     }
 
-    // incremet character counter
-    ++nchars;
-
-    // parse escape sequence
-    if (strlen(shell->inputdata.escseq) > 0) {
-      shell->inputdata.escseq[strlen(shell->inputdata.escseq)] = c;
-      key = _interpreteEscapeSequence(shell->inputdata.escseq);
-      switch (key) {
-        case KEY_AMBIGUOUS:
-          // read next byte to resolve ambiguity
-          continue;
-        case KEY_UNKNOWN:
-          // do nothing here, but handle the unknown sequence below
-          break;
-        default:
-          // reset the sequence variable and buffer
-          memset(shell->inputdata.escseq, '\0', sizeof(shell->inputdata.escseq)*sizeof(shell->inputdata.escseq[0]));
-          break;
+    // try to interprete escape sequence
+    {
+      // set default
+      key = KEY_UNKNOWN;
+      // only interprete, if there is an escape sequence at all
+      const size_t escl = strlen(rdata->input.escseq);
+      if (escl > 0) {
+        // append and 'consume' character
+        rdata->input.escseq[escl] = c;
+        c = '\0';
+        // try to interprete sequence
+        key = _interpreteEscapeSequence(rdata->input.escseq);
+        switch (key) {
+          // ambiguous key due to incomplete sequence
+          case KEY_AMBIGUOUS:
+            // read next byte to resolve ambiguity
+            continue;
+          // an unknown sequence has been encountered
+          case KEY_UNKNOWN:
+            // increment number of inputs but handle this unknown sequence below
+            break;
+          // a key was identified successfully
+          default:
+            // reset the sequence buffer
+            memset(rdata->input.escseq, '\0', AOS_SHELL_ESCSEQUENCE_LENGTH * sizeof(char));
+            break;
+        }
       }
     }
 
-    /* interprete keys or character */
-    {
-      // default
-      action = AOS_SHELL_ACTION_NONE;
+    /*
+     * Derive action to be executed from keypress.
+     * This step handles all sanity checks, so any required prerequisites for the selected action are fulfilled.
+     */
+    // set default
+    action = ACTION_NONE;
+    // if there is no escape sequence pending
+    if (rdata->input.escseq[0] == '\0') {
 
       // printable character
-      if (key == KEY_UNKNOWN && strlen(shell->inputdata.escseq) == 0 && c >= '\x20' && c <= '\x7E') {
-        action = AOS_SHELL_ACTION_READCHAR;
+      if (c >= '\x20' && c <= '\x7E') {
+        action = ACTION_READCHAR;
       }
 
       // tab key or character
-      else if (key == KEY_TAB || c == '\x09') {
+      else if (c == '\x09' || key == KEY_TAB) {
         /*
          * pressing tab once applies auto fill
-         * pressing tab a second time prints suggestions
+         * pressing tab a second time (or more) prints suggestions
          */
-        if (shell->inputdata.lastaction == AOS_SHELL_ACTION_AUTOFILL || shell->inputdata.lastaction == AOS_SHELL_ACTION_SUGGEST) {
-          action = AOS_SHELL_ACTION_SUGGEST;
+        if (rdata->lastaction == ACTION_AUTOCOMPLETE || rdata->lastaction == ACTION_SUGGEST) {
+          action = ACTION_SUGGEST;
         } else {
-          action = AOS_SHELL_ACTION_AUTOFILL;
+          action = ACTION_AUTOCOMPLETE;
         }
       }
 
-      // INS key
-      else if (key == KEY_INSERT) {
-        action = AOS_SHELL_ACTION_INSERTTOGGLE;
-      }
-
-      // DEL key or character
-      else if (key == KEY_DELETE || c == '\x7F') {
-        // ignore if cursor is at very right
-        if (shell->inputdata.cursorpos < shell->inputdata.lineend) {
-          action = AOS_SHELL_ACTION_DELETEFORWARD;
-        }
+      // carriage return ('\r') or line feed ('\n') character
+      else if (c == '\x0D' || c == '\x0A') {
+        action = ACTION_EXECUTE;
       }
 
       // backspace key or character
-      else if (key == KEY_BACKSPACE || c == '\x08') {
+      else if (c == '\x08' || key == KEY_BACKSPACE) {
         // ignore if cursor is at very left
-        if (shell->inputdata.cursorpos > 0) {
-          action = AOS_SHELL_ACTION_DELETEBACKWARD;
+        if (rdata->input.cursorpos > 0) {
+          action = ACTION_DELETEBACKWARD;
         }
       }
 
-      // 'page up', 'arrow up', or key or CTRL + 'arrow up' key combination
-      else if (key == KEY_PAGE_UP || key == KEY_ARROW_UP || key == KEY_CTRL_ARROW_UP) {
-        // ignore if there was some input
-        if (shell->inputdata.noinput) {
-          action = AOS_SHELL_ACTION_RECALLLAST;
+      // DEL key or character
+      else if (c == '\x7F' || key == KEY_DELETE) {
+        // ignore if cursor is at very right
+        if (rdata->input.cursorpos < rdata->input.length) {
+          action = ACTION_DELETEFORWARD;
         }
       }
 
-      // 'page down' key, 'arrow down' key, 'end of test' character or 'end of transmission' character, or CTRL + 'arrow down' key combination
-      else if (key == KEY_PAGE_DOWN || key == KEY_ARROW_DOWN || c == '\x03' || c == '\x03' || key == KEY_CTRL_ARROW_DOWN) {
-        // ignore if line is empty
-        if (shell->inputdata.lineend > 0) {
-          action = AOS_SHELL_ACTION_CLEAR;
+      // 'arrow up' key
+      else if (key == KEY_ARROWUP) {
+        // recall previous input from history only if
+        // not the oldest entry is already selected and
+        // the previous entry has been set.
+        if (rdata->buffer.selected < shell->input.nentries &&
+            (_getRelativeEntry(shell, rdata, rdata->buffer.selected))[0] != INBUF_INIT_CHAR) {
+          action = ACTION_RECALLPREVIOUS;
         }
       }
 
-      // 'home' key
-      else if (key == KEY_HOME) {
-        // ignore if cursor is very left
-        if (shell->inputdata.cursorpos > 0) {
-          action = AOS_SHELL_ACTION_CURSOR2START;
+      // 'arrow down' key
+      else if (key == KEY_ARROWDOWN) {
+        // clear the line if
+        // no historic entry is selected or
+        // the most recent entry is selected, but the current one is occupied by a moodfied version of a historic entry
+        if ((rdata->buffer.selected == 1) ||
+            (rdata->buffer.selected == 2 && rdata->buffer.edited > 1)) {
+          action = ACTION_CLEAR;
         }
-      }
-
-      // 'end' key
-      else if (key == KEY_END) {
-        // ignore if cursos is very right
-        if (shell->inputdata.cursorpos < shell->inputdata.lineend) {
-          action = AOS_SHELL_ACTION_CURSOR2END;
+        // if a historic entry is selected, recall the next input from history
+        else if (rdata->buffer.selected > 1) {
+          action = ACTION_RECALLNEXT;
         }
       }
 
       // 'arrow left' key
-      else if (key == KEY_ARROW_LEFT) {
+      else if (key == KEY_ARROWLEFT) {
         // ignore if cursor is very left
-        if (shell->inputdata.cursorpos > 0) {
-          action = AOS_SHELL_ACTION_CURSORLEFT;
+        if (rdata->input.cursorpos > 0) {
+          action = ACTION_CURSORLEFT;
         }
       }
 
       // 'arrow right' key
-      else if (key == KEY_ARROW_RIGHT) {
+      else if (key == KEY_ARROWRIGHT) {
         // ignore if cursor is very right
-        if (shell->inputdata.cursorpos < shell->inputdata.lineend) {
-          action = AOS_SHELL_ACTION_CURSORRIGHT;
+        if (rdata->input.cursorpos < rdata->input.length) {
+          action = ACTION_CURSORRIGHT;
+        }
+      }
+
+      // CTRL + 'arrow up' key combination or 'page up' key
+      else if (key == KEY_CTRL_ARROWUP || key == KEY_PAGEUP) {
+        // recall oldest input from history only if
+        // not the oldest entry is already selected and
+        // there is at least one history entry set
+        if (rdata->buffer.selected < shell->input.nentries &&
+            (_getRelativeEntry(shell, rdata, (rdata->buffer.selected > 0) ? 1 : 0))[0] != INBUF_INIT_CHAR) {
+          action = ACTION_RECALLOLDEST;
+        }
+      }
+
+      // CTRL + 'arrow down' key combination or 'page down' key
+      else if (key == KEY_CTRL_ARROWDOWN || key == KEY_PAGEDOWN) {
+        // clear the line if
+        // no historic entry is selected or
+        // the most recent entry is selected, but the current one is occupied by a moodfied version of a historic entry
+        if ((rdata->buffer.selected == 1) ||
+            (rdata->buffer.selected > 1 && rdata->buffer.edited > 1)) {
+          action = ACTION_CLEAR;
+        }
+        // if a historic entry is selected, reset to the current input
+        else if (rdata->buffer.selected > 1) {
+          action = ACTION_RECALLCURRENT;
         }
       }
 
       // CTRL + 'arrow left' key combination
-      else if (key == KEY_CTRL_ARROW_LEFT) {
+      else if (key == KEY_CTRL_ARROWLEFT) {
         // ignore if cursor is very left
-        if (shell->inputdata.cursorpos > 0) {
-          action = AOS_SHELL_ACTION_CURSORWORDLEFT;
+        if (rdata->input.cursorpos > 0) {
+          action = ACTION_CURSORWORDLEFT;
         }
       }
 
       // CTRL + 'arrow right' key combination
-      else if (key == KEY_CTRL_ARROW_RIGHT) {
+      else if (key == KEY_CTRL_ARROWRIGHT) {
         // ignore if cursor is very right
-        if (shell->inputdata.cursorpos < shell->inputdata.lineend) {
-          action = AOS_SHELL_ACTION_CURSORWORDRIGHT;
+        if (rdata->input.cursorpos < rdata->input.length) {
+          action = ACTION_CURSORWORDRIGHT;
         }
       }
 
-      // carriage return ('\r') or line feed ('\n') character
-      else if (c == '\x0D' || c == '\x0A') {
-        action = AOS_SHELL_ACTION_EXECUTE;
+      // 'end' key
+      else if (key == KEY_END) {
+        // ignore if cursos is very right
+        if (rdata->input.cursorpos < rdata->input.length) {
+          action = ACTION_CURSOR2END;
+        }
       }
 
-      // ESC key or [ESCAPE] character
-      else if (key == KEY_ESCAPE || c == '\x1B') {
-        action = AOS_SHELL_ACTION_ESCSTART;
+      // 'home' key
+      else if (key == KEY_HOME) {
+        // ignore if cursor is very left
+        if (rdata->input.cursorpos > 0) {
+          action = ACTION_CURSOR2START;
+        }
       }
 
-      // unknown escape sequence
-      else if (key == KEY_UNKNOWN && strlen(shell->inputdata.escseq) > 0) {
-        action = AOS_SHELL_ACTION_PRINTUNKNOWNSEQUENCE;
+      // CTRL + C key combination
+      else if (c == '\x03' || key == KEY_CTRL_C) {
+        action = ACTION_RESET;
+      }
+
+      // INS key
+      else if (key == KEY_INSERT) {
+        action = ACTION_INSERTTOGGLE;
+      }
+
+      // ESC key or [ESCAPE] character
+      else if (c == '\x1B' || key == KEY_ESCAPE) {
+        action = ACTION_ESCSTART;
       }
     }
+    // ongoing escape sequence or interpretation failed
+    else /* if (rdata->input.escseq[0] != '\0') */ {
+      // unknown escape sequence (interpretation failed)
+      if (key == KEY_UNKNOWN) {
+        action = ACTION_PRINTUNKNOWNSEQUENCE;
+      }
+    } /* end of action selection */
 
-    /* handle function */
+    /*
+     * execute action
+     */
     switch (action) {
-      case AOS_SHELL_ACTION_READCHAR:
+      case ACTION_NONE:
+      {
+        // do nothing (ignore input) and read next byte
+        break;
+      }
+
+      case ACTION_READCHAR:
       {
-        if (_readChar(shell, c) == 0) {
+        char* line = _prepare4Modification(shell, rdata);
+        if (_printChar(shell, rdata, c) == 0) {
           // line is full
-          _moveCursor(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
+          _moveCursor(shell, line, rdata->input.cursorpos, rdata->input.length);
           chprintf((BaseSequentialStream*)&shell->stream, "\n\tmaximum line width reached\n");
           _printPrompt(shell);
-          _printLine(shell, 0, shell->inputdata.lineend);
-          _moveCursor(shell, shell->inputdata.lineend, shell->inputdata.cursorpos);
+          _printString(shell, line, 0, rdata->input.length);
+          _moveCursor(shell, line, rdata->input.length, rdata->input.cursorpos);
         }
         break;
       }
 
-      case AOS_SHELL_ACTION_AUTOFILL:
+      case ACTION_AUTOCOMPLETE:
       {
-        const char* fill = shell->input.line;
-        size_t cmatch = shell->inputdata.cursorpos;
+        // local variables
+        char* line = _getVisualisedEntry(shell, rdata);
+        const char* fill = line;
+        size_t cmatch = rdata->input.cursorpos;
         charmatch_t matchlevel = CHAR_MATCH_NOT;
         size_t n;
-        // iterate through command list
-        for (aos_shellcommand_t* cmd = shell->commands; cmd != NULL; cmd = cmd->next) {
-          // compare current match with command
-          n = cmatch;
-          charmatch_t mlvl = CHAR_MATCH_NOT;
-          _strccmp(fill, cmd->name, shell->config & AOS_SHELL_CONFIG_MATCH_CASE, (n == 0) ? NULL : &n, &mlvl);
-          const int cmp = (n < cmatch) ?
-                            ((int)n - (int)cmatch) :
-                            (cmd->name[n] != '\0') ?
-                              (int)strlen(cmd->name) - (int)n :
-                              0;
-          // if an exact match was found
-          if ((size_t)((int)cmatch + cmp) == shell->inputdata.cursorpos) {
-            cmatch = shell->inputdata.cursorpos;
-            fill = cmd->name;
-            // break the loop only if there are no case mismatches with the input
-            n = shell->inputdata.cursorpos;
-            _strccmp(fill, shell->input.line, false, &n, &mlvl);
-            if (mlvl == CHAR_MATCH_CASE) {
-              break;
-            }
-          }
-          // if a not exact match was found
-          else if ((size_t)((int)cmatch + cmp) > shell->inputdata.cursorpos) {
-            // if this is the first one
-            if (fill == shell->input.line) {
-              cmatch = (size_t)((int)cmatch + cmp);
+
+        // only execute autofill if the line is valid
+        if (line) {
+          _prepare4Modification(shell, rdata);
+
+          // iterate through command list
+          for (aos_shellcommand_t* cmd = shell->commands; cmd != NULL; cmd = cmd->next) {
+            // compare current match with command
+            n = cmatch;
+            charmatch_t mlvl = CHAR_MATCH_NOT;
+            _strccmp(fill, cmd->name, shell->config & AOS_SHELL_CONFIG_MATCH_CASE, (n == 0) ? NULL : &n, &mlvl);
+            const int cmp = (n < cmatch) ?
+                  ((int)n - (int)cmatch) :
+                  (cmd->name[n] != '\0') ?
+                    (int)strlen(cmd->name) - (int)n :
+                    0;
+            // if an exact match was found
+            if ((size_t)((int)cmatch + cmp) == rdata->input.cursorpos) {
+              cmatch = rdata->input.cursorpos;
               fill = cmd->name;
+              // break the loop only if there are no case mismatches with the input
+              n = rdata->input.cursorpos;
+              _strccmp(fill, line, false, &n, &mlvl);
+              if (mlvl == CHAR_MATCH_CASE) {
+                break;
+              }
             }
-            // if this is a worse one
-            else if ((cmp < 0) || (cmp == 0 && mlvl == CHAR_MATCH_CASE)) {
-              cmatch = (size_t)((int)cmatch + cmp);
+            // if a not exact match was found
+            else if ((size_t)((int)cmatch + cmp) > rdata->input.cursorpos) {
+              // if this is the first one
+              if (fill == line) {
+                cmatch = (size_t)((int)cmatch + cmp);
+                fill = cmd->name;
+              }
+              // if this is a worse one
+              else if ((cmp < 0) || (cmp == 0 && mlvl == CHAR_MATCH_CASE)) {
+                cmatch = (size_t)((int)cmatch + cmp);
+              }
             }
+            // non matching commands are ignored
+            else {}
           }
-          // non matching commands are ignored
-          else {}
-        }
-        // evaluate if there are case mismatches
-        n = cmatch;
-        _strccmp(shell->input.line, fill, shell->config & AOS_SHELL_CONFIG_MATCH_CASE, &n, &matchlevel);
-        // print the auto fill if any
-        if (cmatch > shell->inputdata.cursorpos || (cmatch == shell->inputdata.cursorpos && matchlevel == CHAR_MATCH_NCASE)) {
-          shell->inputdata.noinput = false;
-          // limit auto fill so it will not overflow the line width
-          if (shell->inputdata.lineend + (cmatch - shell->inputdata.cursorpos) > shell->input.size) {
-            cmatch = shell->input.size - shell->inputdata.lineend + shell->inputdata.cursorpos;
-          }
-          // move trailing memory further in the line
-          memmove(&(shell->input.line[cmatch]), &(shell->input.line[shell->inputdata.cursorpos]), shell->inputdata.lineend - shell->inputdata.cursorpos);
-          shell->inputdata.lineend += cmatch - shell->inputdata.cursorpos;
-          // if there was no incorrect case when matching
-          if (matchlevel == CHAR_MATCH_CASE) {
-            // insert fill command name to line
-            memcpy(&(shell->input.line[shell->inputdata.cursorpos]), &(fill[shell->inputdata.cursorpos]), cmatch - shell->inputdata.cursorpos);
-            // print the output
-            _printLine(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
-          } else {
-            // overwrite line with fill command name
-            memcpy(shell->input.line, fill, cmatch);
-            // reprint the whole line
-            _moveCursor(shell, shell->inputdata.cursorpos, 0);
-            _printLine(shell, 0, shell->inputdata.lineend);
+
+          // evaluate if there are case mismatches
+          n = cmatch;
+          _strccmp(line, fill, shell->config & AOS_SHELL_CONFIG_MATCH_CASE, &n, &matchlevel);
+          // print the auto fill if any
+          if (cmatch > rdata->input.cursorpos || (cmatch == rdata->input.cursorpos && matchlevel == CHAR_MATCH_NCASE)) {
+            // limit auto fill so it will not overflow the line width
+            if (rdata->input.length + (cmatch - rdata->input.cursorpos) > shell->input.linewidth) {
+              cmatch = shell->input.linewidth - rdata->input.length + rdata->input.cursorpos;
+            }
+            // move trailing memory further in the line
+            memmove(&line[cmatch], &line[rdata->input.cursorpos], (rdata->input.length - rdata->input.cursorpos) * sizeof(char));
+            rdata->input.length += cmatch - rdata->input.cursorpos;
+            // if there was no incorrect case when matching
+            if (matchlevel == CHAR_MATCH_CASE) {
+              // insert fill command name to line
+              memcpy(&line[rdata->input.cursorpos], &fill[rdata->input.cursorpos], (cmatch - rdata->input.cursorpos) * sizeof(char));
+              // print the output
+              _printString(shell, line, rdata->input.cursorpos, rdata->input.length);
+            } else {
+              // overwrite line with fill command name
+              memcpy(line, fill, cmatch * sizeof(char));
+              // reprint the whole line
+              _moveCursor(shell, line, rdata->input.cursorpos, 0);
+              _printString(shell, line, 0, rdata->input.length);
+            }
+            // move cursor to the end of the matching sequence
+            rdata->input.cursorpos = cmatch;
+            _moveCursor(shell, line, rdata->input.length, rdata->input.cursorpos);
           }
-          // move cursor to the end of the matching sequence
-          shell->inputdata.cursorpos = cmatch;
-          _moveCursor(shell, shell->inputdata.lineend, shell->inputdata.cursorpos);
         }
         break;
       }
 
-      case AOS_SHELL_ACTION_SUGGEST:
+      case ACTION_SUGGEST:
       {
+        // local variables
+        const char* line = _getVisualisedEntry(shell, rdata);
         unsigned int matches = 0;
+
         // iterate through command list
         for (aos_shellcommand_t* cmd = shell->commands; cmd != NULL; cmd = cmd->next) {
-          // compare line content with command, excpet if cursorpos=0
-          size_t i = shell->inputdata.cursorpos;
-          if (shell->inputdata.cursorpos > 0) {
-            _strccmp(shell->input.line, cmd->name, true, &i, NULL);
+          // compare line content with command, except if cursorpos is 0
+          size_t i = rdata->input.cursorpos;
+          if (rdata->input.cursorpos > 0) {
+            _strccmp(line, cmd->name, true, &i, NULL);
           }
-          const int cmp = (i < shell->inputdata.cursorpos) ?
-                            (int)(i - shell->inputdata.cursorpos) :
-                            (cmd->name[i] != '\0') ?
-                              (int)strlen(cmd->name) - (int)i :
-                              0;
+          const int cmp = (i < rdata->input.cursorpos) ?
+                ((int)i - (int)rdata->input.cursorpos) :
+                (cmd->name[i] != '\0') ?
+                  (int)strlen(cmd->name) - (int)i :
+                  0;
           // if a match was found
           if (cmp > 0) {
             // if this is the first one
             if (matches == 0) {
-              _moveCursor(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
+              _moveCursor(shell, line, rdata->input.cursorpos, rdata->input.length);
               streamPut(&shell->stream, '\n');
             }
             // print the command
@@ -1007,208 +1518,255 @@ static int _readChannel(aos_shell_t* shell, AosShellChannel* channel, bool* exec
         // reprint the prompt and line if any matches have been found
         if (matches > 0) {
           _printPrompt(shell);
-          _printLine(shell, 0, shell->inputdata.lineend);
-          _moveCursor(shell, shell->inputdata.lineend, shell->inputdata.cursorpos);
-          shell->inputdata.noinput = false;
+          _printString(shell, line, 0, rdata->input.length);
+          _moveCursor(shell, line, rdata->input.length, rdata->input.cursorpos);
         }
         break;
       }
 
-      case AOS_SHELL_ACTION_INSERTTOGGLE:
+      case ACTION_EXECUTE:
       {
-        if (shell->config & AOS_SHELL_CONFIG_INPUT_OVERWRITE) {
-          shell->config &= ~AOS_SHELL_CONFIG_INPUT_OVERWRITE;
-        } else {
-          shell->config |= AOS_SHELL_CONFIG_INPUT_OVERWRITE;
+        // if the input buffer can hold historic entries
+        if (shell->input.nentries > 1) {
+          _prepare4Modification(shell, rdata);
         }
         break;
       }
 
-      case AOS_SHELL_ACTION_DELETEFORWARD:
+      case ACTION_DELETEBACKWARD:
       {
-        --shell->inputdata.lineend;
-        memmove(&(shell->input.line[shell->inputdata.cursorpos]), &(shell->input.line[shell->inputdata.cursorpos+1]), shell->inputdata.lineend - shell->inputdata.cursorpos);
-        _printLine(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
+        char* line = _prepare4Modification(shell, rdata);
+        --rdata->input.cursorpos;
+        memmove(&line[rdata->input.cursorpos], &line[rdata->input.cursorpos + 1], (rdata->input.length - rdata->input.cursorpos) * sizeof(char));
+        --rdata->input.length;
+        line[rdata->input.length] = '\0';
+        _moveCursor(shell, line, rdata->input.cursorpos + 1, rdata->input.cursorpos);
+        _printString(shell, line, rdata->input.cursorpos, rdata->input.length);
         streamPut(&shell->stream, ' ');
-        _moveCursor(shell, shell->inputdata.lineend + 1, shell->inputdata.cursorpos);
+        _moveCursor(shell, line, rdata->input.length + 1, rdata->input.cursorpos);
         break;
       }
 
-      case AOS_SHELL_ACTION_DELETEBACKWARD:
+      case ACTION_DELETEFORWARD:
       {
-        --shell->inputdata.cursorpos;
-        memmove(&(shell->input.line[shell->inputdata.cursorpos]), &(shell->input.line[shell->inputdata.cursorpos+1]), shell->inputdata.lineend - shell->inputdata.cursorpos);
-        --shell->inputdata.lineend;
-        shell->input.line[shell->inputdata.lineend] = '\0';
-        _moveCursor(shell, shell->inputdata.cursorpos + 1, shell->inputdata.cursorpos);
-        _printLine(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
+        char* line = _prepare4Modification(shell, rdata);
+        --rdata->input.length;
+        memmove(&line[rdata->input.cursorpos], &line[rdata->input.cursorpos + 1], (rdata->input.length - rdata->input.cursorpos) * sizeof(char));
+        _printString(shell, line, rdata->input.cursorpos, rdata->input.length);
         streamPut(&shell->stream, ' ');
-        _moveCursor(shell, shell->inputdata.lineend+1, shell->inputdata.cursorpos);
+        _moveCursor(shell, line, rdata->input.length + 1, rdata->input.cursorpos);
         break;
       }
 
-      case AOS_SHELL_ACTION_RECALLLAST:
+      case ACTION_CLEAR:
       {
-        // replace any intermediate NUL bytes with spaces
-        shell->inputdata.lineend = 0;
-        size_t nul_start = 0;
-        size_t nul_end = 0;
-        // search line for a NUL byte
-        while (nul_start < shell->input.size) {
-          if (shell->input.line[nul_start] == '\0') {
-            nul_end = nul_start + 1;
-            // keep searcjing for a byte that is not NUL
-            while (nul_end < shell->input.size) {
-              if (shell->input.line[nul_end] != '\0') {
-                // an intermediate NUL sequence was found
-                memset(&(shell->input.line[nul_start]), ' ', nul_end - nul_start);
-                shell->inputdata.lineend = nul_end + 1;
-                break;
-              } else {
-                ++nul_end;
-              }
-            }
-            nul_start = nul_end + 1;
-          } else {
-            ++shell->inputdata.lineend;
-            ++nul_start;
-          }
+        // clear visualization
+        _moveCursor(shell, NULL, rdata->input.cursorpos, 0);
+        for (size_t cpos = 0; cpos < rdata->input.length; ++cpos) {
+          streamPut(&shell->stream, ' ');
         }
-        shell->inputdata.cursorpos = shell->inputdata.lineend;
-        // print the line
-        shell->inputdata.noinput = _printLine(shell, 0, shell->inputdata.lineend) == 0;
+        _moveCursor(shell, NULL, rdata->input.length, 0);
+
+        // update metadata
+        rdata->input.cursorpos = 0;
+        rdata->input.length = 0;
+        rdata->buffer.selected = 0;
+
         break;
       }
 
-      case AOS_SHELL_ACTION_CLEAR:
+      case ACTION_RECALLPREVIOUS:
       {
-        // clear output
-        _moveCursor(shell, shell->inputdata.cursorpos, 0);
-        for (shell->inputdata.cursorpos = 0; shell->inputdata.cursorpos < shell->inputdata.lineend; ++shell->inputdata.cursorpos) {
-          streamPut(&shell->stream, ' ');
+        // if the input was cleared but the current entry is occupied by a modified copy of a history entry, skip the current entry
+        if (rdata->buffer.selected == 0 && rdata->buffer.edited > 1) {
+          rdata->buffer.selected += 2;
+        } else {
+          ++rdata->buffer.selected;
         }
-        _moveCursor(shell, shell->inputdata.lineend, 0);
-        shell->inputdata.cursorpos = 0;
-        shell->inputdata.lineend = 0;
-        shell->inputdata.noinput = true;
+        _overwriteOutput(shell, rdata, _getVisualisedEntry(shell, rdata));
+        break;
+      }
+
+      case ACTION_RECALLNEXT:
+      {
+        --rdata->buffer.selected;
+        _overwriteOutput(shell, rdata, _getVisualisedEntry(shell, rdata));
         break;
       }
 
-      case AOS_SHELL_ACTION_CURSOR2START:
+      case ACTION_RECALLOLDEST:
       {
-        _moveCursor(shell, shell->inputdata.cursorpos, 0);
-        shell->inputdata.cursorpos = 0;
+        // find oldest valid entry
+        rdata->buffer.selected = shell->input.nentries;
+        while (_getSelectedEntry(shell, rdata)[0] == INBUF_INIT_CHAR) {
+          --rdata->buffer.selected;
+        }
+
+        _overwriteOutput(shell, rdata, _getVisualisedEntry(shell, rdata));
         break;
       }
 
-      case AOS_SHELL_ACTION_CURSOR2END:
+      case ACTION_RECALLCURRENT:
       {
-        _moveCursor(shell, shell->inputdata.cursorpos, shell->inputdata.lineend);
-        shell->inputdata.cursorpos = shell->inputdata.lineend;
+        rdata->buffer.selected = 1;
+        _overwriteOutput(shell, rdata, _getVisualisedEntry(shell, rdata));
         break;
       }
 
-      case AOS_SHELL_ACTION_CURSORLEFT:
+      case ACTION_CURSORLEFT:
       {
-        _moveCursor(shell, shell->inputdata.cursorpos, shell->inputdata.cursorpos-1);
-        --shell->inputdata.cursorpos;
+        _moveCursor(shell, NULL, rdata->input.cursorpos, rdata->input.cursorpos - 1);
+        --rdata->input.cursorpos;
         break;
       }
 
-      case AOS_SHELL_ACTION_CURSORRIGHT:
+      case ACTION_CURSORRIGHT:
       {
-        _moveCursor(shell, shell->inputdata.cursorpos, shell->inputdata.cursorpos+1);
-        ++shell->inputdata.cursorpos;
+        _moveCursor(shell, _getVisualisedEntry(shell, rdata), rdata->input.cursorpos, rdata->input.cursorpos + 1);
+        ++rdata->input.cursorpos;
         break;
       }
 
-      case AOS_SHELL_ACTION_CURSORWORDLEFT:
+      case ACTION_CURSORWORDLEFT:
       {
-        size_t cpos = shell->inputdata.cursorpos;
-        while (cpos > 0 && shell->input.line[cpos-1] == ' ') {
+        // local variables
+        const char* line = _getVisualisedEntry(shell, rdata);
+        size_t cpos = rdata->input.cursorpos;
+
+        // skip spaces
+        while (cpos > 0) {
           --cpos;
+          if (line[cpos] != ' ') {
+            break;
+          }
         }
-        while (cpos > 0 && shell->input.line[cpos-1] != ' ') {
+
+        // search for beginning of the word
+        while (cpos > 0) {
           --cpos;
+          if (line[cpos] == ' ') {
+            // go back to first character of the word
+            ++cpos;
+            break;
+          }
         }
-        _moveCursor(shell, shell->inputdata.cursorpos, cpos);
-        shell->inputdata.cursorpos = cpos;
+
+        // move the cursor and set metadata
+        _moveCursor(shell, line, rdata->input.cursorpos, cpos);
+        rdata->input.cursorpos = cpos;
+
         break;
       }
 
-      case AOS_SHELL_ACTION_CURSORWORDRIGHT:
+      case ACTION_CURSORWORDRIGHT:
       {
-        size_t cpos = shell->inputdata.cursorpos;
-        while (cpos < shell->inputdata.lineend && shell->input.line[cpos] != ' ') {
+        // local variables
+        const char* line = _getVisualisedEntry(shell, rdata);
+        size_t cpos = rdata->input.cursorpos;
+
+        // skip spaces
+        while (cpos < rdata->input.length && line[cpos] == ' ') {
           ++cpos;
         }
-        while (cpos < shell->inputdata.lineend && shell->input.line[cpos] == ' ') {
+
+        // search for end of the word
+        while (cpos < rdata->input.length && line[cpos] != ' ') {
           ++cpos;
         }
-        _moveCursor(shell, shell->inputdata.cursorpos, cpos);
-        shell->inputdata.cursorpos = cpos;
+
+        // move cursor and set metadata
+        _moveCursor(shell, line, rdata->input.cursorpos, cpos);
+        rdata->input.cursorpos = cpos;
+
         break;
       }
 
-      case AOS_SHELL_ACTION_EXECUTE:
+      case ACTION_CURSOR2END:
       {
-        streamPut(&shell->stream, '\n');
-        // if there was some input
-        if (!shell->inputdata.noinput) {
-          // fill the remainder of the line with NUL bytes
-          memset(&(shell->input.line[shell->inputdata.lineend]), '\0', shell->input.size - shell->inputdata.lineend);
-        }
-        // set the execution flag
-        exec = true;
+        _moveCursor(shell, _getVisualisedEntry(shell, rdata), rdata->input.cursorpos, rdata->input.length);
+        rdata->input.cursorpos = rdata->input.length;
         break;
       }
 
-      case AOS_SHELL_ACTION_ESCSTART:
+      case ACTION_CURSOR2START:
       {
-        shell->inputdata.escseq[0] = c;
+        _moveCursor(shell, _getVisualisedEntry(shell, rdata), rdata->input.cursorpos, 0);
+        rdata->input.cursorpos = 0;
         break;
       }
 
-      case AOS_SHELL_ACTION_PRINTUNKNOWNSEQUENCE:
+      case ACTION_RESET:
       {
-        size_t seqc = 1; // element 0 would be unprintible ESC character
-        while (shell->inputdata.escseq[seqc] != '\0') {
-          _readChar(shell, shell->inputdata.escseq[seqc]);
-          ++seqc;
+        // print reset indicator
+        chprintf((BaseSequentialStream*)&shell->stream, "^C");
+        if (!(shell->config & AOS_SHELL_CONFIG_INPUT_OVERWRITE)) {
+          _printString(shell, _getVisualisedEntry(shell, rdata), rdata->input.cursorpos, rdata->input.length);
         }
-        memset(shell->inputdata.escseq, '\0', sizeof(shell->inputdata.escseq)*sizeof(shell->inputdata.escseq[0]));
+        chprintf((BaseSequentialStream*)&shell->stream, "\n");
+
+        // reset buffers and metadata
+        if (rdata->buffer.edited != 0) {
+          memset(_getCurrentEntry(shell, rdata), '\0', shell->input.linewidth * sizeof(char));
+        }
+        rdata->input.length = 0;
+        rdata->input.cursorpos = 0;
+        rdata->buffer.selected = (shell->input.nentries > 1) ? 1 : 0;
+        rdata->buffer.edited = 0;
+
+        // print a new prompt
+        _printPrompt(shell);
+
         break;
       }
 
-      case AOS_SHELL_ACTION_NONE:
+      case ACTION_INSERTTOGGLE:
       {
-        // do nothing (ignore input) and read next byte
-        continue;
+        if (shell->config & AOS_SHELL_CONFIG_INPUT_OVERWRITE) {
+          shell->config &= ~AOS_SHELL_CONFIG_INPUT_OVERWRITE;
+        } else {
+          shell->config |= AOS_SHELL_CONFIG_INPUT_OVERWRITE;
+        }
+        break;
       }
-    } /* end of switch */
 
-    shell->inputdata.lastaction = action;
-  } /* end of while */
+      case ACTION_ESCSTART:
+      {
+        rdata->input.escseq[0] = c;
+        break;
+      }
 
-  // set the execution output flag
-  if (execute) {
-    *execute = exec;
-  }
+      case ACTION_PRINTUNKNOWNSEQUENCE:
+      {
+        _prepare4Modification(shell, rdata);
+        for (size_t seqc = 1; rdata->input.escseq[seqc] != '\0'; ++seqc) {
+          // element 0 would be unprintable ESC character
+          _printChar(shell, rdata, rdata->input.escseq[seqc]);
+        }
+        memset(rdata->input.escseq, '\0', AOS_SHELL_ESCSEQUENCE_LENGTH * sizeof(char));
+        break;
+      }
+    }
 
-  return nchars;
+    // update runtime data
+    rdata->lastaction = (action != ACTION_NONE) ? action : rdata->lastaction;
+  } /* end of while */
+
+  return bytes;
 }
 
 /**
- * @brief   Parses the content of the input buffer (line) to separate arguments.
+ * @brief   Parses the content of the given string to separate arguments.
  *
  * @param[in]   shell   Pointer to the shell object.
+ * @param[in]   str     String to be parsed.
  * @param[out]  argbuf  Buffer to store argument pointers to.
  *
  * @return            Number of arguments found.
  */
-static size_t _parseArguments(aos_shell_t* shell, char** argbuf)
+static size_t _parseArguments(aos_shell_t* shell, char* str, char** argbuf)
 {
   aosDbgCheck(shell != NULL);
+  aosDbgCheck(str != NULL);
   aosDbgCheck(argbuf != NULL);
 
   /*
@@ -1226,15 +1784,15 @@ static size_t _parseArguments(aos_shell_t* shell, char** argbuf)
   size_t nargs = 0;
 
   // iterate through the line
-  for (char* c = shell->input.line; c < shell->input.line + shell->input.size; ++c) {
+  for (size_t c = 0; c < shell->input.linewidth; ++c) {
     // terminate at first NUL byte
-    if (*c == '\0') {
+    if (str[c] == '\0') {
       state = END;
       break;
     }
     // spaces become NUL bytes
-    else if (*c == ' ') {
-      *c = '\0';
+    else if (str[c] == ' ') {
+      str[c] = '\0';
       state = SPACE;
     }
     // handle non-NUL bytes
@@ -1244,7 +1802,7 @@ static size_t _parseArguments(aos_shell_t* shell, char** argbuf)
         case SPACE:
           // ignore too many arguments
           if (nargs < shell->input.nargs) {
-            argbuf[nargs] = c;
+            argbuf[nargs] = &str[c];
           }
           ++nargs;
           break;
@@ -1271,40 +1829,37 @@ static size_t _parseArguments(aos_shell_t* shell, char** argbuf)
 /**
  * @brief   Initializes a shell object with the specified parameters.
  *
- * @param[in] shell         Pointer to the shell object.
- * @param[in] stream        I/O stream to use.
- * @param[in] prompt        Prompt line to print (NULL = use default prompt).
- * @param[in] line          Pointer to the input buffer.
- * @param[in] linesize      Size of the input buffer.
- * @param[in] numargs       Maximum number of arguments (defines size of internal buffer).
- */
-void aosShellInit(aos_shell_t* shell, event_source_t* oseventsource, const char* prompt, char* line, size_t linesize, size_t numargs)
+ * @param[in,out] shell           Pointer to the shell object to be initialized.
+ * @param[in]     prompt          Prompt line to print (NULL = use default prompt).
+ * @param[in]     inbuf           Two dimensional input buffer.
+ * @param[in]     entries         Number of entries in the input buffer (1st dimension).
+ * @param[in]     linewidth       Length of each entry in the input buffer (2nd dimension).
+ * @param[in]     numargs         Maximum number of arguments (defines size of internal buffer).
+ */
+void aosShellInit(aos_shell_t* shell, const char* prompt, char inbuf[], size_t entries, size_t linewidth, size_t numargs)
 {
   aosDbgCheck(shell != NULL);
-  aosDbgCheck(oseventsource != NULL);
-  aosDbgCheck(line != NULL);
+  aosDbgCheck(inbuf != NULL);
+  aosDbgCheck(entries > 0);
+  aosDbgCheck(linewidth > 0);
+  aosDbgCheck(numargs > 0);
 
   // set parameters
   shell->thread = NULL;
   chEvtObjectInit(&shell->eventSource);
-  shell->os.eventSource = oseventsource;
   aosShellStreamInit(&shell->stream);
   shell->prompt = prompt;
   shell->commands = NULL;
   shell->execstatus.command = NULL;
   shell->execstatus.retval = 0;
-  shell->input.line = line;
-  shell->input.length = linesize;
-  shell->input.nargs= numargs;
-  shell->inputdata.lastaction = AOS_SHELL_ACTION_NONE;
-  memset(shell->inputdata.escseq, '\0', sizeof(shell->inputdata.escseq)*sizeof(shell->inputdata.escseq[0]));
-  shell->inputdata.cursorpos = 0;
-  shell->inputdata.lineend = 0;
-  shell->inputdata.noinput = true;
+  shell->input.buffer = inbuf;
+  shell->input.nentries = entries;
+  shell->input.linewidth = linewidth;
+  shell->input.nargs = numargs;
   shell->config = 0x00;
 
   // initialize buffers
-  memset(shell->input.line, '\0', shell->input.length);
+  memset(shell->input.buffer, INBUF_INIT_CHAR, shell->input.nentries * shell->input.linewidth * sizeof(char));
 
   return;
 }
@@ -1604,26 +2159,35 @@ void aosShellChannelOutputDisable(AosShellChannel* channel)
 void aosShellThread(void* shell)
 {
   aosDbgCheck(shell != NULL);
+  aosDbgCheck(((aos_shell_t*)shell)->input.nentries > 0);
 
   // local variables
   eventmask_t eventmask;
   eventflags_t eventflags;
   AosShellChannel* channel;
-  bool execute;
+  runtimedata_t rdata;
   char* args[((aos_shell_t*)shell)->input.nargs];
   size_t nargs = 0;
   aos_shellcommand_t* cmd;
 
-  // initialize variables
+  // initialize variables and buffers
+  rdata.input.length = 0;
+  rdata.input.cursorpos = 0;
+  memset(rdata.input.escseq, '\0', AOS_SHELL_ESCSEQUENCE_LENGTH * sizeof(char));
+  rdata.buffer.current = 0;
+  rdata.buffer.selected = (((aos_shell_t*)shell)->input.nentries > 1) ? 1 : 0;
+  rdata.buffer.edited = 0;
+  rdata.lastaction = ACTION_NONE;
   for (size_t arg = 0; arg < ((aos_shell_t*)shell)->input.nargs; ++arg) {
     args[arg] = NULL;
   }
+  memset(_getCurrentEntry((aos_shell_t*)shell, &rdata), '\0', ((aos_shell_t*)shell)->input.linewidth * sizeof(char));
 
   // register OS related events
-  chEvtRegisterMask(((aos_shell_t*)shell)->os.eventSource, &(((aos_shell_t*)shell)->os.eventListener), AOS_SHELL_EVENTMASK_OS);
+  chEvtRegisterMask(&aos.events.os, &(((aos_shell_t*)shell)->osEventListener), EVENTMASK_OS);
   // register events to all input channels
   for (channel = ((aos_shell_t*)shell)->stream.channel; channel != NULL; channel = channel->next) {
-    chEvtRegisterMaskWithFlags(&(channel->asyncchannel->event), &(channel->listener), AOS_SHELL_EVENTMASK_INPUT, channel->listener.wflags);
+    chEvtRegisterMaskWithFlags(&(channel->asyncchannel->event), &(channel->listener), EVENTMASK_INPUT, channel->listener.wflags);
   }
 
   // fire start event
@@ -1641,9 +2205,9 @@ void aosShellThread(void* shell)
     switch (eventmask) {
 
       // OS related events
-      case AOS_SHELL_EVENTMASK_OS:
+      case EVENTMASK_OS:
       {
-        eventflags = chEvtGetAndClearFlags(&((aos_shell_t*)shell)->os.eventListener);
+        eventflags = chEvtGetAndClearFlags(&((aos_shell_t*)shell)->osEventListener);
         // handle shutdown/restart events
         if (eventflags & AOS_SYSTEM_EVENTFLAGS_SHUTDOWN_MASK) {
           chThdTerminate(((aos_shell_t*)shell)->thread);
@@ -1655,7 +2219,7 @@ void aosShellThread(void* shell)
       }
 
       // input events
-      case AOS_SHELL_EVENTMASK_INPUT:
+      case EVENTMASK_INPUT:
       {
         // check and handle all channels
         channel = ((aos_shell_t*)shell)->stream.channel;
@@ -1663,46 +2227,82 @@ void aosShellThread(void* shell)
           eventflags = chEvtGetAndClearFlags(&channel->listener);
           // if there is new input and a command shall be executed
           if (eventflags & CHN_INPUT_AVAILABLE) {
-            _readChannel((aos_shell_t*)shell, channel, &execute);
-            // an execution request was detected
-            if (execute && !((aos_shell_t*)shell)->inputdata.noinput) {
-              // parse input line to argument list
-              nargs = _parseArguments((aos_shell_t*)shell, args);
-              // check number of arguments
-              if (nargs > ((aos_shell_t*)shell)->input.nargs) {
-                // error too many arguments
-                chprintf((BaseSequentialStream*)&((aos_shell_t*)shell)->stream, "\ttoo many arguments\n");
-              } else if (nargs > 0) {
-                // search command list for arg[0] and execute callback
-                cmd = ((aos_shell_t*)shell)->commands;
-                while (cmd != NULL) {
-                  if (strcmp(args[0], cmd->name) == 0) {
-                    ((aos_shell_t*)shell)->execstatus.command = cmd;
-                    chEvtBroadcastFlags(&(((aos_shell_t*)shell)->eventSource), AOS_SHELL_EVTFLAG_EXEC);
-                    ((aos_shell_t*)shell)->execstatus.retval = cmd->callback((BaseSequentialStream*)&((aos_shell_t*)shell)->stream, (int)nargs, args);
-                    chEvtBroadcastFlags(&(((aos_shell_t*)shell)->eventSource), AOS_SHELL_EVTFLAG_DONE);
-                    // notify if the command was not successful
-                    if (((aos_shell_t*)shell)->execstatus.retval != 0) {
-                      chprintf((BaseSequentialStream*)&((aos_shell_t*)shell)->stream, "command returned exit status %d\n", ((aos_shell_t*)shell)->execstatus.retval);
+            _readChannel(shell, &rdata, channel);
+
+            // if an execution request was detected
+            if (rdata.lastaction == ACTION_EXECUTE) {
+              streamPut(&((aos_shell_t*)shell)->stream, '\n');
+              char* line = _getVisualisedEntry((aos_shell_t*)shell, &rdata);
+
+              // skip, if there is nothing to be executed
+              if (line) {
+                // parse arguments
+                nargs = _parseArguments(shell, line, args);
+                // check number of arguments found
+                if (nargs > ((aos_shell_t*)shell)->input.nargs) {
+                  chprintf((BaseSequentialStream*)&((aos_shell_t*)shell)->stream, "\ttoo many arguments\n");
+                } else if (nargs > 0) {
+                  // search command list for arg[0] and execute callback
+                  cmd = ((aos_shell_t*)shell)->commands;
+                  while (cmd != NULL) {
+                    // if the requested command has been found
+                    if (strcmp(args[0], cmd->name) == 0) {
+                      ((aos_shell_t*)shell)->execstatus.command = cmd;
+                      chEvtBroadcastFlags(&((aos_shell_t*)shell)->eventSource, AOS_SHELL_EVTFLAG_EXECUTE);
+                      ((aos_shell_t*)shell)->execstatus.retval = cmd->callback((BaseSequentialStream*)&((aos_shell_t*)shell)->stream, (int)nargs, args);
+                      chEvtBroadcastFlags(&((aos_shell_t*)shell)->eventSource, AOS_SHELL_EVTFLAG_DONE);
+                      // notify user if execution of the command was not successful
+                      if (((aos_shell_t*)shell)->execstatus.retval != 0) {
+                        chprintf((BaseSequentialStream*)&((aos_shell_t*)shell)->stream, "command returned exit status %d\n", ((aos_shell_t*)shell)->execstatus.retval);
+                      }
+                      break;
+                    }
+                    // keep searching
+                    else {
+                      cmd = cmd->next;
                     }
-                    break;
+                  } /* end of while */
+
+                  // if no matching command was found, print a message
+                  if (cmd == NULL) {
+                    chprintf((BaseSequentialStream*)&((aos_shell_t*)shell)->stream, "\tcommand '%s' not found\n", args[0]);
                   }
-                  cmd = cmd->next;
-                } /* end of while */
+                }
 
-                // if no matching command was found, print an error
-                if (cmd == NULL) {
-                  chprintf((BaseSequentialStream*)&((aos_shell_t*)shell)->stream, "\tcommand '%s' not found\n", args[0]);
+                // restore spaces in the current entry, which have been modified to NUL bytes due to argument parsing
+                _restoreWhitespace(line, rdata.input.length);
+
+                // update internat variables
+                {
+                  rdata.input.length = 0;
+                  rdata.input.cursorpos = 0;
+                  rdata.buffer.edited = 0;
+
+                  // if the input buffer can hold historic entries
+                  if (((aos_shell_t*)shell)->input.nentries > 1) {
+                    // iterate in the history only if
+                    // there was some valid input and
+                    // the user did not execute the exact previous command again
+                    if (nargs > 0 &&
+                        strcmp(line, _getRelativeEntry((aos_shell_t*)shell, &rdata, 1)) != 0) {
+                      rdata.buffer.current = (rdata.buffer.current + 1) % ((aos_shell_t*)shell)->input.nentries;
+                    }
+                    // clear and select next (now current) entry
+                    memset(_getCurrentEntry((aos_shell_t*)shell, &rdata), '\0', ((aos_shell_t*)shell)->input.linewidth * sizeof(char));
+                    rdata.buffer.selected = 1;
+                  }
+                  // if there is only a single entry in the input buffer
+                  else {
+                    // do not clear/reset the entry, but start off with a cleared preview
+                    rdata.buffer.selected = 0;
+                  }
                 }
               }
-            }
 
-            // reset some internal variables and print a new prompt
-            if (execute && !chThdShouldTerminateX()) {
-              ((aos_shell_t*)shell)->inputdata.cursorpos = 0;
-              ((aos_shell_t*)shell)->inputdata.lineend = 0;
-              ((aos_shell_t*)shell)->inputdata.noinput = true;
-              _printPrompt((aos_shell_t*)shell);
+              // print a new prompt
+              if (!chThdShouldTerminateX()) {
+                _printPrompt((aos_shell_t*)shell);
+              }
             }
           }
 
diff --git a/core/src/aos_system.c b/core/src/aos_system.c
index 038f3a4..f26497b 100644
--- a/core/src/aos_system.c
+++ b/core/src/aos_system.c
@@ -55,6 +55,15 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 #define SYSTEM_INFO_NAMEWIDTH         20
 
+#if (AMIROOS_CFG_SHELL_ENABLE == true) || defined(__DOXYGEN__)
+
+/**
+ * @brief   Number of entries in the system shell input buffer.
+ */
+#define SYSTEM_SHELL_BUFFERENTRIES    (1 + AMIROOS_CFG_SHELL_HISTLENGTH)
+
+#endif /* (AMIROOS_CFG_SHELL_ENABLE == true) */
+
 /******************************************************************************/
 /* EXPORTED VARIABLES                                                         */
 /******************************************************************************/
@@ -109,7 +118,7 @@ static THD_WORKING_AREA(_shell_wa, AMIROOS_CFG_SHELL_STACKSIZE);
 /**
  * @brief   Shell input buffer.
  */
-static char _shell_line[AMIROOS_CFG_SHELL_LINEWIDTH];
+static char _shell_buffer[SYSTEM_SHELL_BUFFERENTRIES * AMIROOS_CFG_SHELL_LINEWIDTH];
 
 /**
  * @brief   Shell command to retrieve system information.
@@ -507,8 +516,9 @@ static int _shellcmd_infocb(BaseSequentialStream* stream, int argc, char* argv[]
   // print shell info
   chprintf(stream, "System shell information:\n");
   chprintf(stream, "\tcommands available:     %u\n", aosShellCountCommands(&aos.shell));
-  chprintf(stream, "\tline width:             %u characters\n", aos.shell.input.length);
+  chprintf(stream, "\tinput width:            %u characters\n", aos.shell.input.linewidth);
   chprintf(stream, "\tmaximum arguments:      %u\n", aos.shell.input.nargs);
+  chprintf(stream, "\thistory size:           %u\n", aos.shell.input.nentries - 1);
 #if (AMIROOS_CFG_DBG == true)
   chprintf(stream, "\tthread stack size:      %u bytes\n", aosThdGetStacksize(aos.shell.thread));
 #if (CH_DBG_FILL_THREADS == TRUE)
@@ -709,6 +719,8 @@ size_t _numActiveThreads(void)
  */
 void aosSysInit(const char* shellPrompt)
 {
+  aosDbgCheck(shellPrompt != NULL || !AMIROOS_CFG_SHELL_ENABLE);
+
   /* set control thread to maximum priority */
   chThdSetPriority(AOS_THD_CTRLPRIO);
 
@@ -738,9 +750,9 @@ void aosSysInit(const char* shellPrompt)
 
   /* init shell */
   aosShellInit(&aos.shell,
-               &aos.events.os,
                shellPrompt,
-               _shell_line,
+               _shell_buffer,
+               SYSTEM_SHELL_BUFFERENTRIES,
                AMIROOS_CFG_SHELL_LINEWIDTH,
                AMIROOS_CFG_SHELL_MAXARGS);
   // add system commands
diff --git a/modules/DiWheelDrive_1-1/Makefile b/modules/DiWheelDrive_1-1/Makefile
index 1d6f2a7..bdf5c5a 100644
--- a/modules/DiWheelDrive_1-1/Makefile
+++ b/modules/DiWheelDrive_1-1/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/DiWheelDrive_1-1/aosconf.h b/modules/DiWheelDrive_1-1/aosconf.h
index ba96e60..be160f8 100644
--- a/modules/DiWheelDrive_1-1/aosconf.h
+++ b/modules/DiWheelDrive_1-1/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/DiWheelDrive_1-2/Makefile b/modules/DiWheelDrive_1-2/Makefile
index 09d4038..1ce9164 100644
--- a/modules/DiWheelDrive_1-2/Makefile
+++ b/modules/DiWheelDrive_1-2/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/DiWheelDrive_1-2/aosconf.h b/modules/DiWheelDrive_1-2/aosconf.h
index 8bb97df..73246cc 100644
--- a/modules/DiWheelDrive_1-2/aosconf.h
+++ b/modules/DiWheelDrive_1-2/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/LightRing_1-0/Makefile b/modules/LightRing_1-0/Makefile
index 6557859..3cb02ec 100644
--- a/modules/LightRing_1-0/Makefile
+++ b/modules/LightRing_1-0/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/LightRing_1-0/aosconf.h b/modules/LightRing_1-0/aosconf.h
index 747bb25..973e80a 100644
--- a/modules/LightRing_1-0/aosconf.h
+++ b/modules/LightRing_1-0/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/LightRing_1-2/Makefile b/modules/LightRing_1-2/Makefile
index 0d20422..8a901ad 100644
--- a/modules/LightRing_1-2/Makefile
+++ b/modules/LightRing_1-2/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/LightRing_1-2/aosconf.h b/modules/LightRing_1-2/aosconf.h
index 66eddd5..885581e 100644
--- a/modules/LightRing_1-2/aosconf.h
+++ b/modules/LightRing_1-2/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/NUCLEO-F103RB/Makefile b/modules/NUCLEO-F103RB/Makefile
index 121c5b1..6c37e02 100644
--- a/modules/NUCLEO-F103RB/Makefile
+++ b/modules/NUCLEO-F103RB/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/NUCLEO-F103RB/aosconf.h b/modules/NUCLEO-F103RB/aosconf.h
index 605f402..34b0122 100644
--- a/modules/NUCLEO-F103RB/aosconf.h
+++ b/modules/NUCLEO-F103RB/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/NUCLEO-F401RE/Makefile b/modules/NUCLEO-F401RE/Makefile
index a21eb75..8eedcb9 100644
--- a/modules/NUCLEO-F401RE/Makefile
+++ b/modules/NUCLEO-F401RE/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/NUCLEO-F401RE/aosconf.h b/modules/NUCLEO-F401RE/aosconf.h
index b0729a7..7c69db1 100644
--- a/modules/NUCLEO-F401RE/aosconf.h
+++ b/modules/NUCLEO-F401RE/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/NUCLEO-F767ZI/Makefile b/modules/NUCLEO-F767ZI/Makefile
index c00cef9..269dac4 100644
--- a/modules/NUCLEO-F767ZI/Makefile
+++ b/modules/NUCLEO-F767ZI/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/NUCLEO-F767ZI/aosconf.h b/modules/NUCLEO-F767ZI/aosconf.h
index dec9743..80cae03 100644
--- a/modules/NUCLEO-F767ZI/aosconf.h
+++ b/modules/NUCLEO-F767ZI/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           128
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -289,11 +289,21 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum number of arguments.
  */
 #if !defined(OS_CFG_SHELL_MAXARGS)
-  #define AMIROOS_CFG_SHELL_MAXARGS             16
+  #define AMIROOS_CFG_SHELL_MAXARGS             8
 #else /* !defined(OS_CFG_SHELL_MAXARGS) */
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/NUCLEO-L476RG/Makefile b/modules/NUCLEO-L476RG/Makefile
index 5e547e6..d850e2e 100644
--- a/modules/NUCLEO-L476RG/Makefile
+++ b/modules/NUCLEO-L476RG/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/NUCLEO-L476RG/aosconf.h b/modules/NUCLEO-L476RG/aosconf.h
index 47031f4..16664e4 100644
--- a/modules/NUCLEO-L476RG/aosconf.h
+++ b/modules/NUCLEO-L476RG/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/PowerManagement_1-1/Makefile b/modules/PowerManagement_1-1/Makefile
index c02d07c..81986bf 100644
--- a/modules/PowerManagement_1-1/Makefile
+++ b/modules/PowerManagement_1-1/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/PowerManagement_1-1/aosconf.h b/modules/PowerManagement_1-1/aosconf.h
index 9e53959..68b1234 100644
--- a/modules/PowerManagement_1-1/aosconf.h
+++ b/modules/PowerManagement_1-1/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/PowerManagement_1-2/Makefile b/modules/PowerManagement_1-2/Makefile
index d526a51..02f355d 100644
--- a/modules/PowerManagement_1-2/Makefile
+++ b/modules/PowerManagement_1-2/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/PowerManagement_1-2/aosconf.h b/modules/PowerManagement_1-2/aosconf.h
index 0b1fa46..7de2ac8 100644
--- a/modules/PowerManagement_1-2/aosconf.h
+++ b/modules/PowerManagement_1-2/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
diff --git a/modules/STM32F407G-DISC1/Makefile b/modules/STM32F407G-DISC1/Makefile
index 93d5659..d341c74 100644
--- a/modules/STM32F407G-DISC1/Makefile
+++ b/modules/STM32F407G-DISC1/Makefile
@@ -35,7 +35,7 @@ endif
 
 # C specific options here (added to USE_OPT).
 ifeq ($(USE_COPT),)
-  USE_COPT = -std=c17
+  USE_COPT = -std=c99 -fshort-enums
 endif
 
 # C++ specific options here (added to USE_OPT).
diff --git a/modules/STM32F407G-DISC1/aosconf.h b/modules/STM32F407G-DISC1/aosconf.h
index a7258c7..0a6f799 100644
--- a/modules/STM32F407G-DISC1/aosconf.h
+++ b/modules/STM32F407G-DISC1/aosconf.h
@@ -280,7 +280,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * @brief   Shell maximum input line length.
  */
 #if !defined(OS_CFG_SHELL_LINEWIDTH)
-  #define AMIROOS_CFG_SHELL_LINEWIDTH           80
+  #define AMIROOS_CFG_SHELL_LINEWIDTH           64
 #else /* !defined(OS_CFG_SHELL_LINEWIDTH) */
   #define AMIROOS_CFG_SHELL_LINEWIDTH           OS_CFG_SHELL_LINEWIDTH
 #endif /* !defined(OS_CFG_SHELL_LINEWIDTH) */
@@ -294,6 +294,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   #define AMIROOS_CFG_SHELL_MAXARGS             OS_CFG_SHELL_MAXARGS
 #endif /* !defined(OS_CFG_SHELL_MAXARGS) */
 
+/**
+ * @brief   Shell number of history entries.
+ * @details A value of 0 disables shell history.
+ */
+#if !defined(OS_CFG_SHELL_HISTLENGTH)
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          3
+#else /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+  #define AMIROOS_CFG_SHELL_HISTLENGTH          OS_CFG_SHELL_HISTLENGTH
+#endif /* !defined(OS_CFG_SHELL_HISTLENGTH) */
+
 /** @} */
 
 #endif /* AOSCONF_H */
