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., 51 Franklin Street, Fifth Floor, Boston, MA
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
31 #include <ipxe/vsprintf.h>
35 #define CHAR_LEN 0 /**< "hh" length modifier */
36 #define SHORT_LEN 1 /**< "h" length modifier */
37 #define INT_LEN 2 /**< no length modifier */
38 #define LONG_LEN 3 /**< "l" length modifier */
39 #define LONGLONG_LEN 4 /**< "ll" length modifier */
40 #define SIZE_T_LEN 5 /**< "z" length modifier */
42 static uint8_t type_sizes[] = {
43 [CHAR_LEN] = sizeof ( char ),
44 [SHORT_LEN] = sizeof ( short ),
45 [INT_LEN] = sizeof ( int ),
46 [LONG_LEN] = sizeof ( long ),
47 [LONGLONG_LEN] = sizeof ( long long ),
48 [SIZE_T_LEN] = sizeof ( size_t ),
52 * Use lower-case for hexadecimal digits
54 * Note that this value is set to 0x20 since that makes for very
55 * efficient calculations. (Bitwise-ORing with @c LCASE converts to a
56 * lower-case character, for example.)
61 * Use "alternate form"
63 * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
71 * Note that this value is set to 0x10 since that allows the pad
72 * character to be calculated as @c 0x20|(flags&ZPAD)
77 * Format a hexadecimal number
79 * @v end End of buffer to contain number
80 * @v num Number to format
81 * @v width Minimum field width
82 * @v flags Format flags
83 * @ret ptr End of buffer
85 * Fills a buffer in reverse order with a formatted hexadecimal
86 * number. The number will be zero-padded to the specified width.
87 * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
90 * There must be enough space in the buffer to contain the largest
91 * number that this function can format.
93 static char * format_hex ( char *end, unsigned long long num, int width,
96 int case_mod = ( flags & LCASE );
97 int pad = ( ( flags & ZPAD ) | ' ' );
99 /* Generate the number */
101 *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
106 while ( ( end - ptr ) < width )
109 /* Add "0x" or "0X" if alternate form specified */
110 if ( flags & ALT_FORM ) {
111 *(--ptr) = 'X' | case_mod;
119 * Format a decimal number
121 * @v end End of buffer to contain number
122 * @v num Number to format
123 * @v width Minimum field width
124 * @v flags Format flags
125 * @ret ptr End of buffer
127 * Fills a buffer in reverse order with a formatted decimal number.
128 * The number will be space-padded to the specified width.
130 * There must be enough space in the buffer to contain the largest
131 * number that this function can format.
133 static char * format_decimal ( char *end, signed long num, int width,
137 int zpad = ( flags & ZPAD );
138 int pad = ( zpad | ' ' );
140 /* Generate the number */
146 *(--ptr) = '0' + ( num % 10 );
150 /* Add "-" if necessary */
151 if ( negative && ( ! zpad ) )
155 while ( ( end - ptr ) < width )
158 /* Add "-" if necessary */
159 if ( negative && zpad )
166 * Print character via a printf context
171 * Call's the printf_context::handler() method and increments
172 * printf_context::len.
174 static inline void cputchar ( struct printf_context *ctx, unsigned char c ) {
175 ctx->handler ( ctx, c );
180 * Write a formatted string to a printf context
183 * @v fmt Format string
184 * @v args Arguments corresponding to the format string
185 * @ret len Length of formatted string
187 size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
192 char tmp_buf[32]; /* 32 is enough for all numerical formats.
193 * Insane width fields could overflow this buffer. */
196 /* Initialise context */
199 for ( ; *fmt ; fmt++ ) {
200 /* Pass through ordinary characters */
202 cputchar ( ctx, *fmt );
206 /* Process flag characters */
211 } else if ( *fmt == '0' ) {
214 /* End of flag characters */
218 /* Process field width */
221 if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
222 width = ( width * 10 ) + ( *fmt - '0' );
227 /* We don't do floating point */
228 /* Process length modifier */
229 length = &type_sizes[INT_LEN];
233 } else if ( *fmt == 'l' ) {
235 } else if ( *fmt == 'z' ) {
236 length = &type_sizes[SIZE_T_LEN];
241 /* Process conversion specifier */
242 ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
246 if ( length < &type_sizes[LONG_LEN] ) {
247 cputchar ( ctx, va_arg ( args, unsigned int ) );
252 wc = va_arg ( args, wint_t );
253 len = wcrtomb ( tmp_buf, wc, NULL );
257 } else if ( *fmt == 's' ) {
258 if ( length < &type_sizes[LONG_LEN] ) {
259 ptr = va_arg ( args, char * );
261 wptr = va_arg ( args, wchar_t * );
263 if ( ( ptr == NULL ) && ( wptr == NULL ) )
265 } else if ( *fmt == 'p' ) {
268 ptrval = ( intptr_t ) va_arg ( args, void * );
269 ptr = format_hex ( ptr, ptrval, width,
270 ( ALT_FORM | LCASE ) );
271 } else if ( ( *fmt & ~0x20 ) == 'X' ) {
272 unsigned long long hex;
274 flags |= ( *fmt & 0x20 ); /* LCASE */
275 if ( *length >= sizeof ( unsigned long long ) ) {
276 hex = va_arg ( args, unsigned long long );
277 } else if ( *length >= sizeof ( unsigned long ) ) {
278 hex = va_arg ( args, unsigned long );
280 hex = va_arg ( args, unsigned int );
282 ptr = format_hex ( ptr, hex, width, flags );
283 } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
286 if ( *length >= sizeof ( signed long ) ) {
287 decimal = va_arg ( args, signed long );
289 decimal = va_arg ( args, signed int );
291 ptr = format_decimal ( ptr, decimal, width, flags );
295 /* Write out conversion result */
296 if ( wptr == NULL ) {
297 for ( ; *ptr ; ptr++ ) {
298 cputchar ( ctx, *ptr );
301 for ( ; *wptr ; wptr++ ) {
302 size_t len = wcrtomb ( tmp_buf, *wptr, NULL );
303 for ( ptr = tmp_buf ; len-- ; ptr++ ) {
304 cputchar ( ctx, *ptr );
313 /** Context used by vsnprintf() and friends */
314 struct sputc_context {
315 struct printf_context ctx;
316 /** Buffer for formatted string (used by printf_sputc()) */
318 /** Buffer length (used by printf_sputc()) */
323 * Write character to buffer
328 static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
329 struct sputc_context * sctx =
330 container_of ( ctx, struct sputc_context, ctx );
332 if ( ctx->len < sctx->max_len )
333 sctx->buf[ctx->len] = c;
337 * Write a formatted string to a buffer
339 * @v buf Buffer into which to write the string
340 * @v size Size of buffer
341 * @v fmt Format string
342 * @v args Arguments corresponding to the format string
343 * @ret len Length of formatted string
345 * If the buffer is too small to contain the string, the returned
346 * length is the length that would have been written had enough space
349 int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
350 struct sputc_context sctx;
354 /* Hand off to vcprintf */
355 sctx.ctx.handler = printf_sputc;
358 len = vcprintf ( &sctx.ctx, fmt, args );
360 /* Add trailing NUL */
372 * Write a formatted string to a buffer
374 * @v buf Buffer into which to write the string
375 * @v size Size of buffer
376 * @v fmt Format string
377 * @v ... Arguments corresponding to the format string
378 * @ret len Length of formatted string
380 int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
384 va_start ( args, fmt );
385 i = vsnprintf ( buf, size, fmt, args );
391 * Version of vsnprintf() that accepts a signed buffer size
393 * @v buf Buffer into which to write the string
394 * @v size Size of buffer
395 * @v fmt Format string
396 * @v args Arguments corresponding to the format string
397 * @ret len Length of formatted string
399 int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
401 /* Treat negative buffer size as zero buffer size */
405 /* Hand off to vsnprintf */
406 return vsnprintf ( buf, ssize, fmt, args );
410 * Version of vsnprintf() that accepts a signed buffer size
412 * @v buf Buffer into which to write the string
413 * @v size Size of buffer
414 * @v fmt Format string
415 * @v ... Arguments corresponding to the format string
416 * @ret len Length of formatted string
418 int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
422 /* Hand off to vssnprintf */
423 va_start ( args, fmt );
424 len = vssnprintf ( buf, ssize, fmt, args );
430 * Write character to console
435 static void printf_putchar ( struct printf_context *ctx __unused,
441 * Write a formatted string to the console
443 * @v fmt Format string
444 * @v args Arguments corresponding to the format string
445 * @ret len Length of formatted string
447 int vprintf ( const char *fmt, va_list args ) {
448 struct printf_context ctx;
450 /* Hand off to vcprintf */
451 ctx.handler = printf_putchar;
452 return vcprintf ( &ctx, fmt, args );
456 * Write a formatted string to the console.
458 * @v fmt Format string
459 * @v ... Arguments corresponding to the format string
460 * @ret len Length of formatted string
462 int printf ( const char *fmt, ... ) {
466 va_start ( args, fmt );
467 i = vprintf ( fmt, args );