| /* |
| Amazon FreeRTOS OTA Agent V0.9.4 |
| Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy of |
| this software and associated documentation files (the "Software"), to deal in |
| the Software without restriction, including without limitation the rights to |
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
| the Software, and to permit persons to whom the Software is furnished to do so, |
| subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in all |
| copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
| FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| http://aws.amazon.com/freertos |
| http://www.FreeRTOS.org |
| */ |
| |
| |
| #include <stdarg.h> |
| #include "ctype.h" |
| #include "aws_rsprintf.h" |
| #include "FreeRTOS.h" |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #ifdef USE_EFFICIENT_PRINT_SPOOLER |
| #include "printTask.h" |
| #endif |
| |
| |
| static char* prvMemPrint(u8* ptr, u32 len, bool_t bUseIsPrint, bool_t bConsoleOut, char* acSmallBuf, char* dptr); |
| |
| #ifdef USE_EFFICIENT_PRINT_SPOOLER |
| /* This macro will check if console output is selected and spool the data |
| * in the specified buffer (arg1) to the print queue and then reset the |
| * rvsprintf destination pointer (arg2) to the start of that buffer. The |
| * purpose of this is to build and spool the formatted string in small |
| * chunks to minimize memory usage. The result of the queue function call |
| * is returned in arg3. |
| */ |
| #define SPOOL_TO_PRINT_QUEUE_IF_CONSOLE( pcBuffer, dptr, xRes ) do \ |
| { \ |
| if ( bConsoleOut == pdTRUE ) \ |
| { \ |
| xRes = PRINT_QueueChars( pcBuffer, ( uint32_t) ( dptr - pcBuffer ) ); \ |
| /* Reset destination pointer to start of debug buffer so \ |
| * we can output small chunks at a time. */ \ |
| dptr = pcBuffer; \ |
| } \ |
| } while (0); |
| |
| /* This macro will spool the data in the specified buffer (arg1) to the print |
| * queue and then reset the rvsprintf destination pointer (arg2) to the start |
| * of that buffer. The purpose of this is to build and spool the formatted |
| * string in small chunks to minimize memory usage. |
| * The result of the queue function call is returned in arg3. |
| */ |
| #define SPOOL_TO_PRINT_QUEUE( pcBuffer, dptr, xRes ) do\ |
| { \ |
| xRes = PRINT_QueueChars( pcBuffer, ( uint32_t) ( dptr - pcBuffer ) ); \ |
| /* Reset destination pointer to start of debug buffer so \ |
| * we can output small chunks at a time. */ \ |
| dptr = pcBuffer; \ |
| } while (0); |
| #else |
| #define SPOOL_TO_PRINT_QUEUE_IF_CONSOLE(...) |
| #define SPOOL_TO_PRINT_QUEUE(...) |
| #endif |
| |
| /* For spooling to the console, the rsprintf function requires a tiny buffer |
| * on the stack just big enough to hold the minimum sized atomic unit created |
| * by the print formatter. It happens to be the size of the 32 bit signed |
| * decimal output. */ |
| #define kSmallPrintBufferSize kS32_MaxTextSize |
| |
| |
| char *stpcpy(register char *dest, register const char *src) |
| { |
| register char *d = dest; |
| register const char *s = src; |
| |
| while ((*d++ = *s++)); |
| return d - 1; |
| } |
| |
| |
| |
| /****************************************************************************** |
| Function: szU32() |
| |
| Construct an ASCII zero terminated string of an unsigned 32 bit value in |
| decimal. |
| |
| Args: dest = pointer to destination string. |
| val = 32 bit value to convert. |
| iFieldWidth = fixed width of string or 0 to suppress leading zero's. |
| |
| Returns: pointer to the end of the string (the terminating zero). |
| *****************************************************************************/ |
| char *szU32(char* dest, u32 val, u8 iFieldWidth) { |
| |
| char buf[kU32_MaxPlaces]; |
| char *dptr; |
| u32 d, n; |
| |
| if (iFieldWidth > kU32_MaxPlaces) { |
| |
| iFieldWidth = kU32_MaxPlaces; |
| } |
| dptr = buf; |
| for (d = 1000000000; d > 1; d /= 10) { |
| |
| n = val / d; |
| *dptr++ = '0' + n; |
| val -= (n * d); |
| } |
| *dptr++ = '0' + val; |
| |
| if (iFieldWidth == 0) { |
| |
| /* Suppress leading zeros... */ |
| for (d = 0; d < (kU32_MaxPlaces - 1); d++) { |
| |
| if (buf[d] != '0') break; |
| } |
| } |
| else { |
| |
| /* Use a specific field width... */ |
| d = kU32_MaxPlaces - iFieldWidth; |
| } |
| do { |
| |
| *dest++ = buf[d]; |
| } while (d++ < (kU32_MaxPlaces - 1)); /* Write out up to the end of 10 digits. */ |
| *dest = 0; /* Zero terminate the string. */ |
| return dest; /* Return pointer to the zero terminator for easy concatenation. */ |
| } |
| |
| |
| |
| |
| |
| /****************************************************************************** |
| Function: szS32() |
| |
| Construct an ASCII zero terminated string of a signed 32 bit value in |
| decimal. This will prepend a minus sign if the value is negative. |
| |
| Args: dest = pointer to destination string. |
| val = 32 bit value to convert. |
| iFieldWidth = fixed width of string or 0 to suppress leading zero's. |
| |
| Returns: pointer to the end of the string (the terminating zero). |
| *****************************************************************************/ |
| char *szS32(char* dest, s32 val, u8 iFieldWidth) { |
| |
| if (val < 0) { |
| |
| *dest++ = '-'; |
| val = -val; |
| } |
| return szU32(dest, val, iFieldWidth); |
| } |
| |
| |
| |
| /****************************************************************************** |
| Function: szH32() |
| |
| Construct an ASCII zero terminated string of a 32 bit value in hex. |
| |
| Args: dest = pointer to destination string. |
| val = 32 bit value to convert. |
| iFieldWidth = fixed width of string or 0 to suppress leading zero's. |
| |
| Returns: pointer to the end of the string (the terminating zero). |
| *****************************************************************************/ |
| #if 0 |
| char *szH32(char* dest, u32 val, u8 iFieldWidth) { |
| |
| char buf[kH32_MaxPlaces]; |
| char *dptr; |
| u32 d, n; |
| |
| if (iFieldWidth > kH32_MaxPlaces) { |
| |
| iFieldWidth = kH32_MaxPlaces; |
| } |
| dptr = &buf[kH32_MaxPlaces - 1]; |
| for (d = 0; d < kH32_MaxPlaces; d++) { |
| |
| n = val & 0xf; |
| if (n > 9) { |
| |
| *dptr-- = 'a' - 10 + n; |
| } |
| else { |
| |
| *dptr-- = '0' + n; |
| } |
| val >>= 4; |
| } |
| if (iFieldWidth == 0) { |
| |
| /* Suppress leading zeros... */ |
| for (d = 0; d < (kH32_MaxPlaces - 1); d++) { |
| |
| if (buf[d] != '0') break; |
| } |
| } |
| else { |
| |
| /* Use a specific field width... */ |
| d = kH32_MaxPlaces - iFieldWidth; |
| } |
| do { |
| |
| *dest++ = buf[d]; |
| } while (d++ < (kH32_MaxPlaces - 1)); /* Write out up to the end of all digit places. */ |
| *dest = 0; /* Zero terminate the string. */ |
| return dest; /* Return pointer to the zero terminator for easy concatenation. */ |
| } |
| #else |
| |
| char *szH32(char* dest, u32 val, u8 iFieldWidth) { |
| |
| char buf[kH32_MaxPlaces]; |
| char *dptr; |
| u32 d, n; |
| |
| if (iFieldWidth > kH32_MaxPlaces) { |
| |
| iFieldWidth = kH32_MaxPlaces; |
| } |
| dptr = buf; |
| for (d = 0x10000000; d > 0; d >>= 4) { |
| |
| n = val / d; |
| if (n > 9) { |
| |
| *dptr++ = 'a' - 10 + n; |
| } |
| else { |
| |
| *dptr++ = '0' + n; |
| } |
| val -= (n * d); |
| } |
| |
| if (iFieldWidth == 0) { |
| |
| /* Suppress leading zeros... */ |
| for (d = 0; d < (kH32_MaxPlaces - 1); d++) { |
| |
| if (buf[d] != '0') break; |
| } |
| } |
| else { |
| |
| /* Use a specific field width... */ |
| d = kH32_MaxPlaces - iFieldWidth; |
| } |
| do { |
| |
| *dest++ = buf[d]; |
| } while (d++ < (kH32_MaxPlaces - 1)); /* Write out up to the end of 8 digits. */ |
| *dest = 0; /* Zero terminate the string. */ |
| return dest; /* Return pointer to the zero terminator for easy concatenation. */ |
| } |
| #endif |
| |
| |
| |
| /****************************************************************************** |
| Function: rsprintf() "reduced sprintf" |
| |
| Custom limited version of sprintf that doesn't require lots of stack space |
| per thread and only supports the functionality we need plus a few bonus |
| features. There is no floating point support! This is NOT meant to be a |
| full replacement for sprintf(). If you really need that, then use sprintf() |
| but be aware of the stack space requirement. If you are also supporting |
| USE_EFFICIENT_PRINT_SPOOLER, this implementation will automatically output |
| to the console UART if the destination buffer pointer is NULL. If that |
| support is not included then it will simply do nothing and return with a |
| zero length result if the destination pointer is NULL. |
| |
| Args: dest = pointer to destination buffer. |
| fmt = pointer to format string similar to sprintf. |
| |
| Returns: u32 = the number of characters written to the buffer not |
| including the terminating zero. |
| *****************************************************************************/ |
| u32 rsprintf(char *dest, const char *fmt, ...) { |
| |
| va_list args; |
| va_start(args, fmt); |
| u32 iLen = rvsprintf(dest, fmt, args); |
| va_end(args); |
| return iLen; |
| } |
| |
| /* Formatted print to the Console. Prepare the rsprintf to print to the |
| * console UART instead of a user destination buffer. |
| */ |
| u32 cprintf(const char *fmt, ...) { |
| |
| #ifdef USE_EFFICIENT_PRINT_SPOOLER |
| va_list args; |
| va_start(args, fmt); |
| u32 iLen = rvsprintf(NULL, fmt, args); |
| va_end(args); |
| return iLen; |
| #else |
| (void)fmt; |
| return 0; |
| #endif |
| } |
| |
| |
| u32 rvsprintf(char *dest, const char *fmt, va_list va) { |
| |
| char c; |
| u32 iWidth = 0; |
| static const char acNullMsg[] = "{NULL}"; |
| char acSmallBuf[kSmallPrintBufferSize]; |
| bool_t bConsoleOut = pdFALSE; |
| BaseType_t xResult; |
| |
| char *dptr; |
| #ifdef USE_EFFICIENT_PRINT_SPOOLER |
| if (dest == NULL) { |
| |
| bConsoleOut = pdTRUE; |
| dptr = acSmallBuf; |
| } |
| #else |
| if (dest == NULL) |
| { |
| return 0; |
| } |
| #endif |
| else { |
| dptr = dest; |
| } |
| |
| while ((c = *fmt++) != 0) { |
| |
| switch (c) { |
| |
| case '%': |
| { |
| c = *fmt++; |
| /* Ignore unsupported options. */ |
| if ((c == 'l') || (c == 'L') || (c == '.')) |
| { |
| c = *fmt++; |
| } |
| if (c == '*') |
| { |
| /* Pull the field width parameter from the stack. */ |
| iWidth = va_arg(va, u32); |
| c = *fmt++; |
| } |
| else { |
| /* Check for field width. */ |
| if ((c >= '0') && (c <= '9')) { |
| |
| iWidth = strtoul(fmt - 1, (char**)&fmt, 10); |
| c = *fmt++; |
| } |
| else { |
| iWidth = 0; /* Default to suppress leading zero's of numeric values. */ |
| } |
| } |
| switch (c) |
| { |
| /* Ignore next parameter. */ |
| case '~': |
| { |
| va_arg(va, u32); |
| break; |
| } |
| /* Unsigned 32 bit decimal output. */ |
| case 'd': |
| case 'D': |
| { |
| s32 v = va_arg(va, s32); |
| dptr = szS32(dptr, v, iWidth); |
| break; |
| } |
| case 'u': |
| case 'U': |
| { |
| u32 v = va_arg(va, u32); |
| dptr = szU32(dptr, v, iWidth); |
| break; |
| } |
| |
| /* 32 bit hexadecimal output. */ |
| case 'x': |
| case 'X': |
| { |
| u32 v = va_arg(va, u32); |
| dptr = szH32(dptr, v, iWidth); |
| break; |
| } |
| #ifdef SUPPORT_HEX_CHAR_DEBUG_PRINT |
| case 'C': |
| { |
| char c2 = va_arg(va, int); |
| if (isprint((int)c2) == 0) |
| { |
| *dptr++ = '['; |
| dptr = szH32(dptr, c2, 2); |
| *dptr++ = ']'; |
| } |
| else |
| { |
| *dptr++ = c2; |
| } |
| break; |
| } |
| #else |
| case 'C': |
| #endif |
| case 'c': |
| { |
| char c2 = va_arg(va, int); |
| *dptr++ = c2; |
| break; |
| } |
| /* String insertion (nested format specifiers are not handled). */ |
| case 's': |
| { |
| char *p = va_arg(va, char*); |
| if (p != NULL) |
| { |
| /* If we already have the field width, print with width limit. */ |
| if (iWidth != 0) |
| { |
| dptr = prvMemPrint((u8*)p, iWidth, pdFALSE, bConsoleOut, acSmallBuf, dptr); |
| break; |
| } |
| #ifdef USE_EFFICIENT_PRINT_SPOOLER |
| /* We don't know the width so print to zero terminator. */ |
| else if (bConsoleOut == pdTRUE) |
| { |
| char* eptr; |
| do |
| { |
| /* Suppress warning of memccpy name change in Windows since the |
| embedded targets only support the original name. */ |
| #pragma warning(suppress : 4996) |
| eptr = (char*) memccpy(dptr, p, '\0', kSmallPrintBufferSize); |
| /* eptr either points to the 0 or NULL if 0 wasn't found within the range limit. |
| * If 0 wasn't found, we want to spool the entire small buffer so we can free it up. |
| */ |
| if (eptr == NULL) |
| { |
| p += kSmallPrintBufferSize; |
| /* Set the destination pointer to the end of the buffer for spooling. */ |
| dptr = acSmallBuf + kSmallPrintBufferSize; |
| } |
| else |
| { |
| dptr = eptr; |
| } |
| SPOOL_TO_PRINT_QUEUE(acSmallBuf, dptr, xResult); |
| } while ((xResult == pdPASS) && (eptr == NULL)); |
| } |
| #endif |
| else |
| { |
| dptr = stpcpy(dptr, p); |
| } |
| } |
| else { |
| |
| dptr = stpcpy(dptr, acNullMsg); |
| } |
| break; |
| } |
| /* Special hexdump. Takes length followed by pointer to data. */ |
| case 'H': |
| { |
| iWidth = va_arg(va, u32); |
| u8 *p = va_arg(va, u8*); |
| xResult = pdPASS; |
| while ((xResult == pdPASS) && iWidth--) { |
| |
| dptr = szH32(dptr, *p++, 2); |
| SPOOL_TO_PRINT_QUEUE_IF_CONSOLE(acSmallBuf, dptr, xResult); |
| } |
| break; |
| } |
| /* Mem dump with non-printable character substitutions. */ |
| case '^': |
| { |
| iWidth = va_arg(va, u32); |
| u8 *p = va_arg(va, u8*); |
| dptr = prvMemPrint(p, iWidth, pdTRUE, bConsoleOut, acSmallBuf, dptr); |
| break; |
| } |
| default: |
| { |
| *dptr++ = c; |
| break; |
| } |
| } |
| break; |
| } |
| default: |
| { |
| *dptr++ = c; |
| break; |
| } |
| } |
| SPOOL_TO_PRINT_QUEUE_IF_CONSOLE(acSmallBuf, dptr, xResult); |
| } |
| *dptr = 0; /* Zero terminate the string. */ |
| return (u32)(dptr - dest); /* Return number of characters written. */ |
| } |
| |
| |
| static char* prvMemPrint(u8* ptr, u32 len, bool_t bUseIsPrint, bool_t bConsoleOut, char* acSmallBuf, char* dptr) |
| { |
| #ifndef USE_EFFICIENT_PRINT_SPOOLER |
| (void) acSmallBuf; |
| #else |
| BaseType_t xResult; |
| #endif |
| |
| u32 lim = kSmallPrintBufferSize; |
| while (len--) |
| { |
| u8 c3 = *ptr++; |
| if ((bUseIsPrint == pdTRUE) && isprint((int)c3) == 0) |
| { |
| c3 = '.'; |
| } |
| *dptr++ = c3; |
| if (bConsoleOut == pdTRUE) |
| { |
| if (--lim == 0) |
| { |
| SPOOL_TO_PRINT_QUEUE(acSmallBuf, dptr, xResult); |
| lim = kSmallPrintBufferSize; |
| } |
| } |
| } |
| return dptr; |
| } |
| |