2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #define CHAR_LEN 0 /**< "hh" length modifier */
28 #define SHORT_LEN 1 /**< "h" length modifier */
29 #define INT_LEN 2 /**< no length modifier */
30 #define LONG_LEN 3 /**< "l" length modifier */
31 #define LONGLONG_LEN 4 /**< "ll" length modifier */
32 #define SIZE_T_LEN 5 /**< "z" length modifier */
34 static uint8_t type_sizes[] = {
35 [CHAR_LEN] = sizeof ( char ),
36 [SHORT_LEN] = sizeof ( short ),
37 [INT_LEN] = sizeof ( int ),
38 [LONG_LEN] = sizeof ( long ),
39 [LONGLONG_LEN] = sizeof ( long long ),
40 [SIZE_T_LEN] = sizeof ( size_t ),
44 * Use lower-case for hexadecimal digits
46 * Note that this value is set to 0x20 since that makes for very
47 * efficient calculations. (Bitwise-ORing with @c LCASE converts to a
48 * lower-case character, for example.)
53 * Use "alternate form"
55 * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
61 * Format a hexadecimal number
63 * @v end End of buffer to contain number
64 * @v num Number to format
65 * @v width Minimum field width
66 * @ret ptr End of buffer
68 * Fills a buffer in reverse order with a formatted hexadecimal
69 * number. The number will be zero-padded to the specified width.
70 * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
73 * There must be enough space in the buffer to contain the largest
74 * number that this function can format.
76 static char * format_hex ( char *end, unsigned long long num, int width,
81 /* Generate the number */
82 case_mod = flags & LCASE;
84 *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
88 /* Zero-pad to width */
89 while ( ( end - ptr ) < width )
92 /* Add "0x" or "0X" if alternate form specified */
93 if ( flags & ALT_FORM ) {
94 *(--ptr) = 'X' | case_mod;
102 * Format a decimal number
104 * @v end End of buffer to contain number
105 * @v num Number to format
106 * @v width Minimum field width
107 * @ret ptr End of buffer
109 * Fills a buffer in reverse order with a formatted decimal number.
110 * The number will be space-padded to the specified width.
112 * There must be enough space in the buffer to contain the largest
113 * number that this function can format.
115 static char * format_decimal ( char *end, signed long num, int width ) {
119 /* Generate the number */
125 *(--ptr) = '0' + ( num % 10 );
129 /* Add "-" if necessary */
133 /* Space-pad to width */
134 while ( ( end - ptr ) < width )
141 * Print character via a printf context
146 * Call's the printf_context::handler() method and increments
147 * printf_context::len.
149 static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
150 ctx->handler ( ctx, c );
155 * Write a formatted string to a printf context
158 * @v fmt Format string
159 * @v args Arguments corresponding to the format string
160 * @ret len Length of formatted string
162 size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
167 char tmp_buf[32]; /* 32 is enough for all numerical formats.
168 * Insane width fields could overflow this buffer. */
170 /* Initialise context */
173 for ( ; *fmt ; fmt++ ) {
174 /* Pass through ordinary characters */
176 cputchar ( ctx, *fmt );
180 /* Process flag characters */
185 } else if ( *fmt == '0' ) {
186 /* We always 0-pad hex and space-pad decimal */
188 /* End of flag characters */
192 /* Process field width */
195 if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
196 width = ( width * 10 ) + ( *fmt - '0' );
201 /* We don't do floating point */
202 /* Process length modifier */
203 length = &type_sizes[INT_LEN];
207 } else if ( *fmt == 'l' ) {
209 } else if ( *fmt == 'z' ) {
210 length = &type_sizes[SIZE_T_LEN];
215 /* Process conversion specifier */
216 ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
219 cputchar ( ctx, va_arg ( args, unsigned int ) );
220 } else if ( *fmt == 's' ) {
221 ptr = va_arg ( args, char * );
222 } else if ( *fmt == 'p' ) {
225 ptrval = ( intptr_t ) va_arg ( args, void * );
226 ptr = format_hex ( ptr, ptrval, width,
227 ( ALT_FORM | LCASE ) );
228 } else if ( ( *fmt & ~0x20 ) == 'X' ) {
229 unsigned long long hex;
231 flags |= ( *fmt & 0x20 ); /* LCASE */
232 if ( *length >= sizeof ( unsigned long long ) ) {
233 hex = va_arg ( args, unsigned long long );
234 } else if ( *length >= sizeof ( unsigned long ) ) {
235 hex = va_arg ( args, unsigned long );
237 hex = va_arg ( args, unsigned int );
239 ptr = format_hex ( ptr, hex, width, flags );
240 } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
243 if ( *length >= sizeof ( signed long ) ) {
244 decimal = va_arg ( args, signed long );
246 decimal = va_arg ( args, signed int );
248 ptr = format_decimal ( ptr, decimal, width );
252 /* Write out conversion result */
253 for ( ; *ptr ; ptr++ ) {
254 cputchar ( ctx, *ptr );
261 /** Context used by vsnprintf() and friends */
262 struct sputc_context {
263 struct printf_context ctx;
264 /** Buffer for formatted string (used by printf_sputc()) */
266 /** Buffer length (used by printf_sputc()) */
271 * Write character to buffer
276 static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
277 struct sputc_context * sctx =
278 container_of ( ctx, struct sputc_context, ctx );
280 if ( ctx->len <= sctx->max_len )
281 sctx->buf[ctx->len] = c;
285 * Write a formatted string to a buffer
287 * @v buf Buffer into which to write the string
288 * @v size Size of buffer
289 * @v fmt Format string
290 * @v args Arguments corresponding to the format string
291 * @ret len Length of formatted string
293 * If the buffer is too small to contain the string, the returned
294 * length is the length that would have been written had enough space
297 int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
298 struct sputc_context sctx;
302 /* Hand off to vcprintf */
303 sctx.ctx.handler = printf_sputc;
306 len = vcprintf ( &sctx.ctx, fmt, args );
308 /* Add trailing NUL */
320 * Write a formatted string to a buffer
322 * @v buf Buffer into which to write the string
323 * @v size Size of buffer
324 * @v fmt Format string
325 * @v ... Arguments corresponding to the format string
326 * @ret len Length of formatted string
328 int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
332 va_start ( args, fmt );
333 i = vsnprintf ( buf, size, fmt, args );
339 * Write character to console
344 static void printf_putchar ( struct printf_context *ctx __unused,
350 * Write a formatted string to the console
352 * @v fmt Format string
353 * @v args Arguments corresponding to the format string
354 * @ret len Length of formatted string
356 int vprintf ( const char *fmt, va_list args ) {
357 struct printf_context ctx;
359 /* Hand off to vcprintf */
360 ctx.handler = printf_putchar;
361 return vcprintf ( &ctx, fmt, args );
365 * Write a formatted string to the console.
367 * @v fmt Format string
368 * @v ... Arguments corresponding to the format string
369 * @ret len Length of formatted string
371 int printf ( const char *fmt, ... ) {
375 va_start ( args, fmt );
376 i = vprintf ( fmt, args );