vsprintf.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <stddef.h>
00022 #include <stdarg.h>
00023 #include <stdio.h>
00024 #include <console.h>
00025 #include <errno.h>
00026 #include <gpxe/vsprintf.h>
00027 
00028 /** @file */
00029 
00030 #define CHAR_LEN        0       /**< "hh" length modifier */
00031 #define SHORT_LEN       1       /**< "h" length modifier */
00032 #define INT_LEN         2       /**< no length modifier */
00033 #define LONG_LEN        3       /**< "l" length modifier */
00034 #define LONGLONG_LEN    4       /**< "ll" length modifier */
00035 #define SIZE_T_LEN      5       /**< "z" length modifier */
00036 
00037 static uint8_t type_sizes[] = {
00038         [CHAR_LEN]      = sizeof ( char ),
00039         [SHORT_LEN]     = sizeof ( short ),
00040         [INT_LEN]       = sizeof ( int ),
00041         [LONG_LEN]      = sizeof ( long ),
00042         [LONGLONG_LEN]  = sizeof ( long long ),
00043         [SIZE_T_LEN]    = sizeof ( size_t ),
00044 };
00045 
00046 /**
00047  * Use lower-case for hexadecimal digits
00048  *
00049  * Note that this value is set to 0x20 since that makes for very
00050  * efficient calculations.  (Bitwise-ORing with @c LCASE converts to a
00051  * lower-case character, for example.)
00052  */
00053 #define LCASE 0x20
00054 
00055 /**
00056  * Use "alternate form"
00057  *
00058  * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
00059  * the number.
00060  */
00061 #define ALT_FORM 0x02
00062 
00063 /**
00064  * Format a hexadecimal number
00065  *
00066  * @v end               End of buffer to contain number
00067  * @v num               Number to format
00068  * @v width             Minimum field width
00069  * @ret ptr             End of buffer
00070  *
00071  * Fills a buffer in reverse order with a formatted hexadecimal
00072  * number.  The number will be zero-padded to the specified width.
00073  * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
00074  * set.
00075  *
00076  * There must be enough space in the buffer to contain the largest
00077  * number that this function can format.
00078  */
00079 static char * format_hex ( char *end, unsigned long long num, int width,
00080                            int flags ) {
00081         char *ptr = end;
00082         int case_mod;
00083 
00084         /* Generate the number */
00085         case_mod = flags & LCASE;
00086         do {
00087                 *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
00088                 num >>= 4;
00089         } while ( num );
00090 
00091         /* Zero-pad to width */
00092         while ( ( end - ptr ) < width )
00093                 *(--ptr) = '0';
00094 
00095         /* Add "0x" or "0X" if alternate form specified */
00096         if ( flags & ALT_FORM ) {
00097                 *(--ptr) = 'X' | case_mod;
00098                 *(--ptr) = '0';
00099         }
00100 
00101         return ptr;
00102 }
00103 
00104 /**
00105  * Format a decimal number
00106  *
00107  * @v end               End of buffer to contain number
00108  * @v num               Number to format
00109  * @v width             Minimum field width
00110  * @ret ptr             End of buffer
00111  *
00112  * Fills a buffer in reverse order with a formatted decimal number.
00113  * The number will be space-padded to the specified width.
00114  *
00115  * There must be enough space in the buffer to contain the largest
00116  * number that this function can format.
00117  */
00118 static char * format_decimal ( char *end, signed long num, int width ) {
00119         char *ptr = end;
00120         int negative = 0;
00121 
00122         /* Generate the number */
00123         if ( num < 0 ) {
00124                 negative = 1;
00125                 num = -num;
00126         }
00127         do {
00128                 *(--ptr) = '0' + ( num % 10 );
00129                 num /= 10;
00130         } while ( num );
00131 
00132         /* Add "-" if necessary */
00133         if ( negative )
00134                 *(--ptr) = '-';
00135 
00136         /* Space-pad to width */
00137         while ( ( end - ptr ) < width )
00138                 *(--ptr) = ' ';
00139 
00140         return ptr;
00141 }
00142 
00143 /**
00144  * Print character via a printf context
00145  *
00146  * @v ctx               Context
00147  * @v c                 Character
00148  *
00149  * Call's the printf_context::handler() method and increments
00150  * printf_context::len.
00151  */
00152 static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
00153         ctx->handler ( ctx, c );
00154         ++ctx->len;
00155 }
00156 
00157 /**
00158  * Write a formatted string to a printf context
00159  *
00160  * @v ctx               Context
00161  * @v fmt               Format string
00162  * @v args              Arguments corresponding to the format string
00163  * @ret len             Length of formatted string
00164  */
00165 size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
00166         int flags;
00167         int width;
00168         uint8_t *length;
00169         char *ptr;
00170         char tmp_buf[32]; /* 32 is enough for all numerical formats.
00171                            * Insane width fields could overflow this buffer. */
00172 
00173         /* Initialise context */
00174         ctx->len = 0;
00175 
00176         for ( ; *fmt ; fmt++ ) {
00177                 /* Pass through ordinary characters */
00178                 if ( *fmt != '%' ) {
00179                         cputchar ( ctx, *fmt );
00180                         continue;
00181                 }
00182                 fmt++;
00183                 /* Process flag characters */
00184                 flags = 0;
00185                 for ( ; ; fmt++ ) {
00186                         if ( *fmt == '#' ) {
00187                                 flags |= ALT_FORM;
00188                         } else if ( *fmt == '0' ) {
00189                                 /* We always 0-pad hex and space-pad decimal */
00190                         } else {
00191                                 /* End of flag characters */
00192                                 break;
00193                         }
00194                 }
00195                 /* Process field width */
00196                 width = 0;
00197                 for ( ; ; fmt++ ) {
00198                         if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
00199                                 width = ( width * 10 ) + ( *fmt - '0' );
00200                         } else {
00201                                 break;
00202                         }
00203                 }
00204                 /* We don't do floating point */
00205                 /* Process length modifier */
00206                 length = &type_sizes[INT_LEN];
00207                 for ( ; ; fmt++ ) {
00208                         if ( *fmt == 'h' ) {
00209                                 length--;
00210                         } else if ( *fmt == 'l' ) {
00211                                 length++;
00212                         } else if ( *fmt == 'z' ) {
00213                                 length = &type_sizes[SIZE_T_LEN];
00214                         } else {
00215                                 break;
00216                         }
00217                 }
00218                 /* Process conversion specifier */
00219                 ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
00220                 *ptr = '\0';
00221                 if ( *fmt == 'c' ) {
00222                         cputchar ( ctx, va_arg ( args, unsigned int ) );
00223                 } else if ( *fmt == 's' ) {
00224                         ptr = va_arg ( args, char * );
00225                         if ( ! ptr )
00226                                 ptr = "<NULL>";
00227                 } else if ( *fmt == 'p' ) {
00228                         intptr_t ptrval;
00229 
00230                         ptrval = ( intptr_t ) va_arg ( args, void * );
00231                         ptr = format_hex ( ptr, ptrval, width, 
00232                                            ( ALT_FORM | LCASE ) );
00233                 } else if ( ( *fmt & ~0x20 ) == 'X' ) {
00234                         unsigned long long hex;
00235 
00236                         flags |= ( *fmt & 0x20 ); /* LCASE */
00237                         if ( *length >= sizeof ( unsigned long long ) ) {
00238                                 hex = va_arg ( args, unsigned long long );
00239                         } else if ( *length >= sizeof ( unsigned long ) ) {
00240                                 hex = va_arg ( args, unsigned long );
00241                         } else {
00242                                 hex = va_arg ( args, unsigned int );
00243                         }
00244                         ptr = format_hex ( ptr, hex, width, flags );
00245                 } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
00246                         signed long decimal;
00247 
00248                         if ( *length >= sizeof ( signed long ) ) {
00249                                 decimal = va_arg ( args, signed long );
00250                         } else {
00251                                 decimal = va_arg ( args, signed int );
00252                         }
00253                         ptr = format_decimal ( ptr, decimal, width );
00254                 } else {
00255                         *(--ptr) = *fmt;
00256                 }
00257                 /* Write out conversion result */
00258                 for ( ; *ptr ; ptr++ ) {
00259                         cputchar ( ctx, *ptr );
00260                 }
00261         }
00262 
00263         return ctx->len;
00264 }
00265 
00266 /** Context used by vsnprintf() and friends */
00267 struct sputc_context {
00268         struct printf_context ctx;
00269         /** Buffer for formatted string (used by printf_sputc()) */
00270         char *buf;
00271         /** Buffer length (used by printf_sputc()) */
00272         size_t max_len; 
00273 };
00274 
00275 /**
00276  * Write character to buffer
00277  *
00278  * @v ctx               Context
00279  * @v c                 Character
00280  */
00281 static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
00282         struct sputc_context * sctx =
00283                 container_of ( ctx, struct sputc_context, ctx );
00284 
00285         if ( ctx->len < sctx->max_len )
00286                 sctx->buf[ctx->len] = c;
00287 }
00288 
00289 /**
00290  * Write a formatted string to a buffer
00291  *
00292  * @v buf               Buffer into which to write the string
00293  * @v size              Size of buffer
00294  * @v fmt               Format string
00295  * @v args              Arguments corresponding to the format string
00296  * @ret len             Length of formatted string
00297  *
00298  * If the buffer is too small to contain the string, the returned
00299  * length is the length that would have been written had enough space
00300  * been available.
00301  */
00302 int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
00303         struct sputc_context sctx;
00304         size_t len;
00305         size_t end;
00306 
00307         /* Hand off to vcprintf */
00308         sctx.ctx.handler = printf_sputc;
00309         sctx.buf = buf;
00310         sctx.max_len = size;
00311         len = vcprintf ( &sctx.ctx, fmt, args );
00312 
00313         /* Add trailing NUL */
00314         if ( size ) {
00315                 end = size - 1;
00316                 if ( len < end )
00317                         end = len;
00318                 buf[end] = '\0';
00319         }
00320 
00321         return len;
00322 }
00323 
00324 /**
00325  * Write a formatted string to a buffer
00326  *
00327  * @v buf               Buffer into which to write the string
00328  * @v size              Size of buffer
00329  * @v fmt               Format string
00330  * @v ...               Arguments corresponding to the format string
00331  * @ret len             Length of formatted string
00332  */
00333 int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
00334         va_list args;
00335         int i;
00336 
00337         va_start ( args, fmt );
00338         i = vsnprintf ( buf, size, fmt, args );
00339         va_end ( args );
00340         return i;
00341 }
00342 
00343 /**
00344  * Version of vsnprintf() that accepts a signed buffer size
00345  *
00346  * @v buf               Buffer into which to write the string
00347  * @v size              Size of buffer
00348  * @v fmt               Format string
00349  * @v args              Arguments corresponding to the format string
00350  * @ret len             Length of formatted string
00351  */
00352 int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
00353 
00354         /* Treat negative buffer size as zero buffer size */
00355         if ( ssize < 0 )
00356                 ssize = 0;
00357 
00358         /* Hand off to vsnprintf */
00359         return vsnprintf ( buf, ssize, fmt, args );
00360 }
00361 
00362 /**
00363  * Version of vsnprintf() that accepts a signed buffer size
00364  *
00365  * @v buf               Buffer into which to write the string
00366  * @v size              Size of buffer
00367  * @v fmt               Format string
00368  * @v ...               Arguments corresponding to the format string
00369  * @ret len             Length of formatted string
00370  */
00371 int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
00372         va_list args;
00373         int len;
00374 
00375         /* Hand off to vssnprintf */
00376         va_start ( args, fmt );
00377         len = vssnprintf ( buf, ssize, fmt, args );
00378         va_end ( args );
00379         return len;
00380 }
00381 
00382 /**
00383  * Write character to console
00384  *
00385  * @v ctx               Context
00386  * @v c                 Character
00387  */
00388 static void printf_putchar ( struct printf_context *ctx __unused,
00389                              unsigned int c ) {
00390         putchar ( c );
00391 }
00392 
00393 /**
00394  * Write a formatted string to the console
00395  *
00396  * @v fmt               Format string
00397  * @v args              Arguments corresponding to the format string
00398  * @ret len             Length of formatted string
00399  */
00400 int vprintf ( const char *fmt, va_list args ) {
00401         struct printf_context ctx;
00402 
00403         /* Hand off to vcprintf */
00404         ctx.handler = printf_putchar;   
00405         return vcprintf ( &ctx, fmt, args );    
00406 }
00407 
00408 /**
00409  * Write a formatted string to the console.
00410  *
00411  * @v fmt               Format string
00412  * @v ...               Arguments corresponding to the format string
00413  * @ret len             Length of formatted string
00414  */
00415 int printf ( const char *fmt, ... ) {
00416         va_list args;
00417         int i;
00418 
00419         va_start ( args, fmt );
00420         i = vprintf ( fmt, args );
00421         va_end ( args );
00422         return i;
00423 }

Generated on Tue Apr 6 20:00:52 2010 for gPXE by  doxygen 1.5.7.1