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