/* ###################################################################
**     This component module is generated by Processor Expert. Do not modify it.
**     Filename    : McuShell.h
**     Project     : FRDM-K64F_Generator
**     Processor   : MK64FN1M0VLL12
**     Component   : Shell
**     Version     : Component 01.111, Driver 01.00, CPU db: 3.00.000
**     Compiler    : GNU C Compiler
**     Date/Time   : 2021-12-26, 08:30, # CodeGen: 765
**     Abstract    :
**         Module implementing a command line shell.
**     Settings    :
**          Component name                                 : McuShell
**          Echo                                           : no
**          Prompt                                         : "CMD> "
**          Project Name                                   : My Project Name
**          Silent Mode Prefix                             : #
**          Buffer Size                                    : 48
**          Blocking Send                                  : Enabled
**            Wait                                         : McuWait
**            Timeout (ms)                                 : 20
**            Wait Time (ms)                               : 5
**            RTOS Wait                                    : yes
**          Status Colon Pos                               : 13
**          Help Semicolon Pos                             : 26
**          Multi Command                                  : Disabled
**          Utility                                        : McuUtility
**          Default Serial                                 : Disabled
**          Semaphore                                      : no
**          Critical Section                               : McuCriticalSection
**          History                                        : no
**          Kinetis SDK                                    : McuLib
**     Contents    :
**         PrintPrompt                     - void McuShell_PrintPrompt(McuShell_ConstStdIOType *io);
**         SendNum8u                       - void McuShell_SendNum8u(uint8_t val, McuShell_StdIO_OutErr_FctType io);
**         SendNum8s                       - void McuShell_SendNum8s(int8_t val, McuShell_StdIO_OutErr_FctType io);
**         SendNum16u                      - void McuShell_SendNum16u(uint16_t val, McuShell_StdIO_OutErr_FctType io);
**         SendNum16s                      - void McuShell_SendNum16s(int16_t val, McuShell_StdIO_OutErr_FctType io);
**         SendNum32u                      - void McuShell_SendNum32u(uint32_t val, McuShell_StdIO_OutErr_FctType io);
**         SendNum32s                      - void McuShell_SendNum32s(int32_t val, McuShell_StdIO_OutErr_FctType io);
**         SendCh                          - void McuShell_SendCh(uint8_t ch, McuShell_StdIO_OutErr_FctType io);
**         SendStr                         - void McuShell_SendStr(const uint8_t *str, McuShell_StdIO_OutErr_FctType io);
**         PrintMemory                     - uint8_t McuShell_PrintMemory(void *hndl, uint32_t startAddr, uint32_t...
**         printfIO                        - unsigned McuShell_printfIO(McuShell_ConstStdIOType *io, const char *fmt, ...);
**         printf                          - unsigned McuShell_printf(const char *fmt, ...);
**         SendData                        - void McuShell_SendData(const uint8_t *data, uint16_t dataSize,...
**         PrintStatus                     - uint8_t McuShell_PrintStatus(McuShell_ConstStdIOType *io);
**         ParseCommand                    - uint8_t McuShell_ParseCommand(const uint8_t *cmd, bool *handled,...
**         IsHistoryCharacter              - bool McuShell_IsHistoryCharacter(uint8_t ch, uint8_t *cmdBuf, size_t...
**         ReadLine                        - bool McuShell_ReadLine(uint8_t *bufStart, uint8_t *buf, size_t bufSize,...
**         PrintCommandFailed              - void McuShell_PrintCommandFailed(const uint8_t *cmd, McuShell_ConstStdIOType...
**         IterateTable                    - uint8_t McuShell_IterateTable(const uint8_t *cmd, bool *handled,...
**         SetStdio                        - uint8_t McuShell_SetStdio(McuShell_ConstStdIOTypePtr stdio);
**         GetStdio                        - McuShell_ConstStdIOTypePtr McuShell_GetStdio(void);
**         RequestSerial                   - void McuShell_RequestSerial(void);
**         ReleaseSerial                   - void McuShell_ReleaseSerial(void);
**         ReadAndParseWithCommandTableExt - uint8_t McuShell_ReadAndParseWithCommandTableExt(uint8_t *cmdBuf, size_t...
**         ReadCommandLine                 - uint8_t McuShell_ReadCommandLine(uint8_t *cmdBuf, size_t cmdBufSize,...
**         ReadAndParseWithCommandTable    - uint8_t McuShell_ReadAndParseWithCommandTable(uint8_t *cmdBuf, size_t...
**         ParseWithCommandTableExt        - uint8_t McuShell_ParseWithCommandTableExt(const uint8_t *cmd,...
**         ParseWithCommandTable           - uint8_t McuShell_ParseWithCommandTable(const uint8_t *cmd,...
**         GetSemaphore                    - void* McuShell_GetSemaphore(void);
**         SendStatusStr                   - void McuShell_SendStatusStr(const uint8_t *strItem, const uint8_t *strStatus,...
**         SendHelpStr                     - void McuShell_SendHelpStr(const uint8_t *strCmd, const uint8_t *strHelp,...
**         ReadChar                        - void McuShell_ReadChar(uint8_t *c);
**         SendChar                        - void McuShell_SendChar(uint8_t ch);
**         KeyPressed                      - bool McuShell_KeyPressed(void);
**         SendCharFct                     - void McuShell_SendCharFct(uint8_t ch, uint8_t (*fct)(uint8_t ch));
**         Init                            - void McuShell_Init(void);
**         Deinit                          - void McuShell_Deinit(void);
**
** * Copyright (c) 2014-2021, Erich Styger
**  * Web:         https://mcuoneclipse.com
**  * SourceForge: https://sourceforge.net/projects/mcuoneclipse
**  * Git:         https://github.com/ErichStyger/McuOnEclipse_PEx
**  * All rights reserved.
**  *
**  * Redistribution and use in source and binary forms, with or without modification,
**  * are permitted provided that the following conditions are met:
**  *
**  * - Redistributions of source code must retain the above copyright notice, this list
**  *   of conditions and the following disclaimer.
**  *
**  * - Redistributions in binary form must reproduce the above copyright notice, this
**  *   list of conditions and the following disclaimer in the documentation and/or
**  *   other materials provided with the distribution.
**  *
**  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
**  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
**  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
**  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
**  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
**  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
**  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
**  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
**  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
**  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
** ###################################################################*/
/*!
** @file McuShell.h
** @version 01.00
** @brief
**         Module implementing a command line shell.
*/         
/*!
**  @addtogroup McuShell_module McuShell module documentation
**  @{
*/         

/* MODULE McuShell. */
#include <ctype.h> /* for isalnum*/

#include "McuShell.h"
#include "McuXFormat.h"
#include "McuUtility.h"
#include "McuCriticalSection.h"
#include "McuWait.h"

#if McuShell_DEFAULT_SERIAL
  #include McuShell_CONFIG_DEFAULT_SERIAL_INCLUDE
#endif


