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