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