blob: d48377aeae3d5b756bd3aee92929c60d17317e06 [file] [log] [blame]
Qiufang Dai35c31332020-05-13 15:29:06 +08001/*
2Amazon FreeRTOS OTA Agent V0.9.4
3Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
5Permission is hereby granted, free of charge, to any person obtaining a copy of
6this software and associated documentation files (the "Software"), to deal in
7the Software without restriction, including without limitation the rights to
8use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9the Software, and to permit persons to whom the Software is furnished to do so,
10subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 http://aws.amazon.com/freertos
23 http://www.FreeRTOS.org
24*/
25
26
27#include <stdarg.h>
28#include "ctype.h"
29#include "aws_rsprintf.h"
30#include "FreeRTOS.h"
31#include <string.h>
32#include <stdio.h>
33#include <stdlib.h>
34
35#ifdef USE_EFFICIENT_PRINT_SPOOLER
36#include "printTask.h"
37#endif
38
39
40static char* prvMemPrint(u8* ptr, u32 len, bool_t bUseIsPrint, bool_t bConsoleOut, char* acSmallBuf, char* dptr);
41
42#ifdef USE_EFFICIENT_PRINT_SPOOLER
43/* This macro will check if console output is selected and spool the data
44* in the specified buffer (arg1) to the print queue and then reset the
45* rvsprintf destination pointer (arg2) to the start of that buffer. The
46* purpose of this is to build and spool the formatted string in small
47* chunks to minimize memory usage. The result of the queue function call
48* is returned in arg3.
49*/
50#define SPOOL_TO_PRINT_QUEUE_IF_CONSOLE( pcBuffer, dptr, xRes ) do \
51{ \
52 if ( bConsoleOut == pdTRUE ) \
53 { \
54 xRes = PRINT_QueueChars( pcBuffer, ( uint32_t) ( dptr - pcBuffer ) ); \
55 /* Reset destination pointer to start of debug buffer so \
56 * we can output small chunks at a time. */ \
57 dptr = pcBuffer; \
58 } \
59} while (0);
60
61/* This macro will spool the data in the specified buffer (arg1) to the print
62* queue and then reset the rvsprintf destination pointer (arg2) to the start
63* of that buffer. The purpose of this is to build and spool the formatted
64* string in small chunks to minimize memory usage.
65* The result of the queue function call is returned in arg3.
66*/
67#define SPOOL_TO_PRINT_QUEUE( pcBuffer, dptr, xRes ) do\
68{ \
69 xRes = PRINT_QueueChars( pcBuffer, ( uint32_t) ( dptr - pcBuffer ) ); \
70 /* Reset destination pointer to start of debug buffer so \
71 * we can output small chunks at a time. */ \
72 dptr = pcBuffer; \
73} while (0);
74#else
75#define SPOOL_TO_PRINT_QUEUE_IF_CONSOLE(...)
76#define SPOOL_TO_PRINT_QUEUE(...)
77#endif
78
79/* For spooling to the console, the rsprintf function requires a tiny buffer
80* on the stack just big enough to hold the minimum sized atomic unit created
81* by the print formatter. It happens to be the size of the 32 bit signed
82* decimal output. */
83#define kSmallPrintBufferSize kS32_MaxTextSize
84
85
86char *stpcpy(register char *dest, register const char *src)
87{
88 register char *d = dest;
89 register const char *s = src;
90
91 while ((*d++ = *s++));
92 return d - 1;
93}
94
95
96
97/******************************************************************************
98Function: szU32()
99
100Construct an ASCII zero terminated string of an unsigned 32 bit value in
101decimal.
102
103Args: dest = pointer to destination string.
104val = 32 bit value to convert.
105iFieldWidth = fixed width of string or 0 to suppress leading zero's.
106
107Returns: pointer to the end of the string (the terminating zero).
108*****************************************************************************/
109char *szU32(char* dest, u32 val, u8 iFieldWidth) {
110
111 char buf[kU32_MaxPlaces];
112 char *dptr;
113 u32 d, n;
114
115 if (iFieldWidth > kU32_MaxPlaces) {
116
117 iFieldWidth = kU32_MaxPlaces;
118 }
119 dptr = buf;
120 for (d = 1000000000; d > 1; d /= 10) {
121
122 n = val / d;
123 *dptr++ = '0' + n;
124 val -= (n * d);
125 }
126 *dptr++ = '0' + val;
127
128 if (iFieldWidth == 0) {
129
130 /* Suppress leading zeros... */
131 for (d = 0; d < (kU32_MaxPlaces - 1); d++) {
132
133 if (buf[d] != '0') break;
134 }
135 }
136 else {
137
138 /* Use a specific field width... */
139 d = kU32_MaxPlaces - iFieldWidth;
140 }
141 do {
142
143 *dest++ = buf[d];
144 } while (d++ < (kU32_MaxPlaces - 1)); /* Write out up to the end of 10 digits. */
145 *dest = 0; /* Zero terminate the string. */
146 return dest; /* Return pointer to the zero terminator for easy concatenation. */
147}
148
149
150
151
152
153/******************************************************************************
154Function: szS32()
155
156Construct an ASCII zero terminated string of a signed 32 bit value in
157decimal. This will prepend a minus sign if the value is negative.
158
159Args: dest = pointer to destination string.
160val = 32 bit value to convert.
161iFieldWidth = fixed width of string or 0 to suppress leading zero's.
162
163Returns: pointer to the end of the string (the terminating zero).
164*****************************************************************************/
165char *szS32(char* dest, s32 val, u8 iFieldWidth) {
166
167 if (val < 0) {
168
169 *dest++ = '-';
170 val = -val;
171 }
172 return szU32(dest, val, iFieldWidth);
173}
174
175
176
177/******************************************************************************
178Function: szH32()
179
180Construct an ASCII zero terminated string of a 32 bit value in hex.
181
182Args: dest = pointer to destination string.
183val = 32 bit value to convert.
184iFieldWidth = fixed width of string or 0 to suppress leading zero's.
185
186Returns: pointer to the end of the string (the terminating zero).
187*****************************************************************************/
188#if 0
189char *szH32(char* dest, u32 val, u8 iFieldWidth) {
190
191 char buf[kH32_MaxPlaces];
192 char *dptr;
193 u32 d, n;
194
195 if (iFieldWidth > kH32_MaxPlaces) {
196
197 iFieldWidth = kH32_MaxPlaces;
198 }
199 dptr = &buf[kH32_MaxPlaces - 1];
200 for (d = 0; d < kH32_MaxPlaces; d++) {
201
202 n = val & 0xf;
203 if (n > 9) {
204
205 *dptr-- = 'a' - 10 + n;
206 }
207 else {
208
209 *dptr-- = '0' + n;
210 }
211 val >>= 4;
212 }
213 if (iFieldWidth == 0) {
214
215 /* Suppress leading zeros... */
216 for (d = 0; d < (kH32_MaxPlaces - 1); d++) {
217
218 if (buf[d] != '0') break;
219 }
220 }
221 else {
222
223 /* Use a specific field width... */
224 d = kH32_MaxPlaces - iFieldWidth;
225 }
226 do {
227
228 *dest++ = buf[d];
229 } while (d++ < (kH32_MaxPlaces - 1)); /* Write out up to the end of all digit places. */
230 *dest = 0; /* Zero terminate the string. */
231 return dest; /* Return pointer to the zero terminator for easy concatenation. */
232}
233#else
234
235char *szH32(char* dest, u32 val, u8 iFieldWidth) {
236
237 char buf[kH32_MaxPlaces];
238 char *dptr;
239 u32 d, n;
240
241 if (iFieldWidth > kH32_MaxPlaces) {
242
243 iFieldWidth = kH32_MaxPlaces;
244 }
245 dptr = buf;
246 for (d = 0x10000000; d > 0; d >>= 4) {
247
248 n = val / d;
249 if (n > 9) {
250
251 *dptr++ = 'a' - 10 + n;
252 }
253 else {
254
255 *dptr++ = '0' + n;
256 }
257 val -= (n * d);
258 }
259
260 if (iFieldWidth == 0) {
261
262 /* Suppress leading zeros... */
263 for (d = 0; d < (kH32_MaxPlaces - 1); d++) {
264
265 if (buf[d] != '0') break;
266 }
267 }
268 else {
269
270 /* Use a specific field width... */
271 d = kH32_MaxPlaces - iFieldWidth;
272 }
273 do {
274
275 *dest++ = buf[d];
276 } while (d++ < (kH32_MaxPlaces - 1)); /* Write out up to the end of 8 digits. */
277 *dest = 0; /* Zero terminate the string. */
278 return dest; /* Return pointer to the zero terminator for easy concatenation. */
279}
280#endif
281
282
283
284/******************************************************************************
285Function: rsprintf() "reduced sprintf"
286
287Custom limited version of sprintf that doesn't require lots of stack space
288per thread and only supports the functionality we need plus a few bonus
289features. There is no floating point support! This is NOT meant to be a
290full replacement for sprintf(). If you really need that, then use sprintf()
291but be aware of the stack space requirement. If you are also supporting
292USE_EFFICIENT_PRINT_SPOOLER, this implementation will automatically output
293to the console UART if the destination buffer pointer is NULL. If that
294support is not included then it will simply do nothing and return with a
295zero length result if the destination pointer is NULL.
296
297Args: dest = pointer to destination buffer.
298fmt = pointer to format string similar to sprintf.
299
300Returns: u32 = the number of characters written to the buffer not
301including the terminating zero.
302*****************************************************************************/
303u32 rsprintf(char *dest, const char *fmt, ...) {
304
305 va_list args;
306 va_start(args, fmt);
307 u32 iLen = rvsprintf(dest, fmt, args);
308 va_end(args);
309 return iLen;
310}
311
312/* Formatted print to the Console. Prepare the rsprintf to print to the
313* console UART instead of a user destination buffer.
314*/
315u32 cprintf(const char *fmt, ...) {
316
317#ifdef USE_EFFICIENT_PRINT_SPOOLER
318 va_list args;
319 va_start(args, fmt);
320 u32 iLen = rvsprintf(NULL, fmt, args);
321 va_end(args);
322 return iLen;
323#else
324 (void)fmt;
325 return 0;
326#endif
327}
328
329
330u32 rvsprintf(char *dest, const char *fmt, va_list va) {
331
332 char c;
333 u32 iWidth = 0;
334 static const char acNullMsg[] = "{NULL}";
335 char acSmallBuf[kSmallPrintBufferSize];
336 bool_t bConsoleOut = pdFALSE;
337 BaseType_t xResult;
338
339 char *dptr;
340#ifdef USE_EFFICIENT_PRINT_SPOOLER
341 if (dest == NULL) {
342
343 bConsoleOut = pdTRUE;
344 dptr = acSmallBuf;
345 }
346#else
347 if (dest == NULL)
348 {
349 return 0;
350 }
351#endif
352 else {
353 dptr = dest;
354 }
355
356 while ((c = *fmt++) != 0) {
357
358 switch (c) {
359
360 case '%':
361 {
362 c = *fmt++;
363 /* Ignore unsupported options. */
364 if ((c == 'l') || (c == 'L') || (c == '.'))
365 {
366 c = *fmt++;
367 }
368 if (c == '*')
369 {
370 /* Pull the field width parameter from the stack. */
371 iWidth = va_arg(va, u32);
372 c = *fmt++;
373 }
374 else {
375 /* Check for field width. */
376 if ((c >= '0') && (c <= '9')) {
377
378 iWidth = strtoul(fmt - 1, (char**)&fmt, 10);
379 c = *fmt++;
380 }
381 else {
382 iWidth = 0; /* Default to suppress leading zero's of numeric values. */
383 }
384 }
385 switch (c)
386 {
387 /* Ignore next parameter. */
388 case '~':
389 {
390 va_arg(va, u32);
391 break;
392 }
393 /* Unsigned 32 bit decimal output. */
394 case 'd':
395 case 'D':
396 {
397 s32 v = va_arg(va, s32);
398 dptr = szS32(dptr, v, iWidth);
399 break;
400 }
401 case 'u':
402 case 'U':
403 {
404 u32 v = va_arg(va, u32);
405 dptr = szU32(dptr, v, iWidth);
406 break;
407 }
408
409 /* 32 bit hexadecimal output. */
410 case 'x':
411 case 'X':
412 {
413 u32 v = va_arg(va, u32);
414 dptr = szH32(dptr, v, iWidth);
415 break;
416 }
417#ifdef SUPPORT_HEX_CHAR_DEBUG_PRINT
418 case 'C':
419 {
420 char c2 = va_arg(va, int);
421 if (isprint((int)c2) == 0)
422 {
423 *dptr++ = '[';
424 dptr = szH32(dptr, c2, 2);
425 *dptr++ = ']';
426 }
427 else
428 {
429 *dptr++ = c2;
430 }
431 break;
432 }
433#else
434 case 'C':
435#endif
436 case 'c':
437 {
438 char c2 = va_arg(va, int);
439 *dptr++ = c2;
440 break;
441 }
442 /* String insertion (nested format specifiers are not handled). */
443 case 's':
444 {
445 char *p = va_arg(va, char*);
446 if (p != NULL)
447 {
448 /* If we already have the field width, print with width limit. */
449 if (iWidth != 0)
450 {
451 dptr = prvMemPrint((u8*)p, iWidth, pdFALSE, bConsoleOut, acSmallBuf, dptr);
452 break;
453 }
454#ifdef USE_EFFICIENT_PRINT_SPOOLER
455 /* We don't know the width so print to zero terminator. */
456 else if (bConsoleOut == pdTRUE)
457 {
458 char* eptr;
459 do
460 {
461 /* Suppress warning of memccpy name change in Windows since the
462 embedded targets only support the original name. */
463 #pragma warning(suppress : 4996)
464 eptr = (char*) memccpy(dptr, p, '\0', kSmallPrintBufferSize);
465 /* eptr either points to the 0 or NULL if 0 wasn't found within the range limit.
466 * If 0 wasn't found, we want to spool the entire small buffer so we can free it up.
467 */
468 if (eptr == NULL)
469 {
470 p += kSmallPrintBufferSize;
471 /* Set the destination pointer to the end of the buffer for spooling. */
472 dptr = acSmallBuf + kSmallPrintBufferSize;
473 }
474 else
475 {
476 dptr = eptr;
477 }
478 SPOOL_TO_PRINT_QUEUE(acSmallBuf, dptr, xResult);
479 } while ((xResult == pdPASS) && (eptr == NULL));
480 }
481#endif
482 else
483 {
484 dptr = stpcpy(dptr, p);
485 }
486 }
487 else {
488
489 dptr = stpcpy(dptr, acNullMsg);
490 }
491 break;
492 }
493 /* Special hexdump. Takes length followed by pointer to data. */
494 case 'H':
495 {
496 iWidth = va_arg(va, u32);
497 u8 *p = va_arg(va, u8*);
498 xResult = pdPASS;
499 while ((xResult == pdPASS) && iWidth--) {
500
501 dptr = szH32(dptr, *p++, 2);
502 SPOOL_TO_PRINT_QUEUE_IF_CONSOLE(acSmallBuf, dptr, xResult);
503 }
504 break;
505 }
506 /* Mem dump with non-printable character substitutions. */
507 case '^':
508 {
509 iWidth = va_arg(va, u32);
510 u8 *p = va_arg(va, u8*);
511 dptr = prvMemPrint(p, iWidth, pdTRUE, bConsoleOut, acSmallBuf, dptr);
512 break;
513 }
514 default:
515 {
516 *dptr++ = c;
517 break;
518 }
519 }
520 break;
521 }
522 default:
523 {
524 *dptr++ = c;
525 break;
526 }
527 }
528 SPOOL_TO_PRINT_QUEUE_IF_CONSOLE(acSmallBuf, dptr, xResult);
529 }
530 *dptr = 0; /* Zero terminate the string. */
531 return (u32)(dptr - dest); /* Return number of characters written. */
532}
533
534
535static char* prvMemPrint(u8* ptr, u32 len, bool_t bUseIsPrint, bool_t bConsoleOut, char* acSmallBuf, char* dptr)
536{
537#ifndef USE_EFFICIENT_PRINT_SPOOLER
538 (void) acSmallBuf;
539#else
540 BaseType_t xResult;
541#endif
542
543 u32 lim = kSmallPrintBufferSize;
544 while (len--)
545 {
546 u8 c3 = *ptr++;
547 if ((bUseIsPrint == pdTRUE) && isprint((int)c3) == 0)
548 {
549 c3 = '.';
550 }
551 *dptr++ = c3;
552 if (bConsoleOut == pdTRUE)
553 {
554 if (--lim == 0)
555 {
556 SPOOL_TO_PRINT_QUEUE(acSmallBuf, dptr, xResult);
557 lim = kSmallPrintBufferSize;
558 }
559 }
560 }
561 return dptr;
562}
563