uint8_t McuShell_DefaultShellBuffer[McuShell_DEFAULT_SHELL_BUFFER_SIZE]; /* default buffer which can be used by the application */
#if McuShell_CONFIG_HISTORY_ENABLED
  static uint8_t McuShell_history[McuShell_CONFIG_HISTORY_NOF_ITEMS][McuShell_CONFIG_HISTORY_ITEM_LENGTH]; /* History buffers */
  static uint8_t McuShell_history_index = 0; /* Selected command */
#endif
#if McuShell_CONFIG_ECHO_ENABLED
  static bool McuShell_EchoEnabled = TRUE;
#endif

#if McuShell_CONFIG_USE_MUTEX
  #include "FreeRTOS.h"
  #include "task.h"
  #include "semphr.h"
#endif

#ifdef __HC08__
  #pragma MESSAGE DISABLE C3303 /* implicit concatenation of strings */
#endif
#if McuShell_CONFIG_USE_MUTEX
  static SemaphoreHandle_t ShellSem = NULL; /* Semaphore to protect shell SCI access */
#endif

#if McuShell_DEFAULT_SERIAL
  McuShell_ConstStdIOType McuShell_stdio =
  {
    (McuShell_StdIO_In_FctType)McuShell_ReadChar, /* stdin */
    (McuShell_StdIO_OutErr_FctType)McuShell_SendChar, /* stdout */
    (McuShell_StdIO_OutErr_FctType)McuShell_SendChar, /* stderr */
    McuShell_KeyPressed /* if input is not empty */
  };
  static McuShell_ConstStdIOType *McuShell_currStdIO = &McuShell_stdio;
#else
  static McuShell_ConstStdIOType *McuShell_currStdIO = NULL; /* needs to be set through McuShell_SetStdio(); */
#endif
/* Internal method prototypes */
static void SendSeparatedStrings(const uint8_t *strA, const uint8_t *strB, uint8_t tabChar, uint8_t tabPos, McuShell_StdIO_OutErr_FctType io);

/*
** ===================================================================
**     Method      :  SendCh (component Shell)
**
**     Description :
**         Prints a character using an I/O function
**     Parameters  :
**         NAME            - DESCRIPTION
**         ch              - Character to send
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendCh(uint8_t ch, McuShell_StdIO_OutErr_FctType io)
{
  if (io==NULL) {
    return;
  }
  io(ch);
}

/*
** ===================================================================
**     Method      :  SendStr (component Shell)
**
**     Description :
**         Prints a string using an I/O function
**     Parameters  :
**         NAME            - DESCRIPTION
**       * str             - Pointer to string (zero terminated) to be
**                           printed.
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
/*!
 * \brief Prints a string using I/O callbacks
 * \param[in] str String (zero terminated) to be printed
 * \param[in] io I/O function to be used for printing
 */
void McuShell_SendStr(const uint8_t *str, McuShell_StdIO_OutErr_FctType io)
{
  if (io==NULL) {
    return;
  }
  while(*str!='\0') {
    io(*str++);
  }
}

/*
** ===================================================================
**     Method      :  SendNum32s (component Shell)
**
**     Description :
**         Sends a 32bit signed number to the given I/O
**     Parameters  :
**         NAME            - DESCRIPTION
**         val             - number to print
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendNum32s(int32_t val, McuShell_StdIO_OutErr_FctType io)
{
  unsigned char buf[sizeof("-1234567890")];

  McuUtility_Num32sToStr(buf, sizeof(buf), val);
  McuShell_SendStr(buf, io);
}

/*
** ===================================================================
**     Method      :  SendNum32u (component Shell)
**
**     Description :
**         Sends a 32bit unsigned number to the given I/O
**     Parameters  :
**         NAME            - DESCRIPTION
**         val             - number to print
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendNum32u(uint32_t val, McuShell_StdIO_OutErr_FctType io)
{
  unsigned char buf[sizeof("1234567890")];

  McuUtility_Num32uToStr(buf, sizeof(buf), val);
  McuShell_SendStr(buf, io);
}

/*
** ===================================================================
**     Method      :  SendNum16s (component Shell)
**
**     Description :
**         Sends a 16bit signed number to the given I/O
**     Parameters  :
**         NAME            - DESCRIPTION
**         val             - number to print
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendNum16s(int16_t val, McuShell_StdIO_OutErr_FctType io)
{
  unsigned char buf[sizeof("-12345")];

  McuUtility_Num16sToStr(buf, sizeof(buf), val);
  McuShell_SendStr(buf, io);
}

/*
** ===================================================================
**     Method      :  SendNum16u (component Shell)
**
**     Description :
**         Sends a 16bit unsigned number to the given I/O
**     Parameters  :
**         NAME            - DESCRIPTION
**         val             - number to print
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendNum16u(uint16_t val, McuShell_StdIO_OutErr_FctType io)
{
  unsigned char buf[sizeof("12345")];

  McuUtility_Num16uToStr(buf, sizeof(buf), val);
  McuShell_SendStr(buf, io);
}

/*
** ===================================================================
**     Method      :  SendNum8u (component Shell)
**
**     Description :
**         Sends an 8bit unsigned number to the given I/O
**     Parameters  :
**         NAME            - DESCRIPTION
**         val             - number to print
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendNum8u(uint8_t val, McuShell_StdIO_OutErr_FctType io)
{
  unsigned char buf[sizeof("123")];

  McuUtility_Num8uToStr(buf, sizeof(buf), val);
  McuShell_SendStr(buf, io);
}

/*
** ===================================================================
**     Method      :  SendNum8s (component Shell)
**
**     Description :
**         Sends an 8bit signed number to the given I/O
**     Parameters  :
**         NAME            - DESCRIPTION
**         val             - number to print
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendNum8s(int8_t val, McuShell_StdIO_OutErr_FctType io)
{
  unsigned char buf[sizeof("-123")];

  McuUtility_Num8sToStr(buf, sizeof(buf), val);
  McuShell_SendStr(buf, io);
}

/*
** ===================================================================
**     Method      :  ParseCommand (component Shell)
**
**     Description :
**         Parses a shell command. Use 'help' to get a list of
**         supported commands.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmd             - Pointer to command string
**       * handled         - Pointer to variable to indicate if
**                           the command has been handled. The caller
**                           passes this variable to the command scanner
**                           to find out if the passed command has been
**                           handled. The variable is initialized by the
**                           caller.
**       * io              - Pointer to I/O callbacks
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_ParseCommand(const uint8_t *cmd, bool *handled, McuShell_ConstStdIOType *io)
{
  if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "McuShell help")==0) {
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    McuShell_SendStr((unsigned char*)McuShell_DASH_LINE, io->stdOut);
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    McuShell_SendStr((unsigned char*)McuShell_CONFIG_PROJECT_NAME_STRING, io->stdOut);
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    McuShell_SendStr((unsigned char*)McuShell_DASH_LINE, io->stdOut);
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    McuShell_SendHelpStr((unsigned char*)"McuShell", (const unsigned char*)"Group of McuShell commands\r\n", io->stdOut);
    McuShell_SendHelpStr((unsigned char*)"  help|status", (const unsigned char*)"Print help or status information\r\n", io->stdOut);
#if McuShell_CONFIG_ECHO_ENABLED
    McuShell_SendHelpStr((unsigned char*)"  echo (on|off)", (const unsigned char*)"Turn echo on or off\r\n", io->stdOut);
#endif
    *handled = TRUE;
    return ERR_OK;
#if McuShell_CONFIG_ECHO_ENABLED
  } else if ((McuUtility_strcmp((char*)cmd, "McuShell echo on")==0)) {
    *handled = TRUE;
    McuShell_EchoEnabled = TRUE;
    return ERR_OK;
  } else if ((McuUtility_strcmp((char*)cmd, "McuShell echo off")==0)) {
    *handled = TRUE;
    McuShell_EchoEnabled = FALSE;
    return ERR_OK;
#endif
  } else if ((McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0) || (McuUtility_strcmp((char*)cmd, "McuShell status")==0)) {
    *handled = TRUE;
    return McuShell_PrintStatus(io);
  }
  return ERR_OK; /* no error */
}

