2 Copyright 2001, 2002 Georges Menie (www.menie.org)
\r
3 stdarg version contributed by Christian Ettinger
\r
5 This program is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU Lesser General Public License as published by
\r
7 the Free Software Foundation; either version 2 of the License, or
\r
8 (at your option) any later version.
\r
10 Changes for the FreeRTOS ports:
\r
12 - The dot in "%-8.8s"
\r
13 - The specifiers 'l' (long) and 'L' (long long)
\r
14 - The specifier 'u' for unsigned
\r
15 - Dot notation for IP addresses:
\r
16 sprintf("IP = %xip\n", 0xC0A80164);
\r
17 will produce "IP = 192.168.1.100\n"
\r
25 #include "FreeRTOS.h"
\r
31 * Return 1 for readable, 2 for writeable, 3 for both.
\r
32 * Function must be provided by the application.
\r
34 extern BaseType_t xApplicationMemoryPermissions( uint32_t aAddress );
\r
36 extern void vOutputChar( const char cChar, const TickType_t xTicksToWait );
\r
37 static const TickType_t xTicksToWait = pdMS_TO_TICKS( 20 );
\r
59 struct xPrintFlags flags;
\r
62 static void strbuf_init( struct SStringBuf *apStr, char *apBuf, const char *apMaxStr )
\r
65 apStr->orgStr = apBuf;
\r
66 apStr->nulPos = apMaxStr-1;
\r
69 memset( &apStr->flags, '\0', sizeof( apStr->flags ) );
\r
71 /*-----------------------------------------------------------*/
\r
73 static BaseType_t strbuf_printchar( struct SStringBuf *apStr, int c )
\r
75 if( apStr->str == NULL )
\r
77 vOutputChar( ( char ) c, xTicksToWait );
\r
81 if( apStr->str < apStr->nulPos )
\r
83 *( apStr->str++ ) = c;
\r
87 if( apStr->str == apStr->nulPos )
\r
89 *( apStr->str++ ) = '\0';
\r
93 /*-----------------------------------------------------------*/
\r
95 static portINLINE BaseType_t strbuf_printchar_inline( struct SStringBuf *apStr, int c )
\r
97 if( apStr->str == NULL )
\r
99 vOutputChar( ( char ) c, xTicksToWait );
\r
107 if( apStr->str < apStr->nulPos )
\r
109 *(apStr->str++) = c;
\r
117 if( apStr->str == apStr->nulPos )
\r
119 *( apStr->str++ ) = '\0';
\r
123 /*-----------------------------------------------------------*/
\r
125 static portINLINE int i2hex( int aCh )
\r
131 iResult = '0' + aCh;
\r
135 iResult = 'A' + aCh - 10;
\r
140 /*-----------------------------------------------------------*/
\r
142 static BaseType_t prints(struct SStringBuf *apBuf, const char *apString )
\r
144 register int padchar = ' ';
\r
147 if( xApplicationMemoryPermissions( ( uint32_t )apString ) == 0 )
\r
149 /* The user has probably made a mistake with the parameter
\r
150 for '%s', the memory is not readbale. */
\r
151 apString = "INV_MEM";
\r
154 if( apBuf->flags.width > 0 )
\r
156 register int len = 0;
\r
157 register const char *ptr;
\r
158 for( ptr = apString; *ptr; ++ptr )
\r
163 if( len >= apBuf->flags.width )
\r
165 apBuf->flags.width = 0;
\r
169 apBuf->flags.width -= len;
\r
172 if( apBuf->flags.pad & PAD_ZERO )
\r
177 if( ( apBuf->flags.pad & PAD_RIGHT ) == 0 )
\r
179 for( ; apBuf->flags.width > 0; --apBuf->flags.width )
\r
181 if( strbuf_printchar( apBuf, padchar ) == 0 )
\r
187 if( ( apBuf->flags.isNumber == pdTRUE ) && ( apBuf->flags.pad == pdTRUE ) )
\r
189 /* The string to print represents an integer number.
\r
190 * In this case, printLimit is the min number of digits to print
\r
191 * If the length of the number to print is less than the min nb of i
\r
192 * digits to display, we add 0 before printing the number
\r
194 len = strlen( apString );
\r
196 if( len < apBuf->flags.printLimit )
\r
198 i = apBuf->flags.printLimit - len;
\r
201 if( strbuf_printchar( apBuf, '0' ) == 0 )
\r
208 /* The string to print is not the result of a number conversion to ascii.
\r
209 * For a string, printLimit is the max number of characters to display
\r
211 for( ; apBuf->flags.printLimit && *apString ; ++apString, --apBuf->flags.printLimit )
\r
213 if( !strbuf_printchar( apBuf, *apString ) )
\r
219 for( ; apBuf->flags.width > 0; --apBuf->flags.width )
\r
221 if( !strbuf_printchar( apBuf, padchar ) )
\r
229 /*-----------------------------------------------------------*/
\r
231 /* the following should be enough for 32 bit int */
\r
232 #define PRINT_BUF_LEN 12 /* to print 4294967296 */
\r
234 #if SPRINTF_LONG_LONG
\r
235 #warning 64-bit libraries will be included as well
\r
236 static BaseType_t printll( struct SStringBuf *apBuf, long long i )
\r
238 char print_buf[ 2 * PRINT_BUF_LEN ];
\r
240 register int t, neg = 0;
\r
241 register unsigned long long u = i;
\r
242 lldiv_t lldiv_result;
\r
246 * long long int quot; // quotient
\r
247 * long long int rem; // remainder
\r
251 apBuf->flags.isNumber = pdTRUE; /* Parameter for prints */
\r
254 print_buf[ 0 ] = '0';
\r
255 print_buf[ 1 ] = '\0';
\r
256 return prints( apBuf, print_buf );
\r
259 if( ( apBuf->flags.isSigned == pdTRUE ) && ( apBuf->flags.base == 10 ) && ( i < 0LL ) )
\r
265 s = print_buf + sizeof( print_buf ) - 1;
\r
268 /* 18446744073709551616 */
\r
271 lldiv_result = lldiv( u, ( unsigned long long ) apBuf->flags.base );
\r
272 t = lldiv_result.rem;
\r
275 t += apBuf->flags.letBase - '0' - 10;
\r
277 *( --s ) = t + '0';
\r
278 u = lldiv_result.quot;
\r
283 if( ( apBuf->flags.width != 0 ) && ( apBuf->flags.pad & PAD_ZERO ) )
\r
285 if( !strbuf_printchar( apBuf, '-' ) )
\r
289 --apBuf->flags.width;
\r
297 return prints( apBuf, s );
\r
299 #endif /* SPRINTF_LONG_LONG */
\r
300 /*-----------------------------------------------------------*/
\r
302 static BaseType_t printi( struct SStringBuf *apBuf, int i )
\r
304 char print_buf[ PRINT_BUF_LEN ];
\r
306 register int t, neg = 0;
\r
307 register unsigned int u = i;
\r
308 register unsigned base = apBuf->flags.base;
\r
310 apBuf->flags.isNumber = pdTRUE; /* Parameter for prints */
\r
314 print_buf[ 0 ] = '0';
\r
315 print_buf[ 1 ] = '\0';
\r
316 return prints( apBuf, print_buf );
\r
319 if( ( apBuf->flags.isSigned == pdTRUE ) && ( base == 10 ) && ( i < 0 ) )
\r
325 s = print_buf + sizeof( print_buf ) - 1;
\r
336 t += apBuf->flags.letBase - '0' - 10;
\r
338 *( --s ) = t + '0';
\r
345 /* GCC compiles very efficient */
\r
349 *( --s ) = t + '0';
\r
354 // The generic case, not yet in use
\r
361 t += apBuf->flags.letBase - '0' - 10;
\r
363 *( --s ) = t + '0';
\r
372 if( apBuf->flags.width && (apBuf->flags.pad & PAD_ZERO ) )
\r
374 if( strbuf_printchar( apBuf, '-' ) == 0 )
\r
378 --apBuf->flags.width;
\r
386 return prints( apBuf, s );
\r
388 /*-----------------------------------------------------------*/
\r
390 static BaseType_t printIp(struct SStringBuf *apBuf, unsigned i )
\r
392 char print_buf[16];
\r
394 sprintf( print_buf, "%u.%u.%u.%u",
\r
396 ( i >> 16 ) & 0xff,
\r
399 apBuf->flags.isNumber = pdTRUE; /* Parameter for prints */
\r
400 prints( apBuf, print_buf );
\r
404 /*-----------------------------------------------------------*/
\r
406 static void tiny_print( struct SStringBuf *apBuf, const char *format, va_list args )
\r
412 int ch = *( format++ );
\r
418 /* Put the most like flow in a small loop */
\r
419 if( strbuf_printchar_inline( apBuf, ch ) == 0 )
\r
423 ch = *( format++ );
\r
424 } while( ch != '%' );
\r
426 ch = *( format++ );
\r
427 /* Now ch has character after '%', format pointing to next */
\r
435 if( strbuf_printchar( apBuf, ch ) == 0 )
\r
441 memset( &apBuf->flags, '\0', sizeof( apBuf->flags ) );
\r
445 ch = *( format++ );
\r
446 apBuf->flags.pad = PAD_RIGHT;
\r
450 ch = *( format++ );
\r
451 apBuf->flags.pad |= PAD_ZERO;
\r
455 ch = *( format++ );
\r
456 apBuf->flags.width = va_arg( args, int );
\r
460 while( ch >= '0' && ch <= '9' )
\r
462 apBuf->flags.width *= 10;
\r
463 apBuf->flags.width += ch - '0';
\r
464 ch = *( format++ );
\r
469 ch = *( format++ );
\r
472 apBuf->flags.printLimit = va_arg( args, int );
\r
473 ch = *( format++ );
\r
477 while( ch >= '0' && ch <= '9' )
\r
479 apBuf->flags.printLimit *= 10;
\r
480 apBuf->flags.printLimit += ch - '0';
\r
481 ch = *( format++ );
\r
485 if( apBuf->flags.printLimit == 0 )
\r
487 apBuf->flags.printLimit--; /* -1: make it unlimited */
\r
491 register char *s = ( char * )va_arg( args, int );
\r
492 if( prints( apBuf, s ? s : "(null)" ) == 0 )
\r
500 /* char are converted to int then pushed on the stack */
\r
501 scr[0] = ( char ) va_arg( args, int );
\r
503 if( strbuf_printchar( apBuf, scr[0] ) == 0 )
\r
512 ch = *( format++ );
\r
513 apBuf->flags.long32 = 1;
\r
514 /* Makes not difference as u32 == long */
\r
518 ch = *( format++ );
\r
519 apBuf->flags.long64 = 1;
\r
520 /* Does make a difference */
\r
522 apBuf->flags.base = 10;
\r
523 apBuf->flags.letBase = 'a';
\r
525 if( ch == 'd' || ch == 'u' )
\r
527 apBuf->flags.isSigned = ( ch == 'd' );
\r
528 #if SPRINTF_LONG_LONG
\r
529 if( apBuf->flags.long64 != pdFALSE )
\r
531 if( printll( apBuf, va_arg( args, long long ) ) == 0 )
\r
536 #endif /* SPRINTF_LONG_LONG */
\r
537 if( printi( apBuf, va_arg( args, int ) ) == 0 )
\r
544 apBuf->flags.base = 16; /* From here all hexadecimal */
\r
546 if( ch == 'x' && format[0] == 'i' && format[1] == 'p' )
\r
548 format += 2; /* eat the "xi" of "xip" */
\r
549 /* Will use base 10 again */
\r
550 if( printIp( apBuf, va_arg( args, int ) ) == 0 )
\r
556 if( ch == 'x' || ch == 'X' || ch == 'p' || ch == 'o' )
\r
560 apBuf->flags.letBase = 'A';
\r
562 else if( ch == 'o' )
\r
564 apBuf->flags.base = 8;
\r
566 #if SPRINTF_LONG_LONG
\r
567 if( apBuf->flags.long64 != pdFALSE )
\r
569 if( printll( apBuf, va_arg( args, long long ) ) == 0 )
\r
574 #endif /* SPRINTF_LONG_LONG */
\r
575 if( printi( apBuf, va_arg( args, int ) ) == 0 )
\r
582 strbuf_printchar( apBuf, '\0' );
\r
584 /*-----------------------------------------------------------*/
\r
586 int vsnprintf( char *apBuf, size_t aMaxLen, const char *apFmt, va_list args )
\r
588 struct SStringBuf strBuf;
\r
589 strbuf_init( &strBuf, apBuf, ( const char* )apBuf + aMaxLen );
\r
590 tiny_print( &strBuf, apFmt, args );
\r
592 return strBuf.curLen;
\r
594 /*-----------------------------------------------------------*/
\r
596 int snprintf( char *apBuf, size_t aMaxLen, const char *apFmt, ... )
\r
600 va_start( args, apFmt );
\r
601 struct SStringBuf strBuf;
\r
602 strbuf_init( &strBuf, apBuf, ( const char* )apBuf + aMaxLen );
\r
603 tiny_print( &strBuf, apFmt, args );
\r
606 return strBuf.curLen;
\r
608 /*-----------------------------------------------------------*/
\r
610 int sprintf( char *apBuf, const char *apFmt, ... )
\r
614 va_start( args, apFmt );
\r
615 struct SStringBuf strBuf;
\r
616 strbuf_init( &strBuf, apBuf, ( const char * )apBuf + 1024 );
\r
617 tiny_print( &strBuf, apFmt, args );
\r
620 return strBuf.curLen;
\r
622 /*-----------------------------------------------------------*/
\r
624 int vsprintf( char *apBuf, const char *apFmt, va_list args )
\r
626 struct SStringBuf strBuf;
\r
627 strbuf_init( &strBuf, apBuf, ( const char* ) apBuf + 1024 );
\r
628 tiny_print( &strBuf, apFmt, args );
\r
630 return strBuf.curLen;
\r
632 /*-----------------------------------------------------------*/
\r
634 const char *mkSize (unsigned long long aSize, char *apBuf, int aLen)
\r
636 static char retString[33];
\r
637 size_t gb, mb, kb, sb;
\r
639 if (apBuf == NULL) {
\r
641 aLen = sizeof( retString );
\r
643 gb = aSize / (1024*1024*1024);
\r
644 aSize -= gb * (1024*1024*1024);
\r
645 mb = aSize / (1024*1024);
\r
646 aSize -= mb * (1024*1024);
\r
647 kb = aSize / (1024);
\r
648 aSize -= kb * (1024);
\r
652 snprintf (apBuf, aLen, "%u.%02u GB", ( unsigned ) gb, ( unsigned ) ( ( 100 * mb ) / 1024ul ) );
\r
656 snprintf (apBuf, aLen, "%u.%02u MB", ( unsigned ) mb, ( unsigned ) ( ( 100 * kb) / 1024ul ) );
\r
658 else if( kb != 0ul )
\r
660 snprintf (apBuf, aLen, "%u.%02u KB", ( unsigned ) kb, ( unsigned ) ( ( 100 * sb) / 1024ul ) );
\r
664 snprintf (apBuf, aLen, "%u bytes", ( unsigned ) sb);
\r