text: /**
  *
  *      Derived from Dave Hylands & http://www.efgh.com/software/gprintf.htm
  *    code (public domain)
  *
  *
  *    This file is NOT MISRA 2012 compliance
  *
  *
  *****************************************************************************/
  
  #include <stdarg.h>
  
  #define  NO_OPTION       0x00   // No options specified.
  #define  MINUS_SIGN      0x01   // Should we print a minus sign?
  #define  RIGHT_JUSTIFY   0x02   // Should field be right justified?
  #define  ZERO_PAD        0x04   // Should field be zero padded?
  #define  CAPITAL_HEX     0x08   // Did we encounter %X?
  
  #define  IsOptionSet(p,x)     (((p)->options & (x)) != 0)
  #define  IsOptionClear(p,x)   (((p)->options & (x)) == 0)
  #define  SetOption(p,x)       (p)->options = ((p)->options |  (x))
  #define  ClearOption(p,x)     (p)->options = ((p)->options & ~(x))
  
  #ifndef NULL
  #define NULL 0
  #endif
  
  typedef int (*dprintf_func)(void *outParm, int ch);
  
  /**
  *  Operating status structure
  *
  */
  typedef struct _PARAMETERS
  {
      int  numOutputChars;   //!< number output characters
      int  options;          //!< oprions
      int  minFieldWidth;    //!< minimal field width
      int  editedStringLen;  //!< edited string length
      int  leadingZeros;     //!< Leading zeroes
      dprintf_func outFunc;  //!< Pointer to the actual dprintf function
      void * outParm;        //!< Output parameters
  } PARAMETERS;
  
  
  /**
  *  String parameters structure
  *
  */
  typedef struct _STRPRINTFPARAMS
  {
      char * str;     //!<  pointer to string
      int   maxLen;   //!<  maximum length
  } STRPRINTFPARAMS;
  
  //-- Local prototypes
  
  static void OutputChar( PARAMETERS *p, int c );
  static void OutputField( PARAMETERS *p, char *s );
  static int  StrPrintfFunc( void *outParm, int ch );
  static int vStrXPrintf( dprintf_func outFunc, void *outParm,
                          const char *fmt, va_list args);
  int vStrPrintf( char *outStr, int maxLen, const char *fmt, va_list args );
  
  int tn_snprintf( char *outStr, int maxLen, const char *fmt, ... );
  
  //----------------------------------------------------------------------------
  int tn_snprintf( char *outStr, int maxLen, const char *fmt, ... )
  {
      int      rc;
      va_list  args;
  
      va_start( args, fmt );
      rc = vStrPrintf( outStr, maxLen, fmt, args );
      //va_end( args );
  
      return rc;
  }
  
  //----------------------------------------------------------------------------
  int vStrPrintf( char *outStr, int maxLen, const char *fmt, va_list args )
  {
      STRPRINTFPARAMS    strParm;
  
      strParm.str    = outStr;
      strParm.maxLen = maxLen - 1; // Leave space for temrinating null char
  
      return vStrXPrintf(StrPrintfFunc, &strParm, fmt, args );
  }
  
  //----------------------------------------------------------------------------
  static int vStrXPrintf( dprintf_func  outFunc, void * outParm,
                          const char * fmt, va_list args)
  {
      PARAMETERS  p;
      char        controlChar;
  
      p.numOutputChars  = 0;
      p.outFunc         = outFunc;
      p.outParm         = outParm;
  
      controlChar = *fmt++;
  
      while(controlChar != '\0')
      {
          if(controlChar == '%')
          {
              short precision = -1;
              short longArg   = 0;
              short base      = 0;
  
              controlChar = *fmt++;
              p.minFieldWidth = 0;
              p.leadingZeros  = 0;
              p.options       = NO_OPTION;
  
              SetOption( &p, RIGHT_JUSTIFY);
  
              // Process [flags]
  
              if(controlChar == '-')
              {
                  ClearOption( &p, RIGHT_JUSTIFY );
                  controlChar = *fmt++;
              }
  
              if(controlChar == '0')
              {
                  SetOption( &p, ZERO_PAD );
                  controlChar = *fmt++;
              }
  
              // Process [width]
  
              if(controlChar == '*')
              {
                  p.minFieldWidth = (short)va_arg( args, int );
                  controlChar = *fmt++;
              }
              else
              {
                  while(('0' <= controlChar) && (controlChar <= '9'))
                  {
                      p.minFieldWidth = p.minFieldWidth * 10 + controlChar - '0';
                      controlChar = *fmt++;
                  }
              }
  
              // Process [.precision]
  
              if(controlChar == '.')
              {
                  controlChar = *fmt++;
                  if(controlChar == '*')
                  {
                      precision = (short)va_arg( args, int );
                      controlChar = *fmt++;
                  }
                  else
                  {
                      precision = 0;
                      while(('0' <= controlChar) && (controlChar <= '9'))
                      {
                          precision = precision * 10 + controlChar - '0';
                          controlChar = *fmt++;
                      }
                  }
              }
  
              //  Process [l]
  
              if(controlChar == 'l')
              {
                  longArg = 1;
                  controlChar = *fmt++;
              }
  
              //  Process type.
  
              if(controlChar == 'd')
              {
                  base = 10;
              }
              else if(controlChar == 'x')
              {
                  base = 16;
              }
              else if(controlChar == 'X')
              {
                  base = 16;
                  SetOption( &p, CAPITAL_HEX );
              }
              else if(controlChar == 'u')
              {
                  base = 10;
              }
              else if( controlChar == 'o')
              {
                  base = 8;
              }
              else if ( controlChar == 'b' )
              {
                  base = 2;
              }
              else if ( controlChar == 'c' )
              {
                  base = -1;
                  ClearOption( &p, ZERO_PAD );
              }
              else if(controlChar == 's' )
              {
                  base = -2;
                  ClearOption( &p, ZERO_PAD );
              }
  
              if(base == 0)  // invalid conversion type
              {
                  if(controlChar != '\0')
                  {
                      OutputChar( &p, controlChar );
                      controlChar = *fmt++;
                  }
              }
              else
              {
                  if(base == -1)            // conversion type c
                  {
                      char c = (char)va_arg( args, int );
                      p.editedStringLen = 1;
                      OutputField(&p, &c);
                  }
                  else if(base == -2)      // conversion type s
                  {
                      char *string = va_arg(args, char *);
  
                      p.editedStringLen = 0;
                      while(string[ p.editedStringLen ] != '\0')
                      {
                          if((precision >= 0) && (p.editedStringLen >= precision))
                          {
                              // We don't require the string to be null terminated
                              // if a precision is specified.
                              break;
                          }
                          p.editedStringLen++;
                      }
                      OutputField( &p, string );
                  }
                  else  // conversion type d, b, o or x
                  {
                      unsigned long x;
  
                      // Worst case buffer allocation is required for binary output,
                      // which requires one character per bit of a long.
  
                      char buffer[ 8 * sizeof(unsigned long) + 4 ]; //-- 4 for align,need 1
  
                      p.editedStringLen = 0;
                      if(longArg)
                      {
                          x = va_arg( args, unsigned long );
                      }
                      else if(controlChar == 'd')
                      {
                          x = va_arg(args, int);
                      }
                      else
                      {
                          x = va_arg(args, unsigned);
                      }
  
                      if((controlChar == 'd') && ((long) x < 0 ))
                      {
                          SetOption(&p, MINUS_SIGN);
                          x = - (long) x;
                      }
  
                      do
                      {
                          int c;
                          c = x % base + '0';
                          if(c > '9')
                          {
                              if(IsOptionSet(&p, CAPITAL_HEX))
                              {
                                  c += 'A' - '9' - 1;
                              }
                              else
                              {
                                  c += 'a' - '9' - 1;
                              }
                          }
                          buffer[ sizeof( buffer ) - 1 - p.editedStringLen++ ] = (char)c;
                      }
                      while((x /= base) != 0);
  
                      if((precision >= 0) && (precision > p.editedStringLen))
                      {
                          p.leadingZeros = precision - p.editedStringLen;
                      }
                      OutputField(&p, buffer + sizeof(buffer) - p.editedStringLen);
                  }
                  controlChar = *fmt++;
              }
          }
          else
          {
              // We're not processing a % output. Just output the character that
              // was encountered.
  
              OutputChar(&p, controlChar);
              controlChar = *fmt++;
          }
      }
      return p.numOutputChars;
  }
  
  //-----------------------------------------------------------------------------
  static void OutputChar(PARAMETERS *p, int c)
  {
      if(p->numOutputChars >= 0)
      {
          int n = (*p->outFunc)(p->outParm, c);
  
          if(n >= 0)
          {
              p->numOutputChars++;
          }
          else
          {
              p->numOutputChars = n;
          }
      }
  }
  
  //-----------------------------------------------------------------------------
  static void OutputField( PARAMETERS *p, char *s )
  {
      int padLen = p->minFieldWidth - p->leadingZeros - p->editedStringLen;
  
      padLen = p->minFieldWidth - p->leadingZeros - p->editedStringLen;
  
      if(IsOptionSet(p, MINUS_SIGN))
      {
          if(IsOptionSet(p, ZERO_PAD))
          {
              // Since we're zero padding, output the minus sign now. If we're space
              // padding, we wait until we've output the spaces.
              OutputChar( p, '-' );
          }
          // Account for the minus sign now, even if we are going to output it
          // later. Otherwise we'll output too much space padding.
  
          padLen--;
      }
  
      if(IsOptionSet(p, RIGHT_JUSTIFY))
      {
          // Right justified: Output the spaces then the field.
          while ( --padLen >= 0 )
          {
              OutputChar(p, p->options & ZERO_PAD ? '0' : ' ');
          }
      }
      if(IsOptionSet(p, MINUS_SIGN) && IsOptionClear(p, ZERO_PAD))
      {
          // We're not zero padding, which means we haven't output the minus
          // sign yet. Do it now.
          OutputChar( p, '-' );
      }
  
      while(--p->leadingZeros >= 0)  // Output any leading zeros.
      {
          OutputChar(p, '0');
      }
      while(--p->editedStringLen >= 0) // Output the field itself.
      {
          OutputChar(p, *s++ );
      }
  
      // Output any trailing space padding. Note that if we output leading
      // padding, then padLen will already have been decremented to zero.
  
      while(--padLen >= 0)
      {
          OutputChar(p, ' ');
      }
  }
  
  //-----------------------------------------------------------------------------
  static int StrPrintfFunc( void *outParm, int ch )
  {
      STRPRINTFPARAMS * strParm;
  
      strParm = (STRPRINTFPARAMS *)(outParm);
      if(strParm != NULL)
      {
          if(strParm->maxLen > 0 )
          {
              *strParm->str++ = (char)ch;
              *strParm->str = '\0';
              strParm->maxLen--;
  
              return 1;
          }
      }
      return -1;
  }
  
  //----------------------------------------------------------------------------
  //----------------------------------------------------------------------------
  //----------------------------------------------------------------------------
  //----------------------------------------------------------------------------
  