]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/Reliance-Edge/tests/util/printf.c
Update Reliance Edge fail safe file system to the latest version.
[freertos] / FreeRTOS-Plus / Source / Reliance-Edge / tests / util / printf.c
1 /*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
2
3                    Copyright (c) 2014-2015 Datalight, Inc.
4                        All Rights Reserved Worldwide.
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; use version 2 of the License.
9
10     This program is distributed in the hope that it will be useful,
11     but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
12     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*  Businesses and individuals that for commercial or other reasons cannot
20     comply with the terms of the GPLv2 license may obtain a commercial license
21     before incorporating Reliance Edge into proprietary software for
22     distribution in any form.  Visit http://www.datalight.com/reliance-edge for
23     more information.
24 */
25 /** @file
26     @brief Implements functions for printing.
27
28     These functions are intended to be used in portable test code, which cannot
29     assume the standard I/O functions will be available.  Similar to their ANSI
30     C counterparts, these functions allow formatting text strings and (if the
31     configuration allows it) outputing formatted text.  The latter ability
32     relies on the RedOsOutputString() OS service function.
33
34     Do *not* use these functions in code which can safely assume the standard
35     I/O functions are available (e.g., in host tools code).
36
37     Do *not* use these functions from within the file system driver.  These
38     functions use variable arguments and thus are not MISRA-C:2012 compliant.
39 */
40 #include <redfs.h>
41 #include <redtestutils.h>
42 #include <limits.h>
43 #include <stdarg.h>
44
45
46 /** @brief Maximum number of bytes of output supported by RedPrintf().
47
48     Typically only Datalight code uses these functions, and that could should be
49     written to respect this limit, so it should not normally be necessary to
50     adjust this value.
51 */
52 #define OUTPUT_BUFFER_SIZE 256U
53
54
55 typedef enum
56 {
57     PRFMT_UNKNOWN = 0,
58     PRFMT_CHAR,
59     PRFMT_ANSISTRING,
60     PRFMT_SIGNED8BIT,
61     PRFMT_UNSIGNED8BIT,
62     PRFMT_SIGNED16BIT,
63     PRFMT_UNSIGNED16BIT,
64     PRFMT_SIGNED32BIT,
65     PRFMT_UNSIGNED32BIT,
66     PRFMT_SIGNED64BIT,
67     PRFMT_UNSIGNED64BIT,
68     PRFMT_HEX8BIT,
69     PRFMT_HEX16BIT,
70     PRFMT_HEX32BIT,
71     PRFMT_HEX64BIT,
72     PRFMT_POINTER,
73     PRFMT_DOUBLEPERCENT
74 } PRINTTYPE;
75
76 typedef struct
77 {
78     PRINTTYPE       type;               /* The PRFMT_* type found */
79     uint32_t        ulSpecifierIdx;     /* Returns a pointer to the % sign */
80     uint32_t        ulFillLen;
81     char            cFillChar;
82     bool            fLeftJustified;
83     bool            fHasIllegalType;    /* TRUE if an illegal sequence was skipped over */
84     bool            fHasVarWidth;
85 } PRINTFORMAT;
86
87
88 /*  Our output handlers are written for standard fixed width data types.  Map
89     the standard ANSI C data types onto our handlers.  Currently this code has
90     the following requirements:
91
92     1) shorts must be either 16 or 32 bits
93     2) ints must be either 16 or 32 bits
94     3) longs must be between 32 or 64 bits
95     4) long longs must be 64 bits
96 */
97 #if (USHRT_MAX == 0xFFFFU)
98   #define MAPSHORT          PRFMT_SIGNED16BIT
99   #define MAPUSHORT         PRFMT_UNSIGNED16BIT
100   #define MAPHEXUSHORT      PRFMT_HEX16BIT
101 #elif (USHRT_MAX == 0xFFFFFFFFU)
102   #define MAPSHORT          PRFMT_SIGNED32BIT
103   #define MAPUSHORT         PRFMT_UNSIGNED32BIT
104   #define MAPHEXUSHORT      PRFMT_HEX32BIT
105 #else
106   #error "The 'short' data type does not have a 16 or 32-bit width"
107 #endif
108
109 #if (UINT_MAX == 0xFFFFU)
110   #define MAPINT            PRFMT_SIGNED16BIT
111   #define MAPUINT           PRFMT_UNSIGNED16BIT
112   #define MAPHEXUINT        PRFMT_HEX16BIT
113 #elif (UINT_MAX == 0xFFFFFFFFU)
114   #define MAPINT            PRFMT_SIGNED32BIT
115   #define MAPUINT           PRFMT_UNSIGNED32BIT
116   #define MAPHEXUINT        PRFMT_HEX32BIT
117 #else
118   #error "The 'int' data type does not have a 16 or 32-bit width"
119 #endif
120
121 #if (ULONG_MAX == 0xFFFFFFFFU)
122   #define MAPLONG           PRFMT_SIGNED32BIT
123   #define MAPULONG          PRFMT_UNSIGNED32BIT
124   #define MAPHEXULONG       PRFMT_HEX32BIT
125 #elif (ULONG_MAX <= UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))
126   /*  We've run into unusual environments where "longs" are 40-bits wide.
127       In this event, map them to 64-bit types so no data is lost.
128   */
129   #define MAPLONG         PRFMT_SIGNED64BIT
130   #define MAPULONG        PRFMT_UNSIGNED64BIT
131   #define MAPHEXULONG     PRFMT_HEX64BIT
132 #else
133   #error "The 'long' data type is not between 32 and 64 bits wide"
134 #endif
135
136 #if defined(ULLONG_MAX) && (ULLONG_MAX != UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))
137   #error "The 'long long' data type is not 64 bits wide"
138 #else
139   #define MAPLONGLONG       PRFMT_SIGNED64BIT
140   #define MAPULONGLONG      PRFMT_UNSIGNED64BIT
141   #define MAPHEXULONGLONG   PRFMT_HEX64BIT
142 #endif
143
144
145 static uint32_t ProcessFormatSegment(char *pcBuffer, uint32_t ulBufferLen, const char *pszFormat, PRINTFORMAT *pFormat, uint32_t *pulSpecifierLen);
146 static uint32_t ParseFormatSpecifier(char const *pszFormat, PRINTFORMAT *pFormatType);
147 static PRINTTYPE ParseFormatType(const char *pszFormat, uint32_t *pulTypeLen);
148 static uint32_t LtoA(char *pcBuffer, uint32_t ulBufferLen, int32_t lNum, uint32_t ulFillLen, char cFill);
149 static uint32_t LLtoA(char *pcBuffer, uint32_t ulBufferLen, int64_t llNum, uint32_t ulFillLen, char cFill);
150 static uint32_t ULtoA(char *pcBuffer, uint32_t ulBufferLen, uint32_t ulNum, bool fHex, uint32_t ulFillLen, char cFill);
151 static uint32_t ULLtoA(char *pcBuffer, uint32_t ulBufferLen, uint64_t ullNum, bool fHex, uint32_t ulFillLen, char cFill);
152 static uint32_t FinishToA(const char *pcDigits, uint32_t ulDigits, char *pcOutBuffer, uint32_t ulBufferLen, uint32_t ulFillLen, char cFill);
153
154
155 /*  Digits for the *LtoA() routines.
156 */
157 static const char gacDigits[] = "0123456789ABCDEF";
158
159
160 #if REDCONF_OUTPUT == 1
161 /** @brief Print formatted data with a variable length argument list.
162
163     This function provides a subset of the ANSI C printf() functionality with
164     several extensions to support fixed size data types.
165
166     See RedVSNPrintf() for the list of supported types.
167
168     @param pszFormat    A pointer to the null-terminated format string.
169     @param ...          The variable length argument list.
170 */
171 void RedPrintf(
172     const char *pszFormat,
173     ...)
174 {
175     va_list     arglist;
176
177     va_start(arglist, pszFormat);
178
179     RedVPrintf(pszFormat, arglist);
180
181     va_end(arglist);
182 }
183
184
185 /** @brief Print formatted data using a pointer to a variable length argument
186            list.
187
188     This function provides a subset of the ANSI C vprintf() functionality.
189
190     See RedVSNPrintf() for the list of supported types.
191
192     This function accommodates a maximum output length of #OUTPUT_BUFFER_SIZE.
193     If this function must truncate the output, and the original string was
194     \n terminated, the truncated output will be \n terminated as well.
195
196     @param pszFormat    A pointer to the null-terminated format string.
197     @param arglist      The variable length argument list.
198 */
199 void RedVPrintf(
200     const char *pszFormat,
201     va_list     arglist)
202 {
203     char        achBuffer[OUTPUT_BUFFER_SIZE];
204
205     if(RedVSNPrintf(achBuffer, sizeof(achBuffer), pszFormat, arglist) == -1)
206     {
207         /*  Ensture the buffer is null terminated.
208         */
209         achBuffer[sizeof(achBuffer) - 1U] = '\0';
210
211         /*  If the original string was \n terminated and the new one is not, due to
212             truncation, stuff a \n into the new one.
213         */
214         if(pszFormat[RedStrLen(pszFormat) - 1U] == '\n')
215         {
216             achBuffer[sizeof(achBuffer) - 2U] = '\n';
217         }
218     }
219
220     RedOsOutputString(achBuffer);
221 }
222 #endif /* #if REDCONF_OUTPUT == 1 */
223
224
225 /** @brief Format arguments into a string using a subset of the ANSI C
226            vsprintf() functionality.
227
228     This function is modeled after the Microsoft _snprint() extension to the
229     ANSI C sprintf() function, and allows a buffer length to be specified so
230     that overflow is avoided.
231
232     See RedVSNPrintf() for the list of supported types.
233
234     @param pcBuffer     A pointer to the output buffer
235     @param ulBufferLen  The output buffer length
236     @param pszFormat    A pointer to the null terminated format string
237     @param ...          Variable argument list
238
239     @return The length output, or -1 if the buffer filled up.  If -1 is
240             returned, the output buffer may not be null-terminated.
241 */
242 int32_t RedSNPrintf(
243     char       *pcBuffer,
244     uint32_t    ulBufferLen,
245     const char *pszFormat,
246     ...)
247 {
248     int32_t     iLen;
249     va_list     arglist;
250
251     va_start(arglist, pszFormat);
252
253     iLen = RedVSNPrintf(pcBuffer, ulBufferLen, pszFormat, arglist);
254
255     va_end(arglist);
256
257     return iLen;
258 }
259
260
261 /** @brief Format arguments into a string using a subset of the ANSI C
262            vsprintf() functionality.
263
264     This function is modeled after the Microsoft _vsnprint() extension to the
265     ANSI C vsprintf() function, and requires a buffer length to be specified so
266     that overflow is avoided.
267
268     The following ANSI C standard formatting codes are supported:
269
270     | Code | Meaning                            |
271     | ---- | ---------------------------------- |
272     | %c   | Format a character                 |
273     | %s   | Format a null-terminated C string  |
274     | %hd  | Format a signed short              |
275     | %hu  | Format an unsigned short           |
276     | %d   | Format a signed integer            |
277     | %u   | Format an unsigned integer         |
278     | %ld  | Format a signed long               |
279     | %lu  | Format an unsigned long            |
280     | %lld | Format a signed long long          |
281     | %llu | Format an unsigned long long       |
282     | %hx  | Format a short in hex              |
283     | %x   | Format an integer in hex           |
284     | %lx  | Format a long in hex               |
285     | %llx | Format a long long in hex          |
286     | %p   | Format a pointer (hex value)       |
287
288     @note All formatting codes are case-sensitive.
289
290     Fill characters and field widths are supported per the ANSI standard, as is
291     left justification with the '-' character.
292
293     The only supported fill characters are '0', ' ', and '_'.
294
295     '*' is supported to specify variable length field widths.
296
297     Hexidecimal numbers are always displayed in upper case.  Formatting codes
298     which specifically request upper case (e.g., "%lX") are not supported.
299
300     Unsupported behaviors:
301       - Precision is not supported.
302       - Floating point is not supported.
303
304     Errata:
305     - There is a subtle difference in the return value for this function versus
306       the Microsoft implementation.  In the Microsoft version, if the buffer
307       exactly fills up, but there is no room for a null-terminator, the return
308       value will be the length of the buffer.  In this code, -1 will be returned
309       when this happens.
310     - When using left justified strings, the only supported fill character is a
311       space, regardless of what may be specified.  It is not clear if this is
312       ANSI standard or just the way the Microsoft function works, but we emulate
313       the Microsoft behavior.
314
315     @param pcBuffer     A pointer to the output buffer.
316     @param ulBufferLen  The output buffer length.
317     @param pszFormat    A pointer to the null terminated ANSI format string.
318     @param arglist      Variable argument list.
319
320     @return  The length output, or -1 if the buffer filled up.  If -1 is
321              returned, the output buffer may not be null-terminated.
322 */
323 int32_t RedVSNPrintf(
324     char       *pcBuffer,
325     uint32_t    ulBufferLen,
326     const char *pszFormat,
327     va_list     arglist)
328 {
329     uint32_t    ulBufIdx = 0U;
330     uint32_t    ulFmtIdx = 0U;
331     int32_t     iLen;
332
333     while((pszFormat[ulFmtIdx] != '\0') && (ulBufIdx < ulBufferLen))
334     {
335         PRINTFORMAT fmt;
336         uint32_t    ulSpecifierLen;
337         uint32_t    ulWidth;
338
339         /*  Process the next segment of the format string, outputting
340             any non-format specifiers, as output buffer space allows,
341             and return information about the next format specifier.
342         */
343         ulWidth = ProcessFormatSegment(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, &pszFormat[ulFmtIdx], &fmt, &ulSpecifierLen);
344         if(ulWidth)
345         {
346             REDASSERT(ulWidth <= (ulBufferLen - ulBufIdx));
347
348             ulBufIdx += ulWidth;
349         }
350
351         /*  If no specifier was found, or if the output buffer is
352             full, we're done -- get out.
353         */
354         if((ulSpecifierLen == 0U) || (ulBufIdx == ulBufferLen))
355         {
356             break;
357         }
358
359         /*  Otherwise, the math should add up for these things...
360         */
361         REDASSERT(&pszFormat[fmt.ulSpecifierIdx] == &pszFormat[ulWidth]);
362
363         /*  Point past the specifier, to the next piece of the format string.
364         */
365         ulFmtIdx = ulFmtIdx + fmt.ulSpecifierIdx + ulSpecifierLen;
366
367         if(fmt.fHasVarWidth)
368         {
369             int iFillLen = va_arg(arglist, int);
370
371             if(iFillLen >= 0)
372             {
373                 fmt.ulFillLen = (uint32_t)iFillLen;
374             }
375             else
376             {
377                 /*  Bogus fill length.  Ignore.
378                 */
379                 fmt.ulFillLen = 0U;
380             }
381         }
382
383         switch(fmt.type)
384         {
385             case PRFMT_DOUBLEPERCENT:
386             {
387                 /*  Nothing to do.  A single percent has already been output,
388                     and we just finished skipping past the second percent.
389                 */
390                 break;
391             }
392
393             /*----------------->  Small int handling  <------------------
394              *
395              *  Values smaller than "int" will be promoted to "int" by
396              *  the compiler, so we must retrieve them using "int" when
397              *  calling va_arg().  Once we've done that, we immediately
398              *  put the value into the desired data type.
399              *---------------------------------------------------------*/
400
401             case PRFMT_CHAR:
402             {
403                 pcBuffer[ulBufIdx] = (char)va_arg(arglist, int);
404                 ulBufIdx++;
405                 break;
406             }
407             case PRFMT_SIGNED8BIT:
408             {
409                 int8_t num = (int8_t)va_arg(arglist, int);
410
411                 ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);
412                 break;
413             }
414             case PRFMT_UNSIGNED8BIT:
415             {
416                 uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);
417
418                 ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, false, fmt.ulFillLen, fmt.cFillChar);
419                 break;
420             }
421             case PRFMT_HEX8BIT:
422             {
423                 uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);
424
425                 ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, true, fmt.ulFillLen, fmt.cFillChar);
426                 break;
427             }
428             case PRFMT_SIGNED16BIT:
429             {
430                 int16_t num = (int16_t)va_arg(arglist, int);
431
432                 ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);
433                 break;
434             }
435             case PRFMT_UNSIGNED16BIT:
436             {
437                 uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);
438
439                 ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, false, fmt.ulFillLen, fmt.cFillChar);
440                 break;
441             }
442             case PRFMT_HEX16BIT:
443             {
444                 uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);
445
446                 ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, true, fmt.ulFillLen, fmt.cFillChar);
447                 break;
448             }
449             case PRFMT_SIGNED32BIT:
450             {
451                 int32_t lNum = va_arg(arglist, int32_t);
452
453                 ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, lNum, fmt.ulFillLen, fmt.cFillChar);
454                 break;
455             }
456             case PRFMT_UNSIGNED32BIT:
457             {
458                 uint32_t ulNum = va_arg(arglist, uint32_t);
459
460                 ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, false, fmt.ulFillLen, fmt.cFillChar);
461                 break;
462             }
463             case PRFMT_HEX32BIT:
464             {
465                 uint32_t ulNum = va_arg(arglist, uint32_t);
466
467                 ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, true, fmt.ulFillLen, fmt.cFillChar);
468                 break;
469             }
470             case PRFMT_SIGNED64BIT:
471             {
472                 int64_t llNum = va_arg(arglist, int64_t);
473
474                 ulBufIdx += LLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, llNum, fmt.ulFillLen, fmt.cFillChar);
475                 break;
476             }
477             case PRFMT_UNSIGNED64BIT:
478             {
479                 uint64_t ullNum = va_arg(arglist, uint64_t);
480
481                 ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, false, fmt.ulFillLen, fmt.cFillChar);
482                 break;
483             }
484             case PRFMT_HEX64BIT:
485             {
486                 uint64_t ullNum = va_arg(arglist, uint64_t);
487
488                 ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, true, fmt.ulFillLen, fmt.cFillChar);
489                 break;
490             }
491             case PRFMT_POINTER:
492             {
493                 const void *ptr = va_arg(arglist, const void *);
494
495                 /*  Assert our assumption.
496                 */
497                 REDASSERT(sizeof(void *) <= 8U);
498
499                 /*  Format as either a 64-bit or a 32-bit value.
500                 */
501                 if(sizeof(void *) > 4U)
502                 {
503                     /*  Attempt to quiet warnings.
504                     */
505                     uintptr_t   ptrval = (uintptr_t)ptr;
506                     uint64_t    ullPtrVal = (uint64_t)ptrval;
507
508                     ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullPtrVal, true, fmt.ulFillLen, fmt.cFillChar);
509                 }
510                 else
511                 {
512                     /*  Attempt to quiet warnings.
513                     */
514                     uintptr_t   ptrval = (uintptr_t)ptr;
515                     uint32_t    ulPtrVal = (uint32_t)ptrval;
516
517                     ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulPtrVal, true, fmt.ulFillLen, fmt.cFillChar);
518                 }
519
520                 break;
521             }
522             case PRFMT_ANSISTRING:
523             {
524                 const char *pszArg = va_arg(arglist, const char *);
525                 uint32_t    ulArgIdx = 0U;
526
527                 if(pszArg == NULL)
528                 {
529                     pszArg = "null";
530                 }
531
532                 if(fmt.ulFillLen > 0U)
533                 {
534                     if(!fmt.fLeftJustified)
535                     {
536                         uint32_t ulLen = RedStrLen(pszArg);
537
538                         /*  So long as we are not left justifying, fill as many
539                             characters as is necessary to make the string right
540                             justified.
541                         */
542                         while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > ulLen))
543                         {
544                             pcBuffer[ulBufIdx] = fmt.cFillChar;
545                             ulBufIdx++;
546                             fmt.ulFillLen--;
547                         }
548                     }
549
550                     /*  Move as many characters as we have space for into the
551                         output buffer.
552                     */
553                     while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))
554                     {
555                         pcBuffer[ulBufIdx] = pszArg[ulArgIdx];
556                         ulBufIdx++;
557                         ulArgIdx++;
558                         if(fmt.ulFillLen > 0U)
559                         {
560                             fmt.ulFillLen--;
561                         }
562                     }
563
564                     /*  If there is any space left to fill, do it (the string
565                         must have been left justified).
566                     */
567                     while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > 0U))
568                     {
569                         /*  This is NOT a typo -- when using left justified
570                             strings, spaces are the only allowed fill character.
571                             See the errata.
572                         */
573                         pcBuffer[ulBufIdx] = ' ';
574                         ulBufIdx++;
575                         fmt.ulFillLen--;
576                     }
577                 }
578                 else
579                 {
580                     /*  No fill characters, just move up to as many
581                         characters as we have space for in the output
582                         buffer.
583                     */
584                     while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))
585                     {
586                         pcBuffer[ulBufIdx] = pszArg[ulArgIdx];
587                         ulBufIdx++;
588                         ulArgIdx++;
589                     }
590                 }
591                 break;
592             }
593             default:
594             {
595                 REDERROR();
596                 break;
597             }
598         }
599     }
600
601     /*  If there is space, tack on a null and return the output length
602         processed, not including the null.
603     */
604     if(ulBufIdx < ulBufferLen)
605     {
606         pcBuffer[ulBufIdx] = '\0';
607         iLen = (int32_t)ulBufIdx;
608     }
609     else
610     {
611         /*  Not enough space, just return -1, with no null termination
612         */
613         iLen = -1;
614     }
615
616     return iLen;
617 }
618
619
620 /** @brief  Process the next segment of the format string, outputting any
621             non-format specifiers, as output buffer space allows, and return
622             information about the next format specifier.
623
624     @note   If the returned value is the same as the supplied @p ulBufferLen,
625             the output buffer will not be null-terminated.  In all other cases,
626             the result will be null-terminated.  The returned length will never
627             include the null in the count.
628
629     @param pcBuffer         The output buffer.
630     @param ulBufferLen      The output buffer length.
631     @param pszFormat        The format string to process.
632     @param pFormat          The PRINTFORMAT structure to fill.
633     @param pulSpecifierLen  Returns the length of any format specifier string,
634                             or zero if no specifier was found.
635
636     @return The count of characters from pszFormatt which were processed and
637             copied to pcBuffer.
638         - If zero is returned and *pulSpecifierLen is non-zero, then
639           a format specifier string was found at the start of pszFmt.
640         - If non-zero is returned and *pulSpecifierLen is zero, then
641           no format specifier string was found, and the entire pszFmt
642           string was copied to pBuffer (or as much as will fit).
643 */
644 static uint32_t ProcessFormatSegment(
645     char           *pcBuffer,
646     uint32_t        ulBufferLen,
647     const char     *pszFormat,
648     PRINTFORMAT    *pFormat,
649     uint32_t       *pulSpecifierLen)
650 {
651     uint32_t        ulWidth = 0U;
652
653     /*  Find the next format specifier string, and information about it.
654     */
655     *pulSpecifierLen = ParseFormatSpecifier(pszFormat, pFormat);
656
657     if(*pulSpecifierLen == 0U)
658     {
659         /*  If no specifier was found at all, then simply output the full length
660             of the string, or as much as will fit.
661          */
662         ulWidth = REDMIN(ulBufferLen, RedStrLen(pszFormat));
663
664         RedMemCpy(pcBuffer, pszFormat, ulWidth);
665     }
666     else
667     {
668         /*  If we encountered a double percent, skip past one of them so it is
669             copied into the output buffer.
670         */
671         if(pFormat->type == PRFMT_DOUBLEPERCENT)
672         {
673             pFormat->ulSpecifierIdx++;
674
675             /*  A double percent specifier always has a length of two.  Since
676                 we're processing one of those percent signs, reduce the length
677                 to one.  Assert it so.
678             */
679             REDASSERT(*pulSpecifierLen == 2U);
680
681             (*pulSpecifierLen)--;
682         }
683
684         /*  So long as the specifier is not the very first thing in the format
685             string...
686         */
687         if(pFormat->ulSpecifierIdx != 0U)
688         {
689             /*  A specifier was found, but there is other data preceding it.
690                 Copy as much as allowed to the output buffer.
691             */
692             ulWidth = REDMIN(ulBufferLen, pFormat->ulSpecifierIdx);
693
694             RedMemCpy(pcBuffer, pszFormat, ulWidth);
695         }
696     }
697
698     /*  If there is room in the output buffer, null-terminate whatever is there.
699         But note that the returned length never includes the null.
700     */
701     if(ulWidth < ulBufferLen)
702     {
703         pcBuffer[ulWidth] = 0U;
704     }
705
706     return ulWidth;
707 }
708
709
710 /** @brief Parse the specified format string for a valid RedVSNPrintf() format
711            sequence, and return information about it.
712
713     @param pszFormat    The format string to process.
714     @param pFormatType  The PRINTFORMAT structure to fill.  The data is only
715                         valid if a non-zero length is returned.
716
717     @return The length of the full format specifier string, starting at
718             pFormat->ulSpecifierIdx.  Returns zero if a valid specifier was
719             not found.
720 */
721 static uint32_t ParseFormatSpecifier(
722     char const     *pszFormat,
723     PRINTFORMAT    *pFormatType)
724 {
725     bool            fContainsIllegalSequence = false;
726     uint32_t        ulLen = 0U;
727     uint32_t        ulIdx = 0U;
728
729     while(pszFormat[ulIdx] != '\0')
730     {
731         uint32_t    ulTypeLen;
732
733         /*  general output
734         */
735         if(pszFormat[ulIdx] != '%')
736         {
737             ulIdx++;
738         }
739         else
740         {
741             RedMemSet(pFormatType, 0U, sizeof(*pFormatType));
742
743             /*  Record the location of the start of the format sequence
744             */
745             pFormatType->ulSpecifierIdx = ulIdx;
746             ulIdx++;
747
748             if(pszFormat[ulIdx] == '-')
749             {
750                 pFormatType->fLeftJustified = true;
751                 ulIdx++;
752             }
753
754             if((pszFormat[ulIdx] == '0') || (pszFormat[ulIdx] == '_'))
755             {
756                 pFormatType->cFillChar = pszFormat[ulIdx];
757                 ulIdx++;
758             }
759             else
760             {
761                 pFormatType->cFillChar = ' ';
762             }
763
764             if(pszFormat[ulIdx] == '*')
765             {
766                 pFormatType->fHasVarWidth = true;
767                 ulIdx++;
768             }
769             else if(ISDIGIT(pszFormat[ulIdx]))
770             {
771                 pFormatType->ulFillLen = (uint32_t)RedAtoI(&pszFormat[ulIdx]);
772                 while(ISDIGIT(pszFormat[ulIdx]))
773                 {
774                     ulIdx++;
775                 }
776             }
777             else
778             {
779                 /*  No fill length.
780                 */
781             }
782
783             pFormatType->type = ParseFormatType(&pszFormat[ulIdx], &ulTypeLen);
784             if(pFormatType->type != PRFMT_UNKNOWN)
785             {
786                 /*  Even though we are returning successfully, keep track of
787                     whether an illegal sequence was encountered and skipped.
788                 */
789                 pFormatType->fHasIllegalType = fContainsIllegalSequence;
790
791                 ulLen = (ulIdx - pFormatType->ulSpecifierIdx) + ulTypeLen;
792                 break;
793             }
794
795             /*  In the case of an unrecognized type string, simply ignore
796                 it entirely.  Reset the pointer to the position following
797                 the percent sign, so it is not found again.
798             */
799             fContainsIllegalSequence = false;
800             ulIdx = pFormatType->ulSpecifierIdx + 1U;
801         }
802     }
803
804     return ulLen;
805 }
806
807
808 /** @brief Parse a RedPrintf() format type string to determine the proper data
809            type.
810
811     @param pszFormat    The format string to process.  This must be a pointer to
812                         the character following any width or justification
813                         characters.
814     @param pulTypeLen   The location in which to store the type length.  The
815                         value will be 0 if PRFMT_UNKNOWN is returned.
816
817     @return Rhe PRFMT_* type value, or PRFMT_UNKNOWN if the type is not
818             recognized.
819 */
820 static PRINTTYPE ParseFormatType(
821     const char     *pszFormat,
822     uint32_t       *pulTypeLen)
823 {
824     PRINTTYPE       fmtType = PRFMT_UNKNOWN;
825     uint32_t        ulIdx = 0U;
826
827     switch(pszFormat[ulIdx])
828     {
829         case '%':
830             fmtType = PRFMT_DOUBLEPERCENT;
831             break;
832         case 'c':
833             fmtType = PRFMT_CHAR;
834             break;
835         case 's':
836             fmtType = PRFMT_ANSISTRING;
837             break;
838         case 'p':
839             fmtType = PRFMT_POINTER;
840             break;
841         case 'd':
842             fmtType = MAPINT;
843             break;
844         case 'u':
845             fmtType = MAPUINT;
846             break;
847         case 'x':
848             fmtType = MAPHEXUINT;
849             break;
850         case 'h':
851         {
852             ulIdx++;
853             switch(pszFormat[ulIdx])
854             {
855                 case 'd':
856                     fmtType = MAPSHORT;
857                     break;
858                 case 'u':
859                     fmtType = MAPUSHORT;
860                     break;
861                 case 'x':
862                     fmtType = MAPHEXUSHORT;
863                     break;
864                 default:
865                     break;
866             }
867             break;
868         }
869         case 'l':
870         {
871             ulIdx++;
872             switch(pszFormat[ulIdx])
873             {
874                 case 'd':
875                     fmtType = MAPLONG;
876                     break;
877                 case 'u':
878                     fmtType = MAPULONG;
879                     break;
880                 case 'x':
881                     fmtType = MAPHEXULONG;
882                     break;
883                 case 'l':
884                 {
885                     ulIdx++;
886                     switch(pszFormat[ulIdx])
887                     {
888                         case 'd':
889                             fmtType = MAPLONGLONG;
890                             break;
891                         case 'u':
892                             fmtType = MAPULONGLONG;
893                             break;
894                         case 'x':
895                         case 'X':
896                             fmtType = MAPHEXULONGLONG;
897                             break;
898                         default:
899                             break;
900                     }
901                     break;
902                 }
903                 default:
904                     break;
905             }
906             break;
907         }
908         default:
909             break;
910     }
911
912     if(fmtType != PRFMT_UNKNOWN)
913     {
914         *pulTypeLen = ulIdx + 1U;
915     }
916     else
917     {
918         *pulTypeLen = 0U;
919     }
920
921     return fmtType;
922 }
923
924
925 /** @brief Format a signed 32-bit integer as a base 10 ASCII string.
926
927     @note   If the output buffer length is exhausted, the result will *not* be
928             null-terminated.
929
930     @note   If the @p ulFillLen value is greater than or equal to the buffer
931             length, the result will not be null-terminated, even if the
932             formatted portion of the data is shorter than the buffer length.
933
934     @param pcBuffer     The output buffer
935     @param ulBufferLen  A pointer to the output buffer length
936     @param lNum         The 32-bit signed number to convert
937     @param ulFillLen    The fill length, if any
938     @param cFill        The fill character to use
939
940     @return The length of the string.
941 */
942 static uint32_t LtoA(
943     char       *pcBuffer,
944     uint32_t    ulBufferLen,
945     int32_t     lNum,
946     uint32_t    ulFillLen,
947     char        cFill)
948 {
949     uint32_t    ulLen;
950
951     if(pcBuffer == NULL)
952     {
953         REDERROR();
954         ulLen = 0U;
955     }
956     else
957     {
958         char                ach[12U]; /* big enough for a int32_t in base 10 */
959         uint32_t            ulDigits = 0U;
960         uint32_t            ulNum;
961         bool                fSign;
962
963         if(lNum < 0)
964         {
965             fSign = true;
966             ulNum = (uint32_t)-lNum;
967         }
968         else
969         {
970             fSign = false;
971             ulNum = (uint32_t)lNum;
972         }
973
974         do
975         {
976             ach[ulDigits] = gacDigits[ulNum % 10U];
977             ulNum = ulNum / 10U;
978             ulDigits++;
979         }
980         while(ulNum);
981
982         if(fSign)
983         {
984             ach[ulDigits] = '-';
985             ulDigits++;
986         }
987
988         ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
989     }
990
991     return ulLen;
992 }
993
994
995 /** @brief Format a signed 64-bit integer as a base 10 ASCII string.
996
997     @note   If the output buffer length is exhausted, the result will *not* be
998             null-terminated.
999
1000     @note   If the @p ulFillLen value is greater than or equal to the buffer
1001             length, the result will not be null-terminated, even if the
1002             formatted portion of the data is shorter than the buffer length.
1003
1004     @param pcBuffer     The output buffer
1005     @param ulBufferLen  A pointer to the output buffer length
1006     @param llNum        The 64-bit signed number to convert
1007     @param ulFillLen    The fill length, if any
1008     @param cFill        The fill character to use
1009
1010     @return The length of the string.
1011 */
1012 static uint32_t LLtoA(
1013     char       *pcBuffer,
1014     uint32_t    ulBufferLen,
1015     int64_t     llNum,
1016     uint32_t    ulFillLen,
1017     char        cFill)
1018 {
1019     uint32_t    ulLen;
1020
1021     if(pcBuffer == NULL)
1022     {
1023         REDERROR();
1024         ulLen = 0U;
1025     }
1026     else
1027     {
1028         char                ach[12U]; /* big enough for a int32_t in base 10 */
1029         uint32_t            ulDigits = 0U;
1030         uint64_t            ullNum;
1031         bool                fSign;
1032
1033         if(llNum < 0)
1034         {
1035             fSign = true;
1036             ullNum = (uint64_t)-llNum;
1037         }
1038         else
1039         {
1040             fSign = false;
1041             ullNum = (uint64_t)llNum;
1042         }
1043
1044         /*  Not allowed to assume that 64-bit division is OK, so use a
1045             software division routine.
1046         */
1047         do
1048         {
1049             uint64_t ullQuotient;
1050             uint32_t ulRemainder;
1051
1052             /*  Note: RedUint64DivMod32() is smart enough to use normal division
1053                 once ullNumericVal <= UINT32_MAX.
1054             */
1055             ullQuotient = RedUint64DivMod32(ullNum, 10U, &ulRemainder);
1056
1057             ach[ulDigits] = gacDigits[ulRemainder];
1058             ullNum = ullQuotient;
1059             ulDigits++;
1060         }
1061         while(ullNum > 0U);
1062
1063         if(fSign)
1064         {
1065             ach[ulDigits] = '-';
1066             ulDigits++;
1067         }
1068
1069         ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
1070     }
1071
1072     return ulLen;
1073 }
1074
1075
1076 /** @brief Format an unsigned 32-bit integer as an ASCII string as decimal or
1077            hex.
1078
1079     @note If the output buffer length is exhausted, the result will *not* be
1080           null-terminated.
1081
1082     @param pcBuffer     The output buffer
1083     @param ulBufferLen  The output buffer length
1084     @param ulNum        The 32-bit unsigned number to convert
1085     @param fHex         If true, format as hex; if false, decimal.
1086     @param ulFillLen    The fill length, if any
1087     @param cFill        The fill character to use
1088
1089     @return The length of the string.
1090 */
1091 static uint32_t ULtoA(
1092     char       *pcBuffer,
1093     uint32_t    ulBufferLen,
1094     uint32_t    ulNum,
1095     bool        fHex,
1096     uint32_t    ulFillLen,
1097     char        cFill)
1098 {
1099     uint32_t    ulLen;
1100
1101     if(pcBuffer == NULL)
1102     {
1103         REDERROR();
1104         ulLen = 0U;
1105     }
1106     else
1107     {
1108         char        ach[11U];   /* Big enough for a uint32_t in radix 10 */
1109         uint32_t    ulDigits = 0U;
1110         uint32_t    ulNumericVal = ulNum;
1111         uint32_t    ulRadix = fHex ? 16U : 10U;
1112
1113         do
1114         {
1115             ach[ulDigits] = gacDigits[ulNumericVal % ulRadix];
1116             ulNumericVal = ulNumericVal / ulRadix;
1117             ulDigits++;
1118         }
1119         while(ulNumericVal > 0U);
1120
1121         ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
1122     }
1123
1124     return ulLen;
1125 }
1126
1127
1128 /** @brief Format an unsigned 64-bit integer as an ASCII string as decimal or
1129            hex.
1130
1131     @note If the output buffer length is exhausted, the result will *not* be
1132           null-terminated.
1133
1134     @param pcBuffer     The output buffer.
1135     @param ulBufferLen  The output buffer length.
1136     @param ullNum       The unsigned 64-bit number to convert.
1137     @param fHex         If true, format as hex; if false, decimal.
1138     @param ulFillLen    The fill length, if any.
1139     @param cFill        The fill character to use.
1140
1141     @return The length of the string.
1142 */
1143 static uint32_t ULLtoA(
1144     char       *pcBuffer,
1145     uint32_t    ulBufferLen,
1146     uint64_t    ullNum,
1147     bool        fHex,
1148     uint32_t    ulFillLen,
1149     char        cFill)
1150 {
1151     uint32_t    ulLen;
1152
1153     if(pcBuffer == NULL)
1154     {
1155         REDERROR();
1156         ulLen = 0U;
1157     }
1158     else
1159     {
1160
1161         char        ach[21U];   /* Big enough for a uint64_t in radix 10 */
1162         uint32_t    ulDigits = 0U;
1163         uint64_t    ullNumericVal = ullNum;
1164
1165         if(fHex)
1166         {
1167             /*  We can figure out the digits using bit operations.
1168             */
1169             do
1170             {
1171                 ach[ulDigits] = gacDigits[ullNumericVal & 15U];
1172                 ullNumericVal >>= 4U;
1173                 ulDigits++;
1174             }
1175             while(ullNumericVal > 0U);
1176         }
1177         else
1178         {
1179             /*  Not allowed to assume that 64-bit division is OK, so use a
1180                 software division routine.
1181             */
1182             do
1183             {
1184                 uint64_t ullQuotient;
1185                 uint32_t ulRemainder;
1186
1187                 /*  Note: RedUint64DivMod32() is smart enough to use normal division
1188                     once ullNumericVal <= UINT32_MAX.
1189                 */
1190                 ullQuotient = RedUint64DivMod32(ullNumericVal, 10U, &ulRemainder);
1191
1192                 ach[ulDigits] = gacDigits[ulRemainder];
1193                 ullNumericVal = ullQuotient;
1194                 ulDigits++;
1195             }
1196             while(ullNumericVal > 0U);
1197         }
1198
1199         ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
1200     }
1201
1202     return ulLen;
1203 }
1204
1205
1206 /** @brief Finish converting a number into an ASCII string representing that
1207            number.
1208
1209     This helper function contains common logic that needs to run at the end of
1210     all the "toA" functions.  It adds the fill character and reverses the digits
1211     string.
1212
1213     @param pcDigits     The digits (and sign) for the ASCII string, in reverse
1214                         order as they were computed.
1215     @param ulDigits     The number of digit characters.
1216     @param pcOutBuffer  The output buffer.
1217     @param ulBufferLen  The length of the output buffer.
1218     @param ulFillLen    The fill length.  If the number string is shorter than
1219                         this, the remaining bytes are filled with @p cFill.
1220     @param cFill        The fill character.
1221
1222     @return The length of @p pcOutBuffer.
1223 */
1224 static uint32_t FinishToA(
1225     const char *pcDigits,
1226     uint32_t    ulDigits,
1227     char       *pcOutBuffer,
1228     uint32_t    ulBufferLen,
1229     uint32_t    ulFillLen,
1230     char        cFill)
1231 {
1232     uint32_t    ulIdx = 0U;
1233     uint32_t    ulDigitIdx = ulDigits;
1234
1235     /*  user may have asked for a fill char
1236     */
1237     if(ulFillLen > ulDigits)
1238     {
1239         uint32_t ulFillRem = ulFillLen - ulDigits;
1240
1241         while((ulFillRem > 0U) && (ulIdx < ulBufferLen))
1242         {
1243             pcOutBuffer[ulIdx] = cFill;
1244             ulIdx++;
1245             ulFillRem--;
1246         }
1247     }
1248
1249     /*  reverse the string
1250     */
1251     while((ulDigitIdx > 0) && (ulIdx < ulBufferLen))
1252     {
1253         ulDigitIdx--;
1254         pcOutBuffer[ulIdx] = pcDigits[ulDigitIdx];
1255         ulIdx++;
1256     }
1257
1258     if(ulIdx < ulBufferLen)
1259     {
1260         pcOutBuffer[ulIdx] = '\0';
1261     }
1262
1263     return ulIdx;
1264 }
1265