/*
** ===================================================================
**     Method      :  PrintPrompt (component Shell)
**
**     Description :
**         Prints the prompt to the stdOut channel
**     Parameters  :
**         NAME            - DESCRIPTION
**       * io              - Pointer to IO to be used
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_PrintPrompt(McuShell_ConstStdIOType *io)
{
  McuShell_SendStr((unsigned char*)"\r\n", io->stdOut); /* ensure that there is a new line */
  McuShell_SendStr((unsigned char*)McuShell_CONFIG_PROMPT_STRING, io->stdOut);
}

/*
** ===================================================================
**     Method      :  IsHistoryCharacter (component Shell)
**
**     Description :
**         Returns TRUE if character is a history character
**     Parameters  :
**         NAME            - DESCRIPTION
**         ch              - current command character
**       * cmdBuf          - Pointer to command line buffer read
**                           so far
**         cmdBufIdx       - Index of character into cmdBuf
**       * isPrev          - Pointer to return value, if it is
**                           'previous' history or not
**     Returns     :
**         ---             - TRUE if it is an accepted history character
** ===================================================================
*/
bool McuShell_IsHistoryCharacter(uint8_t ch, uint8_t *cmdBuf, size_t cmdBufIdx, bool *isPrev)
{
  *isPrev = FALSE;
#if McuShell_CONFIG_HISTORY_ENABLED
  if (   cmdBufIdx==0 /* first character on command line */
      || (McuUtility_strcmp((const char*)cmdBuf, (const char*)McuShell_history[McuShell_history_index])==0) /* pressing prev/next character on previous history element */
      )
  {
    if (ch==McuShell_CONFIG_HISTORY_CHAR_PREV) {
      *isPrev = TRUE;
      return TRUE;
    } else if (ch==McuShell_CONFIG_HISTORY_CHAR_NEXT) {
      *isPrev = FALSE;
      return TRUE;
    }
  }
#if 0
  if (cmdBufIdx==0 || cmdBufIdx==2) { /* accept only first character or sequence as history sequence */
    if (cmdBufIdx==2 && cmdBuf[0]==0x1b && cmdBuf[1]==0x5b) {
      /* up:    0x27 0x5b 0x41
       * down:  0x27 0x5b 0x42
       * right: 0x27 0x5b 0x43
       * left:  0x27 0x5b 0x44
       */
      if (cmdBuf[2]==0x41 /* up */ || cmdBuf[2]==0x44 /* left */) {
        *isPrev = TRUE;
        return TRUE;
      } else if (cmdBuf[2]==0x42 /* down */ || cmdBuf[2]==0x43 /* right */) {
        *isPrev = FALSE;
        return TRUE;
      }
    }
    /* NYI: handle TAB and SHIFT-TAB */
  }
#endif
#else
  (void)ch; /* not used */
  (void)cmdBuf; /* not used */
  (void)cmdBufIdx; /* not used */
#endif
  return FALSE;
}

