176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is free software; you can redistribute it and/or
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * modify it under the terms of the GNU General Public License as
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * published by the Free Software Foundation; either version 2 of the
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * License, or any later version.
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is distributed in the hope that it will be useful, but
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * General Public License for more details.
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * You should have received a copy of the GNU General Public License
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * along with this program; if not, write to the Free Software
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
1976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER );
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stddef.h>
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdarg.h>
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h>
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <console.h>
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h>
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/vsprintf.h>
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** @file */
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define CHAR_LEN	0	/**< "hh" length modifier */
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define SHORT_LEN	1	/**< "h" length modifier */
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define INT_LEN		2	/**< no length modifier */
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define LONG_LEN	3	/**< "l" length modifier */
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define LONGLONG_LEN	4	/**< "ll" length modifier */
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define SIZE_T_LEN	5	/**< "z" length modifier */
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic uint8_t type_sizes[] = {
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[CHAR_LEN]	= sizeof ( char ),
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[SHORT_LEN]	= sizeof ( short ),
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[INT_LEN]	= sizeof ( int ),
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[LONG_LEN]	= sizeof ( long ),
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[LONGLONG_LEN]	= sizeof ( long long ),
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[SIZE_T_LEN]	= sizeof ( size_t ),
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Use lower-case for hexadecimal digits
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Note that this value is set to 0x20 since that makes for very
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * efficient calculations.  (Bitwise-ORing with @c LCASE converts to a
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * lower-case character, for example.)
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define LCASE 0x20
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Use "alternate form"
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the number.
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define ALT_FORM 0x02
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Format a hexadecimal number
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v end		End of buffer to contain number
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v num		Number to format
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v width		Minimum field width
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret ptr		End of buffer
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Fills a buffer in reverse order with a formatted hexadecimal
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * number.  The number will be zero-padded to the specified width.
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * set.
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * There must be enough space in the buffer to contain the largest
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * number that this function can format.
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic char * format_hex ( char *end, unsigned long long num, int width,
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			   int flags ) {
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	char *ptr = end;
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int case_mod;
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Generate the number */
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case_mod = flags & LCASE;
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	do {
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		*(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		num >>= 4;
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} while ( num );
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Zero-pad to width */
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while ( ( end - ptr ) < width )
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		*(--ptr) = '0';
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Add "0x" or "0X" if alternate form specified */
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( flags & ALT_FORM ) {
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		*(--ptr) = 'X' | case_mod;
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		*(--ptr) = '0';
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return ptr;
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Format a decimal number
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v end		End of buffer to contain number
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v num		Number to format
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v width		Minimum field width
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret ptr		End of buffer
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Fills a buffer in reverse order with a formatted decimal number.
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The number will be space-padded to the specified width.
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * There must be enough space in the buffer to contain the largest
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * number that this function can format.
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic char * format_decimal ( char *end, signed long num, int width ) {
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	char *ptr = end;
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int negative = 0;
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Generate the number */
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( num < 0 ) {
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		negative = 1;
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		num = -num;
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	do {
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		*(--ptr) = '0' + ( num % 10 );
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		num /= 10;
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} while ( num );
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Add "-" if necessary */
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( negative )
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		*(--ptr) = '-';
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Space-pad to width */
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while ( ( end - ptr ) < width )
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		*(--ptr) = ' ';
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return ptr;
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Print character via a printf context
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ctx		Context
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v c			Character
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Call's the printf_context::handler() method and increments
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * printf_context::len.
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ctx->handler ( ctx, c );
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	++ctx->len;
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Write a formatted string to a printf context
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ctx		Context
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fmt		Format string
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v args		Arguments corresponding to the format string
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len		Length of formatted string
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmansize_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int flags;
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int width;
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint8_t *length;
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	char *ptr;
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	char tmp_buf[32]; /* 32 is enough for all numerical formats.
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			   * Insane width fields could overflow this buffer. */
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Initialise context */
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ctx->len = 0;
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for ( ; *fmt ; fmt++ ) {
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Pass through ordinary characters */
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( *fmt != '%' ) {
17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			cputchar ( ctx, *fmt );
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			continue;
18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		fmt++;
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Process flag characters */
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		flags = 0;
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		for ( ; ; fmt++ ) {
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( *fmt == '#' ) {
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				flags |= ALT_FORM;
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else if ( *fmt == '0' ) {
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				/* We always 0-pad hex and space-pad decimal */
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else {
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				/* End of flag characters */
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				break;
19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Process field width */
19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		width = 0;
19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		for ( ; ; fmt++ ) {
19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				width = ( width * 10 ) + ( *fmt - '0' );
20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else {
20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				break;
20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* We don't do floating point */
20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Process length modifier */
20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		length = &type_sizes[INT_LEN];
20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		for ( ; ; fmt++ ) {
20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( *fmt == 'h' ) {
20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				length--;
21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else if ( *fmt == 'l' ) {
21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				length++;
21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else if ( *fmt == 'z' ) {
21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				length = &type_sizes[SIZE_T_LEN];
21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else {
21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				break;
21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Process conversion specifier */
21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		*ptr = '\0';
22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( *fmt == 'c' ) {
22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			cputchar ( ctx, va_arg ( args, unsigned int ) );
22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else if ( *fmt == 's' ) {
22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			ptr = va_arg ( args, char * );
22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( ! ptr )
22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				ptr = "<NULL>";
22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else if ( *fmt == 'p' ) {
22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			intptr_t ptrval;
22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			ptrval = ( intptr_t ) va_arg ( args, void * );
23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			ptr = format_hex ( ptr, ptrval, width,
23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   ( ALT_FORM | LCASE ) );
23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else if ( ( *fmt & ~0x20 ) == 'X' ) {
23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			unsigned long long hex;
23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			flags |= ( *fmt & 0x20 ); /* LCASE */
23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( *length >= sizeof ( unsigned long long ) ) {
23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				hex = va_arg ( args, unsigned long long );
23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else if ( *length >= sizeof ( unsigned long ) ) {
24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				hex = va_arg ( args, unsigned long );
24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else {
24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				hex = va_arg ( args, unsigned int );
24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			ptr = format_hex ( ptr, hex, width, flags );
24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			signed long decimal;
24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( *length >= sizeof ( signed long ) ) {
24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				decimal = va_arg ( args, signed long );
25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else {
25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				decimal = va_arg ( args, signed int );
25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			ptr = format_decimal ( ptr, decimal, width );
25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			*(--ptr) = *fmt;
25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Write out conversion result */
25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		for ( ; *ptr ; ptr++ ) {
25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			cputchar ( ctx, *ptr );
26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return ctx->len;
26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Context used by vsnprintf() and friends */
26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct sputc_context {
26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct printf_context ctx;
26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/** Buffer for formatted string (used by printf_sputc()) */
27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	char *buf;
27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/** Buffer length (used by printf_sputc()) */
27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	size_t max_len;
27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Write character to buffer
27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ctx		Context
27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v c			Character
28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct sputc_context * sctx =
28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		container_of ( ctx, struct sputc_context, ctx );
28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ctx->len < sctx->max_len )
28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		sctx->buf[ctx->len] = c;
28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Write a formatted string to a buffer
29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v buf		Buffer into which to write the string
29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v size		Size of buffer
29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fmt		Format string
29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v args		Arguments corresponding to the format string
29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len		Length of formatted string
29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * If the buffer is too small to contain the string, the returned
29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * length is the length that would have been written had enough space
30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * been available.
30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct sputc_context sctx;
30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	size_t len;
30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	size_t end;
30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Hand off to vcprintf */
30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sctx.ctx.handler = printf_sputc;
30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sctx.buf = buf;
31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sctx.max_len = size;
31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	len = vcprintf ( &sctx.ctx, fmt, args );
31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Add trailing NUL */
31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( size ) {
31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		end = size - 1;
31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( len < end )
31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			end = len;
31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		buf[end] = '\0';
31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return len;
32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Write a formatted string to a buffer
32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v buf		Buffer into which to write the string
32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v size		Size of buffer
32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fmt		Format string
33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ...		Arguments corresponding to the format string
33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len		Length of formatted string
33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint snprintf ( char *buf, size_t size, const char *fmt, ... ) {
33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_list args;
33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int i;
33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_start ( args, fmt );
33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	i = vsnprintf ( buf, size, fmt, args );
33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_end ( args );
34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return i;
34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Version of vsnprintf() that accepts a signed buffer size
34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v buf		Buffer into which to write the string
34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v size		Size of buffer
34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fmt		Format string
34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v args		Arguments corresponding to the format string
35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len		Length of formatted string
35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Treat negative buffer size as zero buffer size */
35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ssize < 0 )
35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ssize = 0;
35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Hand off to vsnprintf */
35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return vsnprintf ( buf, ssize, fmt, args );
36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Version of vsnprintf() that accepts a signed buffer size
36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v buf		Buffer into which to write the string
36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v size		Size of buffer
36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fmt		Format string
36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ...		Arguments corresponding to the format string
36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len		Length of formatted string
37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_list args;
37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int len;
37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Hand off to vssnprintf */
37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_start ( args, fmt );
37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	len = vssnprintf ( buf, ssize, fmt, args );
37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_end ( args );
37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return len;
38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Write character to console
38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ctx		Context
38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v c			Character
38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void printf_putchar ( struct printf_context *ctx __unused,
38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     unsigned int c ) {
39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	putchar ( c );
39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Write a formatted string to the console
39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fmt		Format string
39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v args		Arguments corresponding to the format string
39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret len		Length of formatted string
39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint vprintf ( const char *fmt, va_list args ) {
40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct printf_context ctx;
40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Hand off to vcprintf */
40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ctx.handler = printf_putchar;
40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return vcprintf ( &ctx, fmt, args );
40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Write a formatted string to the console.
41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fmt		Format string
41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ...		Arguments corresponding to the format string
41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret	len		Length of formatted string
41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint printf ( const char *fmt, ... ) {
41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_list args;
41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int i;
41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_start ( args, fmt );
42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	i = vprintf ( fmt, args );
42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	va_end ( args );
42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return i;
42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
424