/* * Copyright 2017, 2020 NXP * All rights reserved. * * * SPDX-License-Identifier: BSD-3-Clause * */ #include #include #include #include /* MISRA C-2012 Rule 22.9 */ #include "fsl_str.h" #include "fsl_debug_console_conf.h" /******************************************************************************* * Definitions ******************************************************************************/ /*! @brief The overflow value.*/ #ifndef HUGE_VAL #define HUGE_VAL (99.e99) #endif /* HUGE_VAL */ #ifndef MAX_FIELD_WIDTH #define MAX_FIELD_WIDTH 99U #endif #if PRINTF_ADVANCED_ENABLE /*! @brief Specification modifier flags for printf. */ enum _debugconsole_printf_flag { kPRINTF_Minus = 0x01U, /*!< Minus FLag. */ kPRINTF_Plus = 0x02U, /*!< Plus Flag. */ kPRINTF_Space = 0x04U, /*!< Space Flag. */ kPRINTF_Zero = 0x08U, /*!< Zero Flag. */ kPRINTF_Pound = 0x10U, /*!< Pound Flag. */ kPRINTF_LengthChar = 0x20U, /*!< Length: Char Flag. */ kPRINTF_LengthShortInt = 0x40U, /*!< Length: Short Int Flag. */ kPRINTF_LengthLongInt = 0x80U, /*!< Length: Long Int Flag. */ kPRINTF_LengthLongLongInt = 0x100U, /*!< Length: Long Long Int Flag. */ }; #endif /* PRINTF_ADVANCED_ENABLE */ /*! @brief Specification modifier flags for scanf. */ enum _debugconsole_scanf_flag { kSCANF_Suppress = 0x2U, /*!< Suppress Flag. */ kSCANF_DestMask = 0x7cU, /*!< Destination Mask. */ kSCANF_DestChar = 0x4U, /*!< Destination Char Flag. */ kSCANF_DestString = 0x8U, /*!< Destination String FLag. */ kSCANF_DestSet = 0x10U, /*!< Destination Set Flag. */ kSCANF_DestInt = 0x20U, /*!< Destination Int Flag. */ kSCANF_DestFloat = 0x30U, /*!< Destination Float Flag. */ kSCANF_LengthMask = 0x1f00U, /*!< Length Mask Flag. */ #if SCANF_ADVANCED_ENABLE kSCANF_LengthChar = 0x100U, /*!< Length Char Flag. */ kSCANF_LengthShortInt = 0x200U, /*!< Length ShortInt Flag. */ kSCANF_LengthLongInt = 0x400U, /*!< Length LongInt Flag. */ kSCANF_LengthLongLongInt = 0x800U, /*!< Length LongLongInt Flag. */ #endif /* SCANF_ADVANCED_ENABLE */ #if SCANF_FLOAT_ENABLE kSCANF_LengthLongLongDouble = 0x1000U, /*!< Length LongLongDuoble Flag. */ #endif /*PRINTF_FLOAT_ENABLE */ kSCANF_TypeSinged = 0x2000U, /*!< TypeSinged Flag. */ }; /*! @brief Keil: suppress ellipsis warning in va_arg usage below. */ #if defined(__CC_ARM) #pragma diag_suppress 1256 #endif /* __CC_ARM */ /******************************************************************************* * Prototypes ******************************************************************************/ /*! * @brief Scanline function which ignores white spaces. * * @param[in] s The address of the string pointer to update. * @return String without white spaces. */ static uint32_t ScanIgnoreWhiteSpace(const char **s); /*! * @brief Converts a radix number to a string and return its length. * * @param[in] numstr Converted string of the number. * @param[in] nump Pointer to the number. * @param[in] neg Polarity of the number. * @param[in] radix The radix to be converted to. * @param[in] use_caps Used to identify %x/X output format. * @return Length of the converted string. */ static int32_t ConvertRadixNumToString(char *numstr, void *nump, int32_t neg, int32_t radix, bool use_caps); #if PRINTF_FLOAT_ENABLE /*! * @brief Converts a floating radix number to a string and return its length. * * @param[in] numstr Converted string of the number. * @param[in] nump Pointer to the number. * @param[in] radix The radix to be converted to. * @param[in] precision_width Specify the precision width. * @return Length of the converted string. */ static int32_t ConvertFloatRadixNumToString(char *numstr, void *nump, int32_t radix, uint32_t precision_width); #endif /* PRINTF_FLOAT_ENABLE */ /*************Code for process formatted data*******************************/ #if PRINTF_ADVANCED_ENABLE static uint8_t PrintGetSignChar(int64_t ival, uint32_t flags_used, char *schar) { uint8_t len = 1U; if (ival < 0) { *schar = '-'; } else { if (0U != (flags_used & (uint32_t)kPRINTF_Plus)) { *schar = '+'; } else if (0U != (flags_used & (uint32_t)kPRINTF_Space)) { *schar = ' '; } else { *schar = '\0'; len = 0U; } } return len; } #endif static uint32_t PrintGetWidth(const char **p, va_list *ap) { uint32_t field_width = 0; uint8_t done = 0U; char c; while (0U == done) { c = *(++(*p)); if ((c >= '0') && (c <= '9')) { (field_width) = ((field_width)*10U) + ((uint32_t)c - (uint32_t)'0'); } #if PRINTF_ADVANCED_ENABLE else if (c == '*') { (field_width) = (uint32_t)va_arg(*ap, uint32_t); } #endif /* PRINTF_ADVANCED_ENABLE */ else { /* We've gone one char too far. */ --(*p); done = 1U; } } return field_width; } static uint32_t PrintGetPrecision(const char **s, va_list *ap, bool *valid_precision_width) { const char *p = *s; uint32_t precision_width = 6U; uint8_t done = 0U; #if PRINTF_ADVANCED_ENABLE if (NULL != valid_precision_width) { *valid_precision_width = false; } #endif /* PRINTF_ADVANCED_ENABLE */ if (*++p == '.') { /* Must get precision field width, if present. */ precision_width = 0U; done = 0U; while (0U == done) { char c = *++p; if ((c >= '0') && (c <= '9')) { precision_width = (precision_width * 10U) + ((uint32_t)c - (uint32_t)'0'); #if PRINTF_ADVANCED_ENABLE if (NULL != valid_precision_width) { *valid_precision_width = true; } #endif /* PRINTF_ADVANCED_ENABLE */ } #if PRINTF_ADVANCED_ENABLE else if (c == '*') { precision_width = (uint32_t)va_arg(*ap, uint32_t); if (NULL != valid_precision_width) { *valid_precision_width = true; } } #endif /* PRINTF_ADVANCED_ENABLE */ else { /* We've gone one char too far. */ --p; done = 1U; } } } else { /* We've gone one char too far. */ --p; } *s = p; return precision_width; } static uint32_t PrintIsobpu(const char c) { uint32_t ret = 0U; if ((c == 'o') || (c == 'b') || (c == 'p') || (c == 'u')) { ret = 1U; } return ret; } static uint32_t PrintIsdi(const char c) { uint32_t ret = 0U; if ((c == 'd') || (c == 'i')) { ret = 1U; } return ret; } static void PrintOutputdifFobpu(uint32_t flags_used, uint32_t field_width, uint32_t vlen, char schar, char *vstrp, printfCb cb, char *buf, int32_t *count) { #if PRINTF_ADVANCED_ENABLE /* Do the ZERO pad. */ if (0U != (flags_used & (uint32_t)kPRINTF_Zero)) { if ('\0' != schar) { cb(buf, count, schar, 1); schar = '\0'; } cb(buf, count, '0', (int)field_width - (int)vlen); vlen = field_width; } else { if (0U == (flags_used & (uint32_t)kPRINTF_Minus)) { cb(buf, count, ' ', (int)field_width - (int)vlen); if ('\0' != schar) { cb(buf, count, schar, 1); schar = '\0'; } } } /* The string was built in reverse order, now display in correct order. */ if ('\0' != schar) { cb(buf, count, schar, 1); } #else cb(buf, count, ' ', (int)field_width - (int)vlen); #endif /* PRINTF_ADVANCED_ENABLE */ while ('\0' != (*vstrp)) { cb(buf, count, *vstrp--, 1); } #if PRINTF_ADVANCED_ENABLE if (0U != (flags_used & (uint32_t)kPRINTF_Minus)) { cb(buf, count, ' ', (int)field_width - (int)vlen); } #endif /* PRINTF_ADVANCED_ENABLE */ } static void PrintOutputxX(uint32_t flags_used, uint32_t field_width, uint32_t vlen, bool use_caps, char *vstrp, printfCb cb, char *buf, int32_t *count) { #if PRINTF_ADVANCED_ENABLE uint8_t dschar = 0; if (0U != (flags_used & (uint32_t)kPRINTF_Zero)) { if (0U != (flags_used & (uint32_t)kPRINTF_Pound)) { cb(buf, count, '0', 1); cb(buf, count, (use_caps ? 'X' : 'x'), 1); dschar = 1U; } cb(buf, count, '0', (int)field_width - (int)vlen); vlen = field_width; } else { if (0U == (flags_used & (uint32_t)kPRINTF_Minus)) { if (0U != (flags_used & (uint32_t)kPRINTF_Pound)) { vlen += 2U; } cb(buf, count, ' ', (int)field_width - (int)vlen); if (0U != (flags_used & (uint32_t)kPRINTF_Pound)) { cb(buf, count, '0', 1); cb(buf, count, (use_caps ? 'X' : 'x'), 1); dschar = 1U; } } } if ((0U != (flags_used & (uint32_t)kPRINTF_Pound)) && (0U == dschar)) { cb(buf, count, '0', 1); cb(buf, count, (use_caps ? 'X' : 'x'), 1); vlen += 2U; } #else cb(buf, count, ' ', (int)field_width - (int)vlen); #endif /* PRINTF_ADVANCED_ENABLE */ while ('\0' != (*vstrp)) { cb(buf, count, *vstrp--, 1); } #if PRINTF_ADVANCED_ENABLE if (0U != (flags_used & (uint32_t)kPRINTF_Minus)) { cb(buf, count, ' ', (int)field_width - (int)vlen); } #endif /* PRINTF_ADVANCED_ENABLE */ } static uint32_t PrintIsfF(const char c) { uint32_t ret = 0U; if ((c == 'f') || (c == 'F')) { ret = 1U; } return ret; } static uint32_t PrintIsxX(const char c) { uint32_t ret = 0U; if ((c == 'x') || (c == 'X')) { ret = 1U; } return ret; } #if PRINTF_ADVANCED_ENABLE static uint32_t PrintCheckFlags(const char **s) { const char *p = *s; /* First check for specification modifier flags. */ uint32_t flags_used = 0U; bool done = false; while (false == done) { switch (*++p) { case '-': flags_used |= (uint32_t)kPRINTF_Minus; break; case '+': flags_used |= (uint32_t)kPRINTF_Plus; break; case ' ': flags_used |= (uint32_t)kPRINTF_Space; break; case '0': flags_used |= (uint32_t)kPRINTF_Zero; break; case '#': flags_used |= (uint32_t)kPRINTF_Pound; break; default: /* We've gone one char too far. */ --p; done = true; break; } } *s = p; return flags_used; } #endif /* PRINTF_ADVANCED_ENABLE */ #if PRINTF_ADVANCED_ENABLE /* * Check for the length modifier. */ static uint32_t PrintGetLengthFlag(const char **s) { const char *p = *s; /* First check for specification modifier flags. */ uint32_t flags_used = 0U; switch (/* c = */ *++p) { case 'h': if (*++p != 'h') { flags_used |= (uint32_t)kPRINTF_LengthShortInt; --p; } else { flags_used |= (uint32_t)kPRINTF_LengthChar; } break; case 'l': if (*++p != 'l') { flags_used |= (uint32_t)kPRINTF_LengthLongInt; --p; } else { flags_used |= (uint32_t)kPRINTF_LengthLongLongInt; } break; default: /* we've gone one char too far */ --p; break; } *s = p; return flags_used; } #else static void PrintFilterLengthFlag(const char **s) { const char *p = *s; char ch; do { ch = *++p; } while ((ch == 'h') || (ch == 'l')); *s = --p; } #endif /* PRINTF_ADVANCED_ENABLE */ static uint8_t PrintGetRadixFromobpu(const char c) { uint8_t radix; if (c == 'o') { radix = 8U; } else if (c == 'b') { radix = 2U; } else if (c == 'p') { radix = 16U; } else { radix = 10U; } return radix; } static uint32_t ScanIsWhiteSpace(const char c) { uint32_t ret = 0U; if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\v') || (c == '\f')) { ret = 1U; } return ret; } static uint32_t ScanIgnoreWhiteSpace(const char **s) { uint32_t count = 0U; char c; c = **s; while (1U == ScanIsWhiteSpace(c)) { count++; (*s)++; c = **s; } return count; } static int32_t ConvertRadixNumToString(char *numstr, void *nump, int32_t neg, int32_t radix, bool use_caps) { #if PRINTF_ADVANCED_ENABLE int64_t a; int64_t b; int64_t c; uint64_t ua; uint64_t ub; uint64_t uc; #else int32_t a; int32_t b; int32_t c; uint32_t ua; uint32_t ub; uint32_t uc; #endif /* PRINTF_ADVANCED_ENABLE */ int32_t nlen; char *nstrp; nlen = 0; nstrp = numstr; *nstrp++ = '\0'; if (0 != neg) { #if PRINTF_ADVANCED_ENABLE a = *(int64_t *)nump; #else a = *(int32_t *)nump; #endif /* PRINTF_ADVANCED_ENABLE */ if (a == 0) { *nstrp = '0'; ++nlen; return nlen; } while (a != 0) { #if PRINTF_ADVANCED_ENABLE b = (int64_t)a / (int64_t)radix; c = (int64_t)a - ((int64_t)b * (int64_t)radix); if (c < 0) { c = (int64_t)'0' - c; } #else b = a / radix; c = a - (b * radix); if (c < 0) { c = (int32_t)'0' - c; } #endif /* PRINTF_ADVANCED_ENABLE */ else { c = c + (int32_t)'0'; } a = b; *nstrp++ = (char)c; ++nlen; } } else { #if PRINTF_ADVANCED_ENABLE ua = *(uint64_t *)nump; #else ua = *(uint32_t *)nump; #endif /* PRINTF_ADVANCED_ENABLE */ if (ua == 0U) { *nstrp = '0'; ++nlen; return nlen; } while (ua != 0U) { #if PRINTF_ADVANCED_ENABLE ub = (uint64_t)ua / (uint64_t)radix; uc = (uint64_t)ua - ((uint64_t)ub * (uint64_t)radix); #else ub = ua / (uint32_t)radix; uc = ua - (ub * (uint32_t)radix); #endif /* PRINTF_ADVANCED_ENABLE */ if (uc < 10U) { uc = uc + (uint32_t)'0'; } else { uc = uc - 10U + (uint32_t)(use_caps ? 'A' : 'a'); } ua = ub; *nstrp++ = (char)uc; ++nlen; } } return nlen; } #if PRINTF_FLOAT_ENABLE static int32_t ConvertFloatRadixNumToString(char *numstr, void *nump, int32_t radix, uint32_t precision_width) { int32_t a; int32_t b; int32_t c; int32_t i; double fa; double dc; double fb; double r; double fractpart; double intpart; int32_t nlen; char *nstrp; nlen = 0; nstrp = numstr; *nstrp++ = '\0'; r = *(double *)nump; if (0.0 == r) { *nstrp = '0'; ++nlen; return nlen; } fractpart = modf((double)r, (double *)&intpart); /* Process fractional part. */ for (i = 0; i < (int32_t)precision_width; i++) { fractpart *= (double)radix; } if (r >= (double)0.0) { fa = fractpart + (double)0.5; if (fa >= pow((double)10, (double)precision_width)) { intpart++; } } else { fa = fractpart - (double)0.5; if (fa <= -pow((double)10, (double)precision_width)) { intpart--; } } for (i = 0; i < (int32_t)precision_width; i++) { fb = fa / (double)radix; dc = (fa - (double)(int64_t)fb * (double)radix); c = (int32_t)dc; if (c < 0) { c = (int32_t)'0' - c; } else { c = c + '0'; } fa = fb; *nstrp++ = (char)c; ++nlen; } *nstrp++ = (char)'.'; ++nlen; a = (int32_t)intpart; if (a == 0) { *nstrp++ = '0'; ++nlen; } else { while (a != 0) { b = (int32_t)a / (int32_t)radix; c = (int32_t)a - ((int32_t)b * (int32_t)radix); if (c < 0) { c = (int32_t)'0' - c; } else { c = c + '0'; } a = b; *nstrp++ = (char)c; ++nlen; } } return nlen; } #endif /* PRINTF_FLOAT_ENABLE */ /*! * brief This function outputs its parameters according to a formatted string. * * note I/O is performed by calling given function pointer using following * (*func_ptr)(c); * * param[in] fmt_ptr Format string for printf. * param[in] args_ptr Arguments to printf. * param[in] buf pointer to the buffer * param cb print callback function pointer * * return Number of characters to be print */ int StrFormatPrintf(const char *fmt, va_list ap, char *buf, printfCb cb) { /* va_list ap; */ const char *p; char c; char vstr[33]; char *vstrp = NULL; int32_t vlen = 0; int32_t count = 0; uint32_t field_width; uint32_t precision_width; char *sval; int32_t cval; bool use_caps; uint8_t radix = 0; #if PRINTF_ADVANCED_ENABLE uint32_t flags_used; char schar; int64_t ival; uint64_t uval = 0; bool valid_precision_width; #else int32_t ival; uint32_t uval = 0; #endif /* PRINTF_ADVANCED_ENABLE */ #if PRINTF_FLOAT_ENABLE double fval; #endif /* PRINTF_FLOAT_ENABLE */ /* Start parsing apart the format string and display appropriate formats and data. */ p = fmt; while (true) { if ('\0' == *p) { break; } c = *p; /* * All formats begin with a '%' marker. Special chars like * '\n' or '\t' are normally converted to the appropriate * character by the __compiler__. Thus, no need for this * routine to account for the '\' character. */ if (c != '%') { cb(buf, &count, c, 1); p++; /* By using 'continue', the next iteration of the loop is used, skipping the code that follows. */ continue; } use_caps = true; #if PRINTF_ADVANCED_ENABLE /* First check for specification modifier flags. */ flags_used = PrintCheckFlags(&p); #endif /* PRINTF_ADVANCED_ENABLE */ /* Next check for minimum field width. */ field_width = PrintGetWidth(&p, &ap); /* Next check for the width and precision field separator. */ #if PRINTF_ADVANCED_ENABLE precision_width = PrintGetPrecision(&p, &ap, &valid_precision_width); #else precision_width = PrintGetPrecision(&p, &ap, NULL); (void)precision_width; #endif #if PRINTF_ADVANCED_ENABLE /* Check for the length modifier. */ flags_used |= PrintGetLengthFlag(&p); #else /* Filter length modifier. */ PrintFilterLengthFlag(&p); #endif /* Now we're ready to examine the format. */ c = *++p; { if (1U == PrintIsdi(c)) { #if PRINTF_ADVANCED_ENABLE if (0U != (flags_used & (uint32_t)kPRINTF_LengthLongLongInt)) { ival = (int64_t)va_arg(ap, int64_t); } else #endif /* PRINTF_ADVANCED_ENABLE */ { ival = (int32_t)va_arg(ap, int32_t); } vlen = ConvertRadixNumToString(vstr, (void *)&ival, 1, 10, use_caps); vstrp = &vstr[vlen]; #if PRINTF_ADVANCED_ENABLE vlen += (int32_t)PrintGetSignChar(ival, flags_used, &schar); PrintOutputdifFobpu(flags_used, field_width, (uint32_t)vlen, schar, vstrp, cb, buf, &count); #else PrintOutputdifFobpu(0U, field_width, (uint32_t)vlen, '\0', vstrp, cb, buf, &count); #endif } else if (1U == PrintIsfF(c)) { #if PRINTF_FLOAT_ENABLE fval = (double)va_arg(ap, double); vlen = ConvertFloatRadixNumToString(vstr, &fval, 10, precision_width); vstrp = &vstr[vlen]; #if PRINTF_ADVANCED_ENABLE vlen += (int32_t)PrintGetSignChar(((fval < 0.0) ? ((int64_t)-1) : ((int64_t)fval)), flags_used, &schar); PrintOutputdifFobpu(flags_used, field_width, (uint32_t)vlen, schar, vstrp, cb, buf, &count); #else PrintOutputdifFobpu(0, field_width, (uint32_t)vlen, '\0', vstrp, cb, buf, &count); #endif #else (void)va_arg(ap, double); #endif /* PRINTF_FLOAT_ENABLE */ } else if (1U == PrintIsxX(c)) { if (c == 'x') { use_caps = false; } #if PRINTF_ADVANCED_ENABLE if (0U != (flags_used & (uint32_t)kPRINTF_LengthLongLongInt)) { uval = (uint64_t)va_arg(ap, uint64_t); } else #endif /* PRINTF_ADVANCED_ENABLE */ { uval = (uint32_t)va_arg(ap, uint32_t); } vlen = ConvertRadixNumToString(vstr, &uval, 0, 16, use_caps); vstrp = &vstr[vlen]; #if PRINTF_ADVANCED_ENABLE PrintOutputxX(flags_used, field_width, (uint32_t)vlen, use_caps, vstrp, cb, buf, &count); #else PrintOutputxX(0U, field_width, (uint32_t)vlen, use_caps, vstrp, cb, buf, &count); #endif } else if (1U == PrintIsobpu(c)) { #if PRINTF_ADVANCED_ENABLE if (0U != (flags_used & (uint32_t)kPRINTF_LengthLongLongInt)) { uval = (uint64_t)va_arg(ap, uint64_t); } else #endif /* PRINTF_ADVANCED_ENABLE */ { uval = (uint32_t)va_arg(ap, uint32_t); } radix = PrintGetRadixFromobpu(c); vlen = ConvertRadixNumToString(vstr, &uval, 0, (int32_t)radix, use_caps); vstrp = &vstr[vlen]; #if PRINTF_ADVANCED_ENABLE PrintOutputdifFobpu(flags_used, field_width, (uint32_t)vlen, '\0', vstrp, cb, buf, &count); #else PrintOutputdifFobpu(0U, field_width, (uint32_t)vlen, '\0', vstrp, cb, buf, &count); #endif } else if (c == 'c') { cval = (int32_t)va_arg(ap, uint32_t); cb(buf, &count, cval, 1); } else if (c == 's') { sval = (char *)va_arg(ap, char *); if (NULL != sval) { #if PRINTF_ADVANCED_ENABLE if (valid_precision_width) { vlen = (int32_t)precision_width; } else { vlen = (int32_t)strlen(sval); } #else vlen = (int32_t)strlen(sval); #endif /* PRINTF_ADVANCED_ENABLE */ #if PRINTF_ADVANCED_ENABLE if (0U == (flags_used & (uint32_t)kPRINTF_Minus)) #endif /* PRINTF_ADVANCED_ENABLE */ { cb(buf, &count, ' ', (int)field_width - (int)vlen); } #if PRINTF_ADVANCED_ENABLE if (valid_precision_width) { while (('\0' != *sval) && (vlen > 0)) { cb(buf, &count, *sval++, 1); vlen--; } /* In case that vlen sval is shorter than vlen */ vlen = (int32_t)precision_width - vlen; } else { #endif /* PRINTF_ADVANCED_ENABLE */ while ('\0' != (*sval)) { cb(buf, &count, *sval++, 1); } #if PRINTF_ADVANCED_ENABLE } #endif /* PRINTF_ADVANCED_ENABLE */ #if PRINTF_ADVANCED_ENABLE if (0U != (flags_used & (uint32_t)kPRINTF_Minus)) { cb(buf, &count, ' ', (int32_t)field_width - vlen); } #endif /* PRINTF_ADVANCED_ENABLE */ } } else { cb(buf, &count, c, 1); } } p++; } return count; } #if SCANF_FLOAT_ENABLE static uint8_t StrFormatScanIsFloat(char *c) { uint8_t ret = 0U; if (('a' == (*c)) || ('A' == (*c)) || ('e' == (*c)) || ('E' == (*c)) || ('f' == (*c)) || ('F' == (*c)) || ('g' == (*c)) || ('G' == (*c))) { ret = 1U; } return ret; } #endif static uint8_t StrFormatScanIsFormatStarting(char *c) { uint8_t ret = 1U; if ((*c != '%')) { ret = 0U; } else if (*(c + 1) == '%') { ret = 0U; } else { /*MISRA rule 15.7*/ } return ret; } static uint8_t StrFormatScanGetBase(uint8_t base, const char *s) { if (base == 0U) { if (s[0] == '0') { if ((s[1] == 'x') || (s[1] == 'X')) { base = 16; } else { base = 8; } } else { base = 10; } } return base; } static uint8_t StrFormatScanCheckSymbol(const char *p, int8_t *neg) { uint8_t len; switch (*p) { case '-': *neg = -1; len = 1; break; case '+': *neg = 1; len = 1; break; default: *neg = 1; len = 0; break; } return len; } static uint8_t StrFormatScanFillInteger(uint32_t flag, va_list *args_ptr, int32_t val) { #if SCANF_ADVANCED_ENABLE if (0U != (flag & (uint32_t)kSCANF_Suppress)) { return 0u; } switch (flag & (uint32_t)kSCANF_LengthMask) { case (uint32_t)kSCANF_LengthChar: if (0U != (flag & (uint32_t)kSCANF_TypeSinged)) { *va_arg(*args_ptr, signed char *) = (signed char)val; } else { *va_arg(*args_ptr, unsigned char *) = (unsigned char)val; } break; case (uint32_t)kSCANF_LengthShortInt: if (0U != (flag & (uint32_t)kSCANF_TypeSinged)) { *va_arg(*args_ptr, signed short *) = (signed short)val; } else { *va_arg(*args_ptr, unsigned short *) = (unsigned short)val; } break; case (uint32_t)kSCANF_LengthLongInt: if (0U != (flag & (uint32_t)kSCANF_TypeSinged)) { *va_arg(*args_ptr, signed long int *) = (signed long int)val; } else { *va_arg(*args_ptr, unsigned long int *) = (unsigned long int)val; } break; case (uint32_t)kSCANF_LengthLongLongInt: if (0U != (flag & (uint32_t)kSCANF_TypeSinged)) { *va_arg(*args_ptr, signed long long int *) = (signed long long int)val; } else { *va_arg(*args_ptr, unsigned long long int *) = (unsigned long long int)val; } break; default: /* The default type is the type int. */ if (0U != (flag & (uint32_t)kSCANF_TypeSinged)) { *va_arg(*args_ptr, signed int *) = (signed int)val; } else { *va_arg(*args_ptr, unsigned int *) = (unsigned int)val; } break; } #else /* The default type is the type int. */ if (0U != (flag & (uint32_t)kSCANF_TypeSinged)) { *va_arg(*args_ptr, signed int *) = (signed int)val; } else { *va_arg(*args_ptr, unsigned int *) = (unsigned int)val; } #endif /* SCANF_ADVANCED_ENABLE */ return 1u; } #if SCANF_FLOAT_ENABLE static uint8_t StrFormatScanFillFloat(uint32_t flag, va_list *args_ptr, double fnum) { #if SCANF_ADVANCED_ENABLE if (0U != (flag & (uint32_t)kSCANF_Suppress)) { return 0u; } else #endif /* SCANF_ADVANCED_ENABLE */ { if (0U != (flag & (uint32_t)kSCANF_LengthLongLongDouble)) { *va_arg(*args_ptr, double *) = fnum; } else { *va_arg(*args_ptr, float *) = (float)fnum; } return 1u; } } #endif /* SCANF_FLOAT_ENABLE */ static uint8_t StrFormatScanfStringHandling(char **str, uint32_t *flag, uint32_t *field_width, uint8_t *base) { uint8_t exitPending = 0U; char *c = *str; /* Loop to get full conversion specification. */ while (('\0' != (*c)) && (0U == (*flag & (uint32_t)kSCANF_DestMask))) { #if SCANF_ADVANCED_ENABLE if ('*' == (*c)) { if (0U != ((*flag) & (uint32_t)kSCANF_Suppress)) { /* Match failure. */ exitPending = 1U; } else { (*flag) |= (uint32_t)kSCANF_Suppress; } } else if ('h' == (*c)) { if (0U != ((*flag) & (uint32_t)kSCANF_LengthMask)) { /* Match failure. */ exitPending = 1U; } else { if (c[1] == 'h') { (*flag) |= (uint32_t)kSCANF_LengthChar; c++; } else { (*flag) |= (uint32_t)kSCANF_LengthShortInt; } } } else if ('l' == (*c)) { if (0U != ((*flag) & (uint32_t)kSCANF_LengthMask)) { /* Match failure. */ exitPending = 1U; } else { if (c[1] == 'l') { (*flag) |= (uint32_t)kSCANF_LengthLongLongInt; c++; } else { (*flag) |= (uint32_t)kSCANF_LengthLongInt; } } } else #endif /* SCANF_ADVANCED_ENABLE */ #if SCANF_FLOAT_ENABLE if ('L' == (*c)) { if (0U != ((*flag) & (uint32_t)kSCANF_LengthMask)) { /* Match failure. */ exitPending = 1U; } else { (*flag) |= (uint32_t)kSCANF_LengthLongLongDouble; } } else #endif /* SCANF_FLOAT_ENABLE */ if (((*c) >= '0') && ((*c) <= '9')) { { char *p; errno = 0; (*field_width) = strtoul(c, &p, 10); if (0 != errno) { *field_width = 0U; } c = p - 1; } } else if ('d' == (*c)) { (*base) = 10U; (*flag) |= (uint32_t)kSCANF_TypeSinged; (*flag) |= (uint32_t)kSCANF_DestInt; } else if ('u' == (*c)) { (*base) = 10U; (*flag) |= (uint32_t)kSCANF_DestInt; } else if ('o' == (*c)) { (*base) = 8U; (*flag) |= (uint32_t)kSCANF_DestInt; } else if (('x' == (*c))) { (*base) = 16U; (*flag) |= (uint32_t)kSCANF_DestInt; } else if ('X' == (*c)) { (*base) = 16U; (*flag) |= (uint32_t)kSCANF_DestInt; } else if ('i' == (*c)) { (*base) = 0U; (*flag) |= (uint32_t)kSCANF_DestInt; } #if SCANF_FLOAT_ENABLE else if (1U == StrFormatScanIsFloat(c)) { (*flag) |= (uint32_t)kSCANF_DestFloat; } #endif /* SCANF_FLOAT_ENABLE */ else if ('c' == (*c)) { (*flag) |= (uint32_t)kSCANF_DestChar; if (MAX_FIELD_WIDTH == (*field_width)) { (*field_width) = 1; } } else if ('s' == (*c)) { (*flag) |= (uint32_t)kSCANF_DestString; } else { exitPending = 1U; } if (1U == exitPending) { break; } else { c++; } } *str = c; return exitPending; } /*! * brief Converts an input line of ASCII characters based upon a provided * string format. * * param[in] line_ptr The input line of ASCII data. * param[in] format Format first points to the format string. * param[in] args_ptr The list of parameters. * * return Number of input items converted and assigned. * retval IO_EOF When line_ptr is empty string "". */ int StrFormatScanf(const char *line_ptr, char *format, va_list args_ptr) { uint8_t base; int8_t neg; /* Identifier for the format string. */ char *c = format; char *buf; /* Flag telling the conversion specification. */ uint32_t flag = 0; /* Filed width for the matching input streams. */ uint32_t field_width; /* How many arguments are assigned except the suppress. */ uint32_t nassigned = 0; /* How many characters are read from the input streams. */ uint32_t n_decode = 0; int32_t val; uint8_t added; uint8_t exitPending = 0; const char *s; #if SCANF_FLOAT_ENABLE char *s_temp; /* MISRA C-2012 Rule 11.3 */ #endif /* Identifier for the input string. */ const char *p = line_ptr; #if SCANF_FLOAT_ENABLE double fnum = 0.0; #endif /* SCANF_FLOAT_ENABLE */ /* Return EOF error before any conversion. */ if (*p == '\0') { return -1; } /* Decode directives. */ while (('\0' != (*c)) && ('\0' != (*p))) { /* Ignore all white-spaces in the format strings. */ if (0U != ScanIgnoreWhiteSpace((const char **)((void *)&c))) { n_decode += ScanIgnoreWhiteSpace(&p); } else if (0U == StrFormatScanIsFormatStarting(c)) { /* Ordinary characters. */ c++; if (*p == *c) { n_decode++; p++; c++; } else { /* Match failure. Misalignment with C99, the unmatched characters need to be pushed back to stream. * However, it is deserted now. */ break; } } else { /* convernsion specification */ c++; /* Reset. */ flag = 0; field_width = MAX_FIELD_WIDTH; base = 0; added = 0U; exitPending = StrFormatScanfStringHandling(&c, &flag, &field_width, &base); if (1U == exitPending) { /* Format strings are exhausted. */ break; } /* Matching strings in input streams and assign to argument. */ if ((flag & (uint32_t)kSCANF_DestMask) == (uint32_t)kSCANF_DestChar) { s = (const char *)p; buf = va_arg(args_ptr, char *); while ((0U != (field_width--)) #if SCANF_ADVANCED_ENABLE && ('\0' != (*p)) #endif ) { #if SCANF_ADVANCED_ENABLE if (0U != (flag & (uint32_t)kSCANF_Suppress)) { p++; } else #endif { *buf++ = *p++; #if SCANF_ADVANCED_ENABLE added = 1u; #endif } n_decode++; } #if SCANF_ADVANCED_ENABLE if (1u == added) #endif { nassigned++; } } else if ((flag & (uint32_t)kSCANF_DestMask) == (uint32_t)kSCANF_DestString) { n_decode += ScanIgnoreWhiteSpace(&p); s = p; buf = va_arg(args_ptr, char *); while ((0U != (field_width--)) && (*p != '\0') && (0U == ScanIsWhiteSpace(*p))) { #if SCANF_ADVANCED_ENABLE if (0U != (flag & (uint32_t)kSCANF_Suppress)) { p++; } else #endif { *buf++ = *p++; #if SCANF_ADVANCED_ENABLE added = 1u; #endif } n_decode++; } #if SCANF_ADVANCED_ENABLE if (1u == added) #endif { /* Add NULL to end of string. */ *buf = '\0'; nassigned++; } } else if ((flag & (uint32_t)kSCANF_DestMask) == (uint32_t)kSCANF_DestInt) { n_decode += ScanIgnoreWhiteSpace(&p); s = p; val = 0; base = StrFormatScanGetBase(base, s); added = StrFormatScanCheckSymbol(p, &neg); n_decode += added; p += added; field_width -= added; s = p; if (strlen(p) > field_width) { char temp[12]; char *tempEnd; (void)memcpy(temp, p, sizeof(temp) - 1U); temp[sizeof(temp) - 1U] = '\0'; errno = 0; val = (int32_t)strtoul(temp, &tempEnd, (int)base); if (0 != errno) { break; } p = p + (tempEnd - temp); } else { char *tempEnd; val = 0; errno = 0; val = (int32_t)strtoul(p, &tempEnd, (int)base); if (0 != errno) { break; } p = tempEnd; } n_decode += (uint32_t)p - (uint32_t)s; val *= neg; nassigned += StrFormatScanFillInteger(flag, &args_ptr, val); } #if SCANF_FLOAT_ENABLE else if ((flag & (uint32_t)kSCANF_DestMask) == (uint32_t)kSCANF_DestFloat) { n_decode += ScanIgnoreWhiteSpace(&p); fnum = 0.0; errno = 0; fnum = strtod(p, (char **)&s_temp); s = s_temp; /* MISRA C-2012 Rule 11.3 */ /* MISRA C-2012 Rule 22.9 */ if (0 != errno) { break; } if ((fnum < HUGE_VAL) && (fnum > -HUGE_VAL)) { n_decode = (uint32_t)n_decode + (uint32_t)s - (uint32_t)p; p = s; nassigned += StrFormatScanFillFloat(flag, &args_ptr, fnum); } } #endif /* SCANF_FLOAT_ENABLE */ else { break; } } } return (int)nassigned; }