/*
** ===================================================================
**     Method      :  ReadLine (component Shell)
**
**     Description :
**         Reads a line from stdIn and returns TRUE if we have a line,
**         FALSE otherwise.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * bufStart        - Pointer to start of buffer
**       * buf             - Pointer to buffer where to read in the
**                           information
**         bufSize         - size of buffer
**       * io              - Pointer to I/O callbacks
**     Returns     :
**         ---             - TRUE if something has been read, FALSE
**                           otherwise
** ===================================================================
*/
bool McuShell_ReadLine(uint8_t *bufStart, uint8_t *buf, size_t bufSize, McuShell_ConstStdIOType *io)
{
  uint8_t c;
  bool isBackwardHistory;

  if (io->keyPressed()) {
    for(;;) {                          /* while not '\r' or '\n' */
      c = '\0';                        /* initialize character */
      io->stdIn(&c);                   /* read character */
      if (c=='\0') { /* nothing in rx buffer? Something is wrong... */
        break; /* get out of loop */
      }
      if (c=='\b' || c=='\177') {      /* check for backspace */
        if (buf > bufStart) {          /* Avoid buffer underflow */
#if McuShell_CONFIG_ECHO_ENABLED
           if (McuShell_EchoEnabled && io->echoEnabled) {
             io->stdOut('\b');         /* delete character on terminal */
             io->stdOut(' ');
             io->stdOut('\b');
           }
#endif
           buf--;                      /* delete last character in buffer */
           *buf = '\0';
           bufSize++;
        }
      } else if (McuShell_IsHistoryCharacter(c, bufStart, (size_t)(buf-bufStart), &isBackwardHistory)) {
#if McuShell_CONFIG_HISTORY_ENABLED
        uint8_t cBuf[3]={'\0','\0','\0'}, cBufIdx = 0;
        bool prevInHistory;
#endif

        while (c!='\0') {              /* empty the rx buffer (escape sequence) */
#if McuShell_CONFIG_HISTORY_ENABLED
           cBuf[cBufIdx] = c;
           cBufIdx++;
           if (cBufIdx==sizeof(cBuf)) {
             cBufIdx = 0; /* ring buffer */
           }
#endif
           c = '\0';                   /* initialize character */
           io->stdIn(&c);              /* read character */
        }
#if McuShell_CONFIG_HISTORY_ENABLED
        /* if not an alphanumeric switch to history  */
        prevInHistory = cBufIdx==0 && cBuf[0]==0x1b && cBuf[1]==0x5b && (cBuf[2]==0x41 /*up*/ || cBuf[2]==0x44 /*left*/);
        /* up:    0x27 0x5b 0x41
         * down:  0x27 0x5b 0x42
         * right: 0x27 0x5b 0x43
         * left:  0x27 0x5b 0x44
         */
        if (prevInHistory) {
          McuUtility_strcpy(bufStart, McuShell_CONFIG_HISTORY_ITEM_LENGTH, McuShell_history[McuShell_history_index]);
          McuShell_history_index++;    /* update the index */
          if (McuShell_history_index==McuShell_CONFIG_HISTORY_NOF_ITEMS) {
            McuShell_history_index = 0;
          }
        } else {
          if (McuShell_history_index==0) {
            McuShell_history_index = (McuShell_CONFIG_HISTORY_NOF_ITEMS-1);
          } else {
            McuShell_history_index--;
          }
          McuUtility_strcpy(bufStart, McuShell_CONFIG_HISTORY_ITEM_LENGTH, McuShell_history[McuShell_history_index]);
        }
        bufSize = bufSize + buf - bufStart - McuUtility_strlen((const char*)bufStart); /* update the buffer */
        buf = bufStart + McuUtility_strlen((const char*)bufStart);
#endif
#if McuShell_CONFIG_ECHO_ENABLED
        if (McuShell_EchoEnabled && io->echoEnabled) {
          McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
          McuShell_PrintPrompt(io);
          McuShell_SendStr(bufStart, io->stdOut);
        }
#endif
      } else {
#if McuShell_CONFIG_ECHO_ENABLED
        if (McuShell_EchoEnabled && io->echoEnabled) {
  #if McuLib_CONFIG_CPU_IS_ESP32
          if (c=='\r') { /* idf.py monitor uses '\r' for '\n'? */
            c = '\n';
          }
  #endif
          io->stdOut(c);               /* echo character */
        }
#endif
        *buf = (uint8_t)c;             /* append character to the string */
        buf++;
        bufSize--;
        if ((c=='\r') || (c=='\n')) {
#if McuShell_CONFIG_ECHO_ENABLED
          if (McuShell_EchoEnabled && io->echoEnabled) {
            #if McuLib_CONFIG_CPU_IS_ESP32
            McuShell_SendStr((unsigned char*)" \n", io->stdOut); /* for ESP32 idf.py monitor it uses '\r' at the end, plus we need a space */
            #else
            McuShell_SendStr((unsigned char*)"\n", io->stdOut);
            #endif
          }
#endif
#if McuShell_CONFIG_HISTORY_ENABLED
          if ((bufStart[0] != '\0') && (bufStart[0] != '\r') && (bufStart[0] != '\n')) {
            int i;

            for(i=McuShell_CONFIG_HISTORY_NOF_ITEMS-1; i>0;i--) {
              McuUtility_strcpy(McuShell_history[i], McuShell_CONFIG_HISTORY_ITEM_LENGTH, McuShell_history[i-1]); /* move previous commands */
            }
            McuShell_history_index = 0; /* update the history with the current command */
            McuUtility_strcpy(McuShell_history[0], McuShell_CONFIG_HISTORY_ITEM_LENGTH, bufStart); /* add the current command to the history */
            if (buf-bufStart <= McuShell_CONFIG_HISTORY_ITEM_LENGTH) { /* size check */
              McuShell_history[0][buf-bufStart-1] = '\0';
            } else {
              McuShell_history[0][McuShell_CONFIG_HISTORY_ITEM_LENGTH-1] = '\0';
            }
          }
#endif
          break;
        }
        if (bufSize <= 1) {            /* buffer full */
          break;
        }
      }
    } /* for */
    *buf = '\0';                       /* zero terminate string */
    return TRUE;
  } else {
    return FALSE;
  }
}

/*
** ===================================================================
**     Method      :  PrintStatus (component Shell)
**
**     Description :
**         Prints various available system status information
**     Parameters  :
**         NAME            - DESCRIPTION
**       * io              - Pointer to I/O callbacks
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_PrintStatus(McuShell_ConstStdIOType *io)
{
  unsigned char buf[16];

  McuShell_SendStatusStr((const unsigned char*)"McuShell", (const unsigned char*)"Commandline shell status\r\n", io->stdOut);
  McuShell_SendStatusStr((const unsigned char*)"  Build", (const unsigned char*)__DATE__, io->stdOut);
  McuShell_SendStr((unsigned char*)" ", io->stdOut);
  McuShell_SendStr((unsigned char*)__TIME__, io->stdOut);
  McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
#if McuShell_CONFIG_ECHO_ENABLED
  McuShell_SendStatusStr((const unsigned char*)"  echo", McuShell_EchoEnabled?(const unsigned char*)"On\r\n":(const unsigned char*)"Off\r\n", io->stdOut);
#endif

  buf[0] = '\0';
  if (McuShell_CONFIG_SILENT_PREFIX_CHAR!=McuShell_NO_SILENT_PREFIX_CHAR) {
    McuUtility_chcat(buf, sizeof(buf), McuShell_CONFIG_SILENT_PREFIX_CHAR);
    McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
  } else {
    McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"none\r\n");
  }
  McuShell_SendStatusStr((const unsigned char*)"  silent", buf, io->stdOut);
#if McuShell_CONFIG_MULTI_CMD_ENABLED
  McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"yes: '");
  McuUtility_chcat(buf, sizeof(buf), McuShell_CONFIG_MULTI_CMD_CHAR);
  McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"'\r\n");
  McuShell_SendStatusStr((const unsigned char*)"  multiCmd", buf, io->stdOut);
#else
  McuShell_SendStatusStr((const unsigned char*)"  multiCmd", (unsigned char*)"no\r\n", io->stdOut);
#endif

  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  PrintCommandFailed (component Shell)
**
**     Description :
**         Prints a standard message for failed or unknown commands
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmd             - Pointer to command which was failing
**       * io              - Pointer to I/O callbacks
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_PrintCommandFailed(const uint8_t *cmd, McuShell_ConstStdIOType *io)
{
  McuShell_SendStr((unsigned char*)"*** Failed or unknown command: ", io->stdErr);
  McuShell_SendStr(cmd, io->stdErr);
  McuShell_SendStr((unsigned char*)"\r\n", io->stdErr);
  McuShell_SendStr((unsigned char*)"*** Type ", io->stdErr);
  McuShell_SendStr((unsigned char*)McuShell_CMD_HELP, io->stdErr);
  McuShell_SendStr((unsigned char*)" to get a list of available commands\r\n", io->stdErr);
}

/*
** ===================================================================
**     Method      :  IterateTable (component Shell)
**
**     Description :
**         Parses a shell command. It handles first the internal
**         commands and will call the provided callback.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmd             - Pointer to command string
**       * handled         - Pointer to boolean which is set to
**                           TRUE if a command parser has handled the
**                           command.
**       * io              - Pointer to I/O callbacks
**       * parserTable     - Pointer to callback which
**                           will be called to parse commands in the
**                           user application, or NULL if not used.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_IterateTable(const uint8_t *cmd, bool *handled, McuShell_ConstStdIOType *io, McuShell_ConstParseCommandCallback *parserTable)
{
  uint8_t res = ERR_OK;

  if (parserTable==NULL) { /* no table??? */
    return ERR_FAILED;
  }
  if (io==NULL) { /* no IO handler??? */
    return ERR_FAILED;
  }
  /* iterate through all parser functions in table */
  while(*parserTable!=NULL) {
    if ((*parserTable)(cmd, handled, io)!=ERR_OK) {
      res = ERR_FAILED;
    }
    parserTable++;
  }
  return res;
}

