123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- /*
- * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER );
-
- #include <stddef.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <errno.h>
- #include <wchar.h>
- #include <ipxe/vsprintf.h>
-
- /** @file */
-
- #define CHAR_LEN 0 /**< "hh" length modifier */
- #define SHORT_LEN 1 /**< "h" length modifier */
- #define INT_LEN 2 /**< no length modifier */
- #define LONG_LEN 3 /**< "l" length modifier */
- #define LONGLONG_LEN 4 /**< "ll" length modifier */
- #define SIZE_T_LEN 5 /**< "z" length modifier */
-
- static uint8_t type_sizes[] = {
- [CHAR_LEN] = sizeof ( char ),
- [SHORT_LEN] = sizeof ( short ),
- [INT_LEN] = sizeof ( int ),
- [LONG_LEN] = sizeof ( long ),
- [LONGLONG_LEN] = sizeof ( long long ),
- [SIZE_T_LEN] = sizeof ( size_t ),
- };
-
- /**
- * Use lower-case for hexadecimal digits
- *
- * Note that this value is set to 0x20 since that makes for very
- * efficient calculations. (Bitwise-ORing with @c LCASE converts to a
- * lower-case character, for example.)
- */
- #define LCASE 0x20
-
- /**
- * Use "alternate form"
- *
- * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
- * the number.
- */
- #define ALT_FORM 0x02
-
- /**
- * Use zero padding
- *
- * Note that this value is set to 0x10 since that allows the pad
- * character to be calculated as @c 0x20|(flags&ZPAD)
- */
- #define ZPAD 0x10
-
- /**
- * Format a hexadecimal number
- *
- * @v end End of buffer to contain number
- * @v num Number to format
- * @v width Minimum field width
- * @v flags Format flags
- * @ret ptr End of buffer
- *
- * Fills a buffer in reverse order with a formatted hexadecimal
- * number. The number will be zero-padded to the specified width.
- * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
- * set.
- *
- * There must be enough space in the buffer to contain the largest
- * number that this function can format.
- */
- static char * format_hex ( char *end, unsigned long long num, int width,
- int flags ) {
- char *ptr = end;
- int case_mod = ( flags & LCASE );
- int pad = ( ( flags & ZPAD ) | ' ' );
-
- /* Generate the number */
- do {
- *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
- num >>= 4;
- } while ( num );
-
- /* Pad to width */
- while ( ( end - ptr ) < width )
- *(--ptr) = pad;
-
- /* Add "0x" or "0X" if alternate form specified */
- if ( flags & ALT_FORM ) {
- *(--ptr) = 'X' | case_mod;
- *(--ptr) = '0';
- }
-
- return ptr;
- }
-
- /**
- * Format a decimal number
- *
- * @v end End of buffer to contain number
- * @v num Number to format
- * @v width Minimum field width
- * @v flags Format flags
- * @ret ptr End of buffer
- *
- * Fills a buffer in reverse order with a formatted decimal number.
- * The number will be space-padded to the specified width.
- *
- * There must be enough space in the buffer to contain the largest
- * number that this function can format.
- */
- static char * format_decimal ( char *end, signed long num, int width,
- int flags ) {
- char *ptr = end;
- int negative = 0;
- int zpad = ( flags & ZPAD );
- int pad = ( zpad | ' ' );
-
- /* Generate the number */
- if ( num < 0 ) {
- negative = 1;
- num = -num;
- }
- do {
- *(--ptr) = '0' + ( num % 10 );
- num /= 10;
- } while ( num );
-
- /* Add "-" if necessary */
- if ( negative && ( ! zpad ) )
- *(--ptr) = '-';
-
- /* Pad to width */
- while ( ( end - ptr ) < width )
- *(--ptr) = pad;
-
- /* Add "-" if necessary */
- if ( negative && zpad )
- *ptr = '-';
-
- return ptr;
- }
-
- /**
- * Print character via a printf context
- *
- * @v ctx Context
- * @v c Character
- *
- * Call's the printf_context::handler() method and increments
- * printf_context::len.
- */
- static inline void cputchar ( struct printf_context *ctx, unsigned char c ) {
- ctx->handler ( ctx, c );
- ++ctx->len;
- }
-
- /**
- * Write a formatted string to a printf context
- *
- * @v ctx Context
- * @v fmt Format string
- * @v args Arguments corresponding to the format string
- * @ret len Length of formatted string
- */
- size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
- int flags;
- int width;
- uint8_t *length;
- char *ptr;
- char tmp_buf[32]; /* 32 is enough for all numerical formats.
- * Insane width fields could overflow this buffer. */
- wchar_t *wptr;
-
- /* Initialise context */
- ctx->len = 0;
-
- for ( ; *fmt ; fmt++ ) {
- /* Pass through ordinary characters */
- if ( *fmt != '%' ) {
- cputchar ( ctx, *fmt );
- continue;
- }
- fmt++;
- /* Process flag characters */
- flags = 0;
- for ( ; ; fmt++ ) {
- if ( *fmt == '#' ) {
- flags |= ALT_FORM;
- } else if ( *fmt == '0' ) {
- flags |= ZPAD;
- } else {
- /* End of flag characters */
- break;
- }
- }
- /* Process field width */
- width = 0;
- for ( ; ; fmt++ ) {
- if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
- width = ( width * 10 ) + ( *fmt - '0' );
- } else {
- break;
- }
- }
- /* We don't do floating point */
- /* Process length modifier */
- length = &type_sizes[INT_LEN];
- for ( ; ; fmt++ ) {
- if ( *fmt == 'h' ) {
- length--;
- } else if ( *fmt == 'l' ) {
- length++;
- } else if ( *fmt == 'z' ) {
- length = &type_sizes[SIZE_T_LEN];
- } else {
- break;
- }
- }
- /* Process conversion specifier */
- ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
- *ptr = '\0';
- wptr = NULL;
- if ( *fmt == 'c' ) {
- if ( length < &type_sizes[LONG_LEN] ) {
- cputchar ( ctx, va_arg ( args, unsigned int ) );
- } else {
- wchar_t wc;
- size_t len;
-
- wc = va_arg ( args, wint_t );
- len = wcrtomb ( tmp_buf, wc, NULL );
- tmp_buf[len] = '\0';
- ptr = tmp_buf;
- }
- } else if ( *fmt == 's' ) {
- if ( length < &type_sizes[LONG_LEN] ) {
- ptr = va_arg ( args, char * );
- } else {
- wptr = va_arg ( args, wchar_t * );
- }
- if ( ( ptr == NULL ) && ( wptr == NULL ) )
- ptr = "<NULL>";
- } else if ( *fmt == 'p' ) {
- intptr_t ptrval;
-
- ptrval = ( intptr_t ) va_arg ( args, void * );
- ptr = format_hex ( ptr, ptrval, width,
- ( ALT_FORM | LCASE ) );
- } else if ( ( *fmt & ~0x20 ) == 'X' ) {
- unsigned long long hex;
-
- flags |= ( *fmt & 0x20 ); /* LCASE */
- if ( *length >= sizeof ( unsigned long long ) ) {
- hex = va_arg ( args, unsigned long long );
- } else if ( *length >= sizeof ( unsigned long ) ) {
- hex = va_arg ( args, unsigned long );
- } else {
- hex = va_arg ( args, unsigned int );
- }
- ptr = format_hex ( ptr, hex, width, flags );
- } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
- signed long decimal;
-
- if ( *length >= sizeof ( signed long ) ) {
- decimal = va_arg ( args, signed long );
- } else {
- decimal = va_arg ( args, signed int );
- }
- ptr = format_decimal ( ptr, decimal, width, flags );
- } else {
- *(--ptr) = *fmt;
- }
- /* Write out conversion result */
- if ( wptr == NULL ) {
- for ( ; *ptr ; ptr++ ) {
- cputchar ( ctx, *ptr );
- }
- } else {
- for ( ; *wptr ; wptr++ ) {
- size_t len = wcrtomb ( tmp_buf, *wptr, NULL );
- for ( ptr = tmp_buf ; len-- ; ptr++ ) {
- cputchar ( ctx, *ptr );
- }
- }
- }
- }
-
- return ctx->len;
- }
-
- /** Context used by vsnprintf() and friends */
- struct sputc_context {
- struct printf_context ctx;
- /** Buffer for formatted string (used by printf_sputc()) */
- char *buf;
- /** Buffer length (used by printf_sputc()) */
- size_t max_len;
- };
-
- /**
- * Write character to buffer
- *
- * @v ctx Context
- * @v c Character
- */
- static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
- struct sputc_context * sctx =
- container_of ( ctx, struct sputc_context, ctx );
-
- if ( ctx->len < sctx->max_len )
- sctx->buf[ctx->len] = c;
- }
-
- /**
- * Write a formatted string to a buffer
- *
- * @v buf Buffer into which to write the string
- * @v size Size of buffer
- * @v fmt Format string
- * @v args Arguments corresponding to the format string
- * @ret len Length of formatted string
- *
- * If the buffer is too small to contain the string, the returned
- * length is the length that would have been written had enough space
- * been available.
- */
- int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
- struct sputc_context sctx;
- size_t len;
- size_t end;
-
- /* Hand off to vcprintf */
- sctx.ctx.handler = printf_sputc;
- sctx.buf = buf;
- sctx.max_len = size;
- len = vcprintf ( &sctx.ctx, fmt, args );
-
- /* Add trailing NUL */
- if ( size ) {
- end = size - 1;
- if ( len < end )
- end = len;
- buf[end] = '\0';
- }
-
- return len;
- }
-
- /**
- * Write a formatted string to a buffer
- *
- * @v buf Buffer into which to write the string
- * @v size Size of buffer
- * @v fmt Format string
- * @v ... Arguments corresponding to the format string
- * @ret len Length of formatted string
- */
- int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
- va_list args;
- int i;
-
- va_start ( args, fmt );
- i = vsnprintf ( buf, size, fmt, args );
- va_end ( args );
- return i;
- }
-
- /**
- * Version of vsnprintf() that accepts a signed buffer size
- *
- * @v buf Buffer into which to write the string
- * @v size Size of buffer
- * @v fmt Format string
- * @v args Arguments corresponding to the format string
- * @ret len Length of formatted string
- */
- int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
-
- /* Treat negative buffer size as zero buffer size */
- if ( ssize < 0 )
- ssize = 0;
-
- /* Hand off to vsnprintf */
- return vsnprintf ( buf, ssize, fmt, args );
- }
-
- /**
- * Version of vsnprintf() that accepts a signed buffer size
- *
- * @v buf Buffer into which to write the string
- * @v size Size of buffer
- * @v fmt Format string
- * @v ... Arguments corresponding to the format string
- * @ret len Length of formatted string
- */
- int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
- va_list args;
- int len;
-
- /* Hand off to vssnprintf */
- va_start ( args, fmt );
- len = vssnprintf ( buf, ssize, fmt, args );
- va_end ( args );
- return len;
- }
-
- /**
- * Write character to console
- *
- * @v ctx Context
- * @v c Character
- */
- static void printf_putchar ( struct printf_context *ctx __unused,
- unsigned int c ) {
- putchar ( c );
- }
-
- /**
- * Write a formatted string to the console
- *
- * @v fmt Format string
- * @v args Arguments corresponding to the format string
- * @ret len Length of formatted string
- */
- int vprintf ( const char *fmt, va_list args ) {
- struct printf_context ctx;
-
- /* Hand off to vcprintf */
- ctx.handler = printf_putchar;
- return vcprintf ( &ctx, fmt, args );
- }
-
- /**
- * Write a formatted string to the console.
- *
- * @v fmt Format string
- * @v ... Arguments corresponding to the format string
- * @ret len Length of formatted string
- */
- int printf ( const char *fmt, ... ) {
- va_list args;
- int i;
-
- va_start ( args, fmt );
- i = vprintf ( fmt, args );
- va_end ( args );
- return i;
- }
|