Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2006 Michael Brown <mbrown (at) fensystems.co.uk>.
      3  *
      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.
      8  *
      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.
     13  *
     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.
     17  */
     18 
     19 FILE_LICENCE ( GPL2_OR_LATER );
     20 
     21 #include <stddef.h>
     22 #include <stdarg.h>
     23 #include <stdio.h>
     24 #include <console.h>
     25 #include <errno.h>
     26 #include <gpxe/vsprintf.h>
     27 
     28 /** @file */
     29 
     30 #define CHAR_LEN	0	/**< "hh" length modifier */
     31 #define SHORT_LEN	1	/**< "h" length modifier */
     32 #define INT_LEN		2	/**< no length modifier */
     33 #define LONG_LEN	3	/**< "l" length modifier */
     34 #define LONGLONG_LEN	4	/**< "ll" length modifier */
     35 #define SIZE_T_LEN	5	/**< "z" length modifier */
     36 
     37 static uint8_t type_sizes[] = {
     38 	[CHAR_LEN]	= sizeof ( char ),
     39 	[SHORT_LEN]	= sizeof ( short ),
     40 	[INT_LEN]	= sizeof ( int ),
     41 	[LONG_LEN]	= sizeof ( long ),
     42 	[LONGLONG_LEN]	= sizeof ( long long ),
     43 	[SIZE_T_LEN]	= sizeof ( size_t ),
     44 };
     45 
     46 /**
     47  * Use lower-case for hexadecimal digits
     48  *
     49  * Note that this value is set to 0x20 since that makes for very
     50  * efficient calculations.  (Bitwise-ORing with @c LCASE converts to a
     51  * lower-case character, for example.)
     52  */
     53 #define LCASE 0x20
     54 
     55 /**
     56  * Use "alternate form"
     57  *
     58  * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
     59  * the number.
     60  */
     61 #define ALT_FORM 0x02
     62 
     63 /**
     64  * Format a hexadecimal number
     65  *
     66  * @v end		End of buffer to contain number
     67  * @v num		Number to format
     68  * @v width		Minimum field width
     69  * @ret ptr		End of buffer
     70  *
     71  * Fills a buffer in reverse order with a formatted hexadecimal
     72  * number.  The number will be zero-padded to the specified width.
     73  * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
     74  * set.
     75  *
     76  * There must be enough space in the buffer to contain the largest
     77  * number that this function can format.
     78  */
     79 static char * format_hex ( char *end, unsigned long long num, int width,
     80 			   int flags ) {
     81 	char *ptr = end;
     82 	int case_mod;
     83 
     84 	/* Generate the number */
     85 	case_mod = flags & LCASE;
     86 	do {
     87 		*(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
     88 		num >>= 4;
     89 	} while ( num );
     90 
     91 	/* Zero-pad to width */
     92 	while ( ( end - ptr ) < width )
     93 		*(--ptr) = '0';
     94 
     95 	/* Add "0x" or "0X" if alternate form specified */
     96 	if ( flags & ALT_FORM ) {
     97 		*(--ptr) = 'X' | case_mod;
     98 		*(--ptr) = '0';
     99 	}
    100 
    101 	return ptr;
    102 }
    103 
    104 /**
    105  * Format a decimal number
    106  *
    107  * @v end		End of buffer to contain number
    108  * @v num		Number to format
    109  * @v width		Minimum field width
    110  * @ret ptr		End of buffer
    111  *
    112  * Fills a buffer in reverse order with a formatted decimal number.
    113  * The number will be space-padded to the specified width.
    114  *
    115  * There must be enough space in the buffer to contain the largest
    116  * number that this function can format.
    117  */
    118 static char * format_decimal ( char *end, signed long num, int width ) {
    119 	char *ptr = end;
    120 	int negative = 0;
    121 
    122 	/* Generate the number */
    123 	if ( num < 0 ) {
    124 		negative = 1;
    125 		num = -num;
    126 	}
    127 	do {
    128 		*(--ptr) = '0' + ( num % 10 );
    129 		num /= 10;
    130 	} while ( num );
    131 
    132 	/* Add "-" if necessary */
    133 	if ( negative )
    134 		*(--ptr) = '-';
    135 
    136 	/* Space-pad to width */
    137 	while ( ( end - ptr ) < width )
    138 		*(--ptr) = ' ';
    139 
    140 	return ptr;
    141 }
    142 
    143 /**
    144  * Print character via a printf context
    145  *
    146  * @v ctx		Context
    147  * @v c			Character
    148  *
    149  * Call's the printf_context::handler() method and increments
    150  * printf_context::len.
    151  */
    152 static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
    153 	ctx->handler ( ctx, c );
    154 	++ctx->len;
    155 }
    156 
    157 /**
    158  * Write a formatted string to a printf context
    159  *
    160  * @v ctx		Context
    161  * @v fmt		Format string
    162  * @v args		Arguments corresponding to the format string
    163  * @ret len		Length of formatted string
    164  */
    165 size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
    166 	int flags;
    167 	int width;
    168 	uint8_t *length;
    169 	char *ptr;
    170 	char tmp_buf[32]; /* 32 is enough for all numerical formats.
    171 			   * Insane width fields could overflow this buffer. */
    172 
    173 	/* Initialise context */
    174 	ctx->len = 0;
    175 
    176 	for ( ; *fmt ; fmt++ ) {
    177 		/* Pass through ordinary characters */
    178 		if ( *fmt != '%' ) {
    179 			cputchar ( ctx, *fmt );
    180 			continue;
    181 		}
    182 		fmt++;
    183 		/* Process flag characters */
    184 		flags = 0;
    185 		for ( ; ; fmt++ ) {
    186 			if ( *fmt == '#' ) {
    187 				flags |= ALT_FORM;
    188 			} else if ( *fmt == '0' ) {
    189 				/* We always 0-pad hex and space-pad decimal */
    190 			} else {
    191 				/* End of flag characters */
    192 				break;
    193 			}
    194 		}
    195 		/* Process field width */
    196 		width = 0;
    197 		for ( ; ; fmt++ ) {
    198 			if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
    199 				width = ( width * 10 ) + ( *fmt - '0' );
    200 			} else {
    201 				break;
    202 			}
    203 		}
    204 		/* We don't do floating point */
    205 		/* Process length modifier */
    206 		length = &type_sizes[INT_LEN];
    207 		for ( ; ; fmt++ ) {
    208 			if ( *fmt == 'h' ) {
    209 				length--;
    210 			} else if ( *fmt == 'l' ) {
    211 				length++;
    212 			} else if ( *fmt == 'z' ) {
    213 				length = &type_sizes[SIZE_T_LEN];
    214 			} else {
    215 				break;
    216 			}
    217 		}
    218 		/* Process conversion specifier */
    219 		ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
    220 		*ptr = '\0';
    221 		if ( *fmt == 'c' ) {
    222 			cputchar ( ctx, va_arg ( args, unsigned int ) );
    223 		} else if ( *fmt == 's' ) {
    224 			ptr = va_arg ( args, char * );
    225 			if ( ! ptr )
    226 				ptr = "<NULL>";
    227 		} else if ( *fmt == 'p' ) {
    228 			intptr_t ptrval;
    229 
    230 			ptrval = ( intptr_t ) va_arg ( args, void * );
    231 			ptr = format_hex ( ptr, ptrval, width,
    232 					   ( ALT_FORM | LCASE ) );
    233 		} else if ( ( *fmt & ~0x20 ) == 'X' ) {
    234 			unsigned long long hex;
    235 
    236 			flags |= ( *fmt & 0x20 ); /* LCASE */
    237 			if ( *length >= sizeof ( unsigned long long ) ) {
    238 				hex = va_arg ( args, unsigned long long );
    239 			} else if ( *length >= sizeof ( unsigned long ) ) {
    240 				hex = va_arg ( args, unsigned long );
    241 			} else {
    242 				hex = va_arg ( args, unsigned int );
    243 			}
    244 			ptr = format_hex ( ptr, hex, width, flags );
    245 		} else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
    246 			signed long decimal;
    247 
    248 			if ( *length >= sizeof ( signed long ) ) {
    249 				decimal = va_arg ( args, signed long );
    250 			} else {
    251 				decimal = va_arg ( args, signed int );
    252 			}
    253 			ptr = format_decimal ( ptr, decimal, width );
    254 		} else {
    255 			*(--ptr) = *fmt;
    256 		}
    257 		/* Write out conversion result */
    258 		for ( ; *ptr ; ptr++ ) {
    259 			cputchar ( ctx, *ptr );
    260 		}
    261 	}
    262 
    263 	return ctx->len;
    264 }
    265 
    266 /** Context used by vsnprintf() and friends */
    267 struct sputc_context {
    268 	struct printf_context ctx;
    269 	/** Buffer for formatted string (used by printf_sputc()) */
    270 	char *buf;
    271 	/** Buffer length (used by printf_sputc()) */
    272 	size_t max_len;
    273 };
    274 
    275 /**
    276  * Write character to buffer
    277  *
    278  * @v ctx		Context
    279  * @v c			Character
    280  */
    281 static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
    282 	struct sputc_context * sctx =
    283 		container_of ( ctx, struct sputc_context, ctx );
    284 
    285 	if ( ctx->len < sctx->max_len )
    286 		sctx->buf[ctx->len] = c;
    287 }
    288 
    289 /**
    290  * Write a formatted string to a buffer
    291  *
    292  * @v buf		Buffer into which to write the string
    293  * @v size		Size of buffer
    294  * @v fmt		Format string
    295  * @v args		Arguments corresponding to the format string
    296  * @ret len		Length of formatted string
    297  *
    298  * If the buffer is too small to contain the string, the returned
    299  * length is the length that would have been written had enough space
    300  * been available.
    301  */
    302 int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
    303 	struct sputc_context sctx;
    304 	size_t len;
    305 	size_t end;
    306 
    307 	/* Hand off to vcprintf */
    308 	sctx.ctx.handler = printf_sputc;
    309 	sctx.buf = buf;
    310 	sctx.max_len = size;
    311 	len = vcprintf ( &sctx.ctx, fmt, args );
    312 
    313 	/* Add trailing NUL */
    314 	if ( size ) {
    315 		end = size - 1;
    316 		if ( len < end )
    317 			end = len;
    318 		buf[end] = '\0';
    319 	}
    320 
    321 	return len;
    322 }
    323 
    324 /**
    325  * Write a formatted string to a buffer
    326  *
    327  * @v buf		Buffer into which to write the string
    328  * @v size		Size of buffer
    329  * @v fmt		Format string
    330  * @v ...		Arguments corresponding to the format string
    331  * @ret len		Length of formatted string
    332  */
    333 int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
    334 	va_list args;
    335 	int i;
    336 
    337 	va_start ( args, fmt );
    338 	i = vsnprintf ( buf, size, fmt, args );
    339 	va_end ( args );
    340 	return i;
    341 }
    342 
    343 /**
    344  * Version of vsnprintf() that accepts a signed buffer size
    345  *
    346  * @v buf		Buffer into which to write the string
    347  * @v size		Size of buffer
    348  * @v fmt		Format string
    349  * @v args		Arguments corresponding to the format string
    350  * @ret len		Length of formatted string
    351  */
    352 int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
    353 
    354 	/* Treat negative buffer size as zero buffer size */
    355 	if ( ssize < 0 )
    356 		ssize = 0;
    357 
    358 	/* Hand off to vsnprintf */
    359 	return vsnprintf ( buf, ssize, fmt, args );
    360 }
    361 
    362 /**
    363  * Version of vsnprintf() that accepts a signed buffer size
    364  *
    365  * @v buf		Buffer into which to write the string
    366  * @v size		Size of buffer
    367  * @v fmt		Format string
    368  * @v ...		Arguments corresponding to the format string
    369  * @ret len		Length of formatted string
    370  */
    371 int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
    372 	va_list args;
    373 	int len;
    374 
    375 	/* Hand off to vssnprintf */
    376 	va_start ( args, fmt );
    377 	len = vssnprintf ( buf, ssize, fmt, args );
    378 	va_end ( args );
    379 	return len;
    380 }
    381 
    382 /**
    383  * Write character to console
    384  *
    385  * @v ctx		Context
    386  * @v c			Character
    387  */
    388 static void printf_putchar ( struct printf_context *ctx __unused,
    389 			     unsigned int c ) {
    390 	putchar ( c );
    391 }
    392 
    393 /**
    394  * Write a formatted string to the console
    395  *
    396  * @v fmt		Format string
    397  * @v args		Arguments corresponding to the format string
    398  * @ret len		Length of formatted string
    399  */
    400 int vprintf ( const char *fmt, va_list args ) {
    401 	struct printf_context ctx;
    402 
    403 	/* Hand off to vcprintf */
    404 	ctx.handler = printf_putchar;
    405 	return vcprintf ( &ctx, fmt, args );
    406 }
    407 
    408 /**
    409  * Write a formatted string to the console.
    410  *
    411  * @v fmt		Format string
    412  * @v ...		Arguments corresponding to the format string
    413  * @ret	len		Length of formatted string
    414  */
    415 int printf ( const char *fmt, ... ) {
    416 	va_list args;
    417 	int i;
    418 
    419 	va_start ( args, fmt );
    420 	i = vprintf ( fmt, args );
    421 	va_end ( args );
    422 	return i;
    423 }
    424