/*
** ===================================================================
**     Method      :  ParseWithCommandTableExt (component Shell)
**
**     Description :
**         Parses a shell command. It handles first the internal
**         commands and will call the provided callback.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmd             - Pointer to command string
**       * io              - Pointer to I/O callbacks
**       * parseCallback   - Pointer to callback
**                           which will be called to parse commands in
**                           the user application, or NULL if not used.
**         silent          - If handling shall be silent, i.e. no
**                           command prompt printed
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_ParseWithCommandTableExt(const uint8_t *cmd, McuShell_ConstStdIOType *io, McuShell_ConstParseCommandCallback *parseCallback, bool silent)
{
  uint8_t res = ERR_OK;
  bool handled;
#if McuShell_SILENT_PREFIX_CHAR_ENABLED
  bool silentPrefix = FALSE;
#endif
#if McuShell_CONFIG_MULTI_CMD_ENABLED
  uint8_t buf[McuShell_CONFIG_MULTI_CMD_SIZE];
  uint8_t i;
  bool parseBuffer, finished;
  bool insideDoubleQuotes = FALSE; /* with multi-commands: allow the McuShell_CONFIG_MULTI_CMD_CHAR inside double quoted strings */
#endif

  if (io==NULL) { /* no I/O handler? */
    return ERR_FAILED;
  }
  if (*cmd=='\0') { /* empty command */
    return ERR_OK;
  }
#if McuShell_CONFIG_MULTI_CMD_ENABLED
  parseBuffer = FALSE;
  finished = FALSE;
  i = 0;
  for(;;) { /* breaks */
    if (i>sizeof(buf)-2) {
      res = ERR_FAILED;
      McuShell_PrintCommandFailed(buf, io);
      break; /* buffer overflow */
    }
    buf[i] = *cmd;
    if (buf[i]=='"') {
      if (insideDoubleQuotes) { /* already had a double quote? */
        insideDoubleQuotes = FALSE; /* note: we do not support nested double quotes */
      } else {
        insideDoubleQuotes = TRUE; /* mark that we are inside double quotes */
      }
    }
    cmd++; i++;
  #if McuShell_SILENT_PREFIX_CHAR_ENABLED
    if (i==1 && buf[0]==McuShell_CONFIG_SILENT_PREFIX_CHAR) { /* first character is silent character */
      silentPrefix |= (bool)(buf[0]==McuShell_CONFIG_SILENT_PREFIX_CHAR);
      buf[0] = *cmd; /* skip silent character */
      cmd++;
    }
  #endif
    if (!insideDoubleQuotes && buf[i-1] == McuShell_CONFIG_MULTI_CMD_CHAR) { /* found separator, but not inside double quoted string */
      buf[i-1] = '\0';
      parseBuffer = TRUE;
    } else if (buf[i-1]=='\0') {
      parseBuffer = TRUE;
      finished = TRUE;
    }
    if (parseBuffer) {
      handled = FALSE;
      res = McuShell_IterateTable(buf, &handled, io, parseCallback); /* iterate through all parser functions in table */
      if (!handled || res!=ERR_OK) { /* no handler has handled the command, or error? */
        McuShell_PrintCommandFailed(buf, io);
        res = ERR_FAILED;
      }
      parseBuffer = FALSE;
      i = 0; /* restart */
    }
    if (finished) {
      break; /* get out of loop */
    }
  } /* for */
#else
  #if McuShell_SILENT_PREFIX_CHAR_ENABLED
  silentPrefix = (bool)(*cmd==McuShell_CONFIG_SILENT_PREFIX_CHAR);
  if (silentPrefix) {
    cmd++; /* skip silent character */
  }
  #endif
  handled = FALSE;
  res = McuShell_IterateTable(cmd, &handled, io, parseCallback); /* iterate through all parser functions in table */
  if (!handled || res!=ERR_OK) { /* no handler has handled the command? */
    McuShell_PrintCommandFailed(cmd, io);
    res = ERR_FAILED;
  }
#endif
#if McuShell_SILENT_PREFIX_CHAR_ENABLED
  if (!silentPrefix && !silent) {
    McuShell_PrintPrompt(io);
  }
#else
  if (!silent) {
    McuShell_PrintPrompt(io);
  }
#endif
  return res;
}

/*
** ===================================================================
**     Method      :  ParseWithCommandTable (component Shell)
**
**     Description :
**         Parses a shell command. It handles first the internal
**         commands and will call the provided callback.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmd             - Pointer to command string
**       * io              - Pointer to I/O callbacks
**       * parseCallback   - Pointer to callback
**                           which will be called to parse commands in
**                           the user application, or NULL if not used.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_ParseWithCommandTable(const uint8_t *cmd, McuShell_ConstStdIOType *io, McuShell_ConstParseCommandCallback *parseCallback)
{
  return McuShell_ParseWithCommandTableExt(cmd, io, parseCallback, FALSE);
}

/*
** ===================================================================
**     Method      :  SetStdio (component Shell)
**
**     Description :
**         Sets an StdIO structure which is returned by GetStdio()
**     Parameters  :
**         NAME            - DESCRIPTION
**         stdio           - New stdio structure to be used.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_SetStdio(McuShell_ConstStdIOTypePtr stdio)
{
  McuShell_currStdIO = stdio;
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  GetStdio (component Shell)
**
**     Description :
**         Returns the default stdio channel. This method is only
**         available if a shell is enabled in the component properties.
**     Parameters  : None
**     Returns     :
**         ---             - Pointer to the stdio descriptor
** ===================================================================
*/
McuShell_ConstStdIOTypePtr McuShell_GetStdio(void)
{
  return McuShell_currStdIO;
}

