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