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