/*
** ===================================================================
**     Method      :  ReadAndParseWithCommandTableExt (component Shell)
**
**     Description :
**         Reads characters from the default input channel and appends
**         it to the buffer. Once a new line has been detected, the
**         line will be parsed using the handlers in the table.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmdBuf          - Pointer to buffer provided by the
**                           caller where to store the command to read
**                           in. Characters will be appended, so make
**                           sure string buffer is initialized with a
**                           zero byte at the beginning.
**         cmdBufSize      - Size of buffer
**       * io              - Pointer to I/O channels to be used
**       * parseCallback   - Pointer to callback
**                           table provided by the user application to
**                           parse commands. The table has a NULL
**                           sentinel.
**         silent          - If handling shall be silent, i.e. no
**                           command prompt printed
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_ReadAndParseWithCommandTableExt(uint8_t *cmdBuf, size_t cmdBufSize, McuShell_ConstStdIOType *io, McuShell_ConstParseCommandCallback *parseCallback, bool silent)
{
  /* IMPORTANT NOTE: this function *appends* to the buffer, so the buffer needs to be initialized first! */
  uint8_t res = ERR_OK;
  size_t len;

  if (io==NULL) { /* no I/O handler? */
    return ERR_FAILED;
  }
  len = McuUtility_strlen((const char*)cmdBuf);
  if (McuShell_ReadLine(cmdBuf, cmdBuf+len, cmdBufSize-len, io)) {
    len = McuUtility_strlen((const char*)cmdBuf); /* length of buffer string */
    if (len==0) { /* error case */
      return ERR_FAILED;
    } else if (len==1 && (cmdBuf[0]=='\n' || cmdBuf[0]=='\r')) { /* eat preceding newline characters */
      cmdBuf[0] = '\0';
    }
    if (len>=cmdBufSize-1) {           /* buffer overflow? Parse what we have, will be likely return an error */
      (void)McuShell_ParseWithCommandTableExt(cmdBuf, io, parseCallback, silent);
      cmdBuf[0] = '\0'; /* start again */
      res = ERR_OVERFLOW;
    } else if (cmdBuf[len-1]=='\n' || cmdBuf[len-1]=='\r') { /* line end: parse command */
      cmdBuf[len-1] = '\0';            /* remove line end character for parser */
      res = McuShell_ParseWithCommandTableExt(cmdBuf, io, parseCallback, silent);
      cmdBuf[0] = '\0';                /* start again */
    } else {
      /* continue to append to buffer */
    }
  }
  return res;
}

/*
** ===================================================================
**     Method      :  ReadCommandLine (component Shell)
**
**     Description :
**         Similar to ReadAndParseWithCommandTableExt, but does not
**         call the parser. Reads characters from the default input
**         channel and appends it to the buffer. Once a new line has
**         been detected, it  removes it and returns ERR_OK
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmdBuf          - Pointer to buffer provided by the
**                           caller where to store the command to read
**                           in. Characters will be appended, so make
**                           sure string buffer is initialized with a
**                           zero byte at the beginning.
**         cmdBufSize      - Size of buffer
**       * io              - Pointer to I/O channels to be used
**     Returns     :
**         ---             - Error code, ERR_OK if a complete line has
**                           been detected
** ===================================================================
*/
uint8_t McuShell_ReadCommandLine(uint8_t *cmdBuf, size_t cmdBufSize, McuShell_ConstStdIOType *io)
{
  /* IMPORTANT NOTE: this function *appends* to the buffer, so the buffer needs to be initialized first! */
  size_t len;

  if (io==NULL) { /* no I/O handler? */
    return ERR_FAILED;
  }
  len = McuUtility_strlen((const char*)cmdBuf);
  if (McuShell_ReadLine(cmdBuf, cmdBuf+len, cmdBufSize-len, io)) {
    len = McuUtility_strlen((const char*)cmdBuf); /* length of buffer string */
    if (len==0) { /* error case */
      return ERR_FAILED;
    } else if (len==1 && (cmdBuf[0]=='\n' || cmdBuf[0]=='\r')) { /* eat preceding newline characters */
      cmdBuf[0] = '\0';
    }
    if (len>=cmdBufSize-1) {           /* buffer overflow? Parse what we have, will be likely return an error */
      cmdBuf[0] = '\0'; /* start again */
      return ERR_OVERFLOW;
    } else if (cmdBuf[len-1]=='\n' || cmdBuf[len-1]=='\r') { /* line end: parse command */
      cmdBuf[len-1] = '\0';            /* remove line end character for parser */
      return ERR_OK;
    } else {
      /* continue to append to buffer */
    }
  }
  return ERR_BUSY;
}

/*
** ===================================================================
**     Method      :  ReadAndParseWithCommandTable (component Shell)
**
**     Description :
**         Reads characters from the default input channel and appends
**         it to the buffer. Once a new line has been detected, the
**         line will be parsed using the handlers in the table.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmdBuf          - Pointer to buffer provided by the
**                           caller where to store the command to read
**                           in. Characters will be appended, so make
**                           sure string buffer is initialized with a
**                           zero byte at the beginning.
**         cmdBufSize      - Size of buffer
**       * io              - Pointer to I/O channels to be used
**       * parseCallback   - Pointer to callback
**                           table provided by the user application to
**                           parse commands. The table has a NULL
**                           sentinel.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_ReadAndParseWithCommandTable(uint8_t *cmdBuf, size_t cmdBufSize, McuShell_ConstStdIOType *io, McuShell_ConstParseCommandCallback *parseCallback)
{
  return McuShell_ReadAndParseWithCommandTableExt(cmdBuf, cmdBufSize, io, parseCallback, FALSE);
}

/*
** ===================================================================
**     Method      :  RequestSerial (component Shell)
**
**     Description :
**         Used to get mutual access to the shell console. Only has an
**         effect if using an RTOS with semaphore for the console
**         access.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_RequestSerial(void)
{
#if McuShell_CONFIG_USE_MUTEX
  (void)xSemaphoreTakeRecursive(ShellSem, portMAX_DELAY);
#endif
}

/*
** ===================================================================
**     Method      :  ReleaseSerial (component Shell)
**
**     Description :
**         Used to release mutual access to the shell console. Only has
**         an effect if using an RTOS with semaphore for the console
**         access.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_ReleaseSerial(void)
{
#if McuShell_CONFIG_USE_MUTEX
  (void)xSemaphoreGiveRecursive(ShellSem);
#endif
}

/*
** ===================================================================
**     Method      :  GetSemaphore (component Shell)
**
**     Description :
**         Return the semaphore of the shell.
**     Parameters  : None
**     Returns     :
**         ---             - semaphore, or NULL if not used or not
**                           allocated.
** ===================================================================
*/
void* McuShell_GetSemaphore(void)
{
#if McuShell_CONFIG_USE_MUTEX
  return ShellSem;
#else
  return NULL;
#endif
}

/*
** ===================================================================
**     Method      :  SendSeparatedStrings (component Shell)
**
**     Description :
**         This method is internal. It is used by Processor Expert only.
** ===================================================================
*/
static void SendSeparatedStrings(const uint8_t *strA, const uint8_t *strB, uint8_t tabChar, uint8_t tabPos, McuShell_StdIO_OutErr_FctType io)
{
  /* write command part */
  if (strA!=NULL) {
    while(*strA!='\0' && tabPos>0) {
      io(*strA++);
      tabPos--;
    }
  }
  /* fill up until ';' */
  while(tabPos>0) {
    io(' ');
    tabPos--;
  }
  /* write separator */
  io(tabChar);
  io(' ');
  if (strB!=NULL) {
    /* write help text */
    McuShell_SendStr(strB, io);
  }
}

/*
** ===================================================================
**     Method      :  SendHelpStr (component Shell)
**
**     Description :
**         Prints a string using an I/O function, formated for the
**         'help' command
**     Parameters  :
**         NAME            - DESCRIPTION
**       * strCmd          - Pointer to string of the command
**       * strHelp         - Pointer to help text string
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendHelpStr(const uint8_t *strCmd, const uint8_t *strHelp, McuShell_StdIO_OutErr_FctType io)
{
  SendSeparatedStrings(strCmd, strHelp, ';', McuShell_CONFIG_HELP_SEMICOLON_POS, io);
}

/*
** ===================================================================
**     Method      :  SendStatusStr (component Shell)
**
**     Description :
**         Prints a status string using an I/O function, formated for
**         the 'status' command
**     Parameters  :
**         NAME            - DESCRIPTION
**       * strItem         - Pointer to string of the command
**       * strStatus       - Pointer to help text string
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendStatusStr(const uint8_t *strItem, const uint8_t *strStatus, McuShell_StdIO_OutErr_FctType io)
{
  SendSeparatedStrings(strItem, strStatus, ':', McuShell_CONFIG_STATUS_COLON_POS, io);
}

/*
** ===================================================================
**     Method      :  ReadChar (component Shell)
**
**     Description :
**         Reads a character (blocking)
**     Parameters  :
**         NAME            - DESCRIPTION
**       * c               - Pointer to character to be used to store the
**                           result
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_ReadChar(uint8_t *c)
{
#if McuShell_CONFIG_DEFAULT_SERIAL
  uint8_t res;

#if McuShell_CONFIG_USE_MUTEX
  (void)xSemaphoreTakeRecursive(ShellSem, portMAX_DELAY);
#endif
  res = McuShell_CONFIG_DEFAULT_SERIAL_RECEIVE_FCT_NAME((uint8_t*)c);
  if (res==ERR_RXEMPTY) {
    /* no character in buffer */
    *c = '\0';
  }
#if McuShell_CONFIG_USE_MUTEX
  (void)xSemaphoreGiveRecursive(ShellSem);
#endif
#else
  *c = '\0';
  return; /* no serial component set up in properties */
#endif
}

/*
** ===================================================================
**     Method      :  SendChar (component Shell)
**
**     Description :
**         Sends a character (blocking)
**     Parameters  :
**         NAME            - DESCRIPTION
**         ch              - character to be sent
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendChar(uint8_t ch)
{
#if McuShell_CONFIG_DEFAULT_SERIAL
  McuShell_SendCharFct(ch, McuShell_CONFIG_DEFAULT_SERIAL_SEND_FCT_NAME);
#else
  (void)ch;                            /* avoid compiler warning about unused argument */
#endif
}

/*
** ===================================================================
**     Method      :  KeyPressed (component Shell)
**
**     Description :
**         Checks if a key has been pressed (a character is present in
**         the input buffer)
**     Parameters  : None
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
bool McuShell_KeyPressed(void)
{
#if McuShell_CONFIG_DEFAULT_SERIAL
  bool res;

#if McuShell_CONFIG_USE_MUTEX
  (void)xSemaphoreTakeRecursive(ShellSem, portMAX_DELAY);
#endif
#if McuShell_CONFIG_DEFAULT_SERIAL
  res = (bool)((McuShell_CONFIG_DEFAULT_SERIAL_RXAVAIL_FCT_NAME()==0U) ? FALSE : TRUE); /* true if there are characters in receive buffer */
#endif
#if McuShell_CONFIG_USE_MUTEX
  (void)xSemaphoreGiveRecursive(ShellSem);
#endif
  return res;
#else
  return FALSE; /* no serial component set up in properties */
#endif
}

/*
** ===================================================================
**     Method      :  Init (component Shell)
**
**     Description :
**         Initializes the module, especially creates the mutex
**         semaphore if an RTOS is used.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_Init(void)
{
#if McuShell_CONFIG_USE_MUTEX
#if configSUPPORT_STATIC_ALLOCATION
  static StaticSemaphore_t xMutexBuffer;
#endif
  bool schedulerStarted;
  McuCriticalSection_CriticalVariable();

  schedulerStarted = (bool)(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED);
  if (!schedulerStarted) { /* FreeRTOS not started yet. We are called in PE_low_level_init(), and interrupts are disabled */
    McuCriticalSection_EnterCritical();
  }
#if configSUPPORT_STATIC_ALLOCATION
  ShellSem = xSemaphoreCreateRecursiveMutexStatic(&xMutexBuffer);
#else
  ShellSem = xSemaphoreCreateRecursiveMutex();
#endif
  if (!schedulerStarted) { /* above RTOS call might have enabled interrupts! Make sure we restore the state */
    McuCriticalSection_ExitCritical();
  }
  if (ShellSem==NULL) { /* semaphore creation failed */
    for(;;) {} /* error, not enough memory? */
  }
  vQueueAddToRegistry(ShellSem, "McuShell_Sem");
#endif
#if McuShell_CONFIG_HISTORY_ENABLED
  {
    int i;

    McuShell_history_index = 0;
    for(i=0; i<McuShell_CONFIG_HISTORY_NOF_ITEMS;i++) {
      McuShell_history[i][0] = '\0';
    }
  }
#endif
#if McuShell_CONFIG_ECHO_ENABLED
  McuShell_EchoEnabled = TRUE;
#endif
}

/*
** ===================================================================
**     Method      :  Deinit (component Shell)
**
**     Description :
**         De-Initializes the module, especially frees the mutex
**         semaphore if an RTOS is used.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_Deinit(void)
{
#if McuShell_CONFIG_USE_MUTEX
  vQueueUnregisterQueue(ShellSem);
  vSemaphoreDelete(ShellSem);
  ShellSem = NULL;
#endif
}

/*
** ===================================================================
**     Method      :  SendData (component Shell)
**
**     Description :
**         Sends data using an I/O function. Unlike SendStr(), with
**         this method it is possible to send binary data, including
**         zero bytes.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * data            - Pointer to data to be sent
**         dataSize        - Number of bytes to be sent.
**         io              - I/O callbacks to be used for printing.
**     Returns     : Nothing
** ===================================================================
*/
/*!
 * \brief Sends data using I/O callbacks
 * \param[in] data Pointer to data to be sent
 * \param[in] io I/O function to be used for sending
 */
void McuShell_SendData(const uint8_t *data, uint16_t dataSize, McuShell_StdIO_OutErr_FctType io)
{
  while(dataSize>0) {
    io(*data++);
    dataSize--;
  }
}

/*
** ===================================================================
**     Method      :  McuShell_printfPutChar (component Shell)
**
**     Description :
**         This method is internal. It is used by Processor Expert only.
** ===================================================================
*/
void McuShell_printfPutChar(void *arg, char c)
{
  McuShell_StdIO_OutErr_FctType fct = (McuShell_StdIO_OutErr_FctType)arg;

  fct(c); /* print character */
}

/*
** ===================================================================
**     Method      :  printfIO (component Shell)
**
**     Description :
**         Printf() style function using XFormat component, using a
**         custom I/O handler.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * io              - Pointer to 
**         fmt             - printf style format string
**     Returns     :
**         ---             - number of characters written
** ===================================================================
*/
unsigned McuShell_printfIO(McuShell_ConstStdIOType *io, const char *fmt, ...)
{
  va_list args;
  unsigned int count = 0;

  va_start(args,fmt);
  count = McuXFormat_xvformat(McuShell_printfPutChar, (void*)io->stdOut, fmt, args);
  va_end(args);
  return count;
}

/*
** ===================================================================
**     Method      :  printf (component Shell)
**
**     Description :
**         Printf() style function using XFormat component, using the
**         shell default I/O handler.
**     Parameters  :
**         NAME            - DESCRIPTION
**         fmt             - printf style format string
**     Returns     :
**         ---             - number of characters written
** ===================================================================
*/
unsigned McuShell_printf(const char *fmt, ...)
{
  va_list args;
  unsigned int count = 0;

  va_start(args,fmt);
  count = McuXFormat_xvformat(McuShell_printfPutChar, (void*)McuShell_GetStdio()->stdOut, fmt, args);
  va_end(args);
  return count;
}

/*
** ===================================================================
**     Method      :  SendCharFct (component Shell)
**
**     Description :
**         Method to send a character using a standard I/O handle.
**     Parameters  :
**         NAME            - DESCRIPTION
**         ch              - character to be sent
**       * fct             - Function pointer to output function: takes
**                           a byte to write and returns error code.
**     Returns     : Nothing
** ===================================================================
*/
void McuShell_SendCharFct(uint8_t ch, uint8_t (*fct)(uint8_t ch))
{
#if McuShell_CONFIG_BLOCKING_SEND_ENABLED
  uint8_t res;
  #if McuShell_CONFIG_BLOCKING_SEND_TIMEOUT_MS>0
  int timeoutMs = McuShell_CONFIG_BLOCKING_SEND_TIMEOUT_MS;
  #endif
#endif

#if McuShell_CONFIG_USE_MUTEX
  (void)xSemaphoreTakeRecursive(ShellSem, portMAX_DELAY);
#endif
#if McuShell_CONFIG_BLOCKING_SEND_ENABLED
  do {
    res = fct((uint8_t)ch);            /* Send char, returns error code */
  #if McuShell_CONFIG_BLOCKING_SEND_TIMEOUT_MS>0
    if (res==ERR_TXFULL) {
    #if McuShell_CONFIG_BLOCKING_SEND_RTOS_WAIT
      McuWait_WaitOSms(McuShell_CONFIG_BLOCKING_SEND_TIMEOUT_WAIT_MS);
    #else
      McuWait_Waitms(McuShell_CONFIG_BLOCKING_SEND_TIMEOUT_WAIT_MS);
    #endif
    }
  #endif
  #if McuShell_CONFIG_BLOCKING_SEND_TIMEOUT_MS>0
    if(timeoutMs<=0) {
      break; /* timeout */
    }
    timeoutMs -= McuShell_CONFIG_BLOCKING_SEND_TIMEOUT_WAIT_MS;
  #endif
  } while(res==ERR_TXFULL);
#else
  (void)fct((uint8_t)ch);              /* non blocking send */
#endif
#if McuShell_CONFIG_USE_MUTEX
  (void)xSemaphoreGiveRecursive(ShellSem);
#endif
}

/*
** ===================================================================
**     Method      :  PrintMemory (component Shell)
**
**     Description :
**         Prints a chunk of memory bytes in a formatted way.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * hndl            - Pointer to 
**         startAddr       - Memory start address
**         endAddr         - Memory end address
**         addrSize        - Number of bytes for the address
**                           (1, 2, 3 or 4)
**         bytesPerLine    - Number of bytes per line
**         readfp          - Function pointer to read the memory.
**                           Returns error code, uses a device handle,
**                           32bit address with a pointer to a buffer
**                           and a buffer size.
**       * io              - Pointer to I/O to be used
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuShell_PrintMemory(void *hndl, uint32_t startAddr, uint32_t endAddr, uint8_t addrSize, uint8_t bytesPerLine, uint8_t (*readfp)(void *, uint32_t, uint8_t*, size_t), McuShell_ConstStdIOType *io)
{
  #define NOF_BYTES_PER_LINE 32 /* how many bytes are shown on a line. This defines as well the chunk size we read from memory */
  #define MAX_NOF_BYTES_PER_LINE 32
  uint8_t buf[MAX_NOF_BYTES_PER_LINE]; /* this is the chunk of data we get (per line in output) */
  uint8_t str[3*MAX_NOF_BYTES_PER_LINE+((MAX_NOF_BYTES_PER_LINE+1)/8)+1]; /* maximum string for output:
                                              - '3*' because each byte is 2 hex digits plus a space
                                              - '(NOF_BYTES_PER_LINE+1)/8' because we add a space between every 8 byte block
                                              - '+1' for the final zero byte */
  uint32_t addr;
  uint8_t res=0, j, bufSize;
  uint8_t ch;

  if (endAddr<startAddr) {
    McuShell_SendStr((unsigned char*)"\r\n*** End address must be larger or equal than start address\r\n", io->stdErr);
    return ERR_RANGE;
  }
  for(addr=startAddr; addr<=endAddr; /* nothing */ ) {
    if (endAddr-addr+1 >= bytesPerLine) { /* read only part of buffer */
      bufSize = bytesPerLine; /* read full buffer */
    } else {
      bufSize = (uint8_t)(endAddr-addr+1);
    }
    if (readfp(hndl, addr, buf, bufSize)!=ERR_OK) {
      McuShell_SendStr((unsigned char*)"\r\n*** Read failed!\r\n", io->stdErr);
      return ERR_FAILED;
    }
    if (res != ERR_OK) {
      McuShell_SendStr((unsigned char*)"\r\n*** Failure reading memory block!\r\n", io->stdErr);
      return ERR_FAULT;
    }
    /* write address */
    McuUtility_strcpy(str, sizeof(str), (unsigned char*)"0x");
    McuUtility_strcatNumHex(str, sizeof(str), addr, addrSize);
    McuUtility_chcat(str, sizeof(str), ':');
    McuShell_SendStr((unsigned char*)str, io->stdOut);
    /* write data in hex */
    str[0] = '\0';
    for (j=0; j<bufSize; j++) {
      if ((j)==0) {
        McuUtility_chcat(str, sizeof(str), ' ');
      }
      McuUtility_strcatNum8Hex(str, sizeof(str), buf[j]);
      McuUtility_chcat(str, sizeof(str), ' ');
    }
    for (/*empty*/; j<bytesPerLine; j++) { /* fill up line */
      McuUtility_strcat(str, sizeof(str), (unsigned char*)"-- ");
    }
    McuShell_SendStr((unsigned char*)str, io->stdOut);
    /* write in ASCII */
    io->stdOut(' ');
    for (j=0; j<bufSize; j++) {
      ch = buf[j];
      if (ch >= ' ' && ch <= 0x7f) {
        io->stdOut(ch);
      } else {
        io->stdOut('.'); /* place holder */
      }
    }
    for (/*empty*/; j<bytesPerLine; j++) { /* fill up line */
      McuUtility_strcat(str, sizeof(str), (unsigned char*)"-- ");
    }
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    addr += bytesPerLine;
  }
  return ERR_OK;
}

/* END McuShell. */

/*!
** @}
*/
