[contrib] Allow Network Protocol header to display in rom-o-matic
[gpxe.git] / src / core / vsprintf.c
blob21ab2429243cdd33069db1347b4d561167531efe
1 /*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
19 FILE_LICENCE ( GPL2_OR_LATER );
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>
28 /** @file */
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 */
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 ),
46 /**
47 * Use lower-case for hexadecimal digits
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.)
53 #define LCASE 0x20
55 /**
56 * Use "alternate form"
58 * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
59 * the number.
61 #define ALT_FORM 0x02
63 /**
64 * Format a hexadecimal number
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
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.
76 * There must be enough space in the buffer to contain the largest
77 * number that this function can format.
79 static char * format_hex ( char *end, unsigned long long num, int width,
80 int flags ) {
81 char *ptr = end;
82 int case_mod;
84 /* Generate the number */
85 case_mod = flags & LCASE;
86 do {
87 *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
88 num >>= 4;
89 } while ( num );
91 /* Zero-pad to width */
92 while ( ( end - ptr ) < width )
93 *(--ptr) = '0';
95 /* Add "0x" or "0X" if alternate form specified */
96 if ( flags & ALT_FORM ) {
97 *(--ptr) = 'X' | case_mod;
98 *(--ptr) = '0';
101 return ptr;
105 * Format a decimal number
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
112 * Fills a buffer in reverse order with a formatted decimal number.
113 * The number will be space-padded to the specified width.
115 * There must be enough space in the buffer to contain the largest
116 * number that this function can format.
118 static char * format_decimal ( char *end, signed long num, int width ) {
119 char *ptr = end;
120 int negative = 0;
122 /* Generate the number */
123 if ( num < 0 ) {
124 negative = 1;
125 num = -num;
127 do {
128 *(--ptr) = '0' + ( num % 10 );
129 num /= 10;
130 } while ( num );
132 /* Add "-" if necessary */
133 if ( negative )
134 *(--ptr) = '-';
136 /* Space-pad to width */
137 while ( ( end - ptr ) < width )
138 *(--ptr) = ' ';
140 return ptr;
144 * Print character via a printf context
146 * @v ctx Context
147 * @v c Character
149 * Call's the printf_context::handler() method and increments
150 * printf_context::len.
152 static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
153 ctx->handler ( ctx, c );
154 ++ctx->len;
158 * Write a formatted string to a printf context
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
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. */
173 /* Initialise context */
174 ctx->len = 0;
176 for ( ; *fmt ; fmt++ ) {
177 /* Pass through ordinary characters */
178 if ( *fmt != '%' ) {
179 cputchar ( ctx, *fmt );
180 continue;
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;
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;
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;
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;
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;
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 );
244 ptr = format_hex ( ptr, hex, width, flags );
245 } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
246 signed long decimal;
248 if ( *length >= sizeof ( signed long ) ) {
249 decimal = va_arg ( args, signed long );
250 } else {
251 decimal = va_arg ( args, signed int );
253 ptr = format_decimal ( ptr, decimal, width );
254 } else {
255 *(--ptr) = *fmt;
257 /* Write out conversion result */
258 for ( ; *ptr ; ptr++ ) {
259 cputchar ( ctx, *ptr );
263 return ctx->len;
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;
276 * Write character to buffer
278 * @v ctx Context
279 * @v c Character
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 );
285 if ( ctx->len < sctx->max_len )
286 sctx->buf[ctx->len] = c;
290 * Write a formatted string to a buffer
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
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.
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;
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 );
313 /* Add trailing NUL */
314 if ( size ) {
315 end = size - 1;
316 if ( len < end )
317 end = len;
318 buf[end] = '\0';
321 return len;
325 * Write a formatted string to a buffer
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
333 int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
334 va_list args;
335 int i;
337 va_start ( args, fmt );
338 i = vsnprintf ( buf, size, fmt, args );
339 va_end ( args );
340 return i;
344 * Version of vsnprintf() that accepts a signed buffer size
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
352 int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
354 /* Treat negative buffer size as zero buffer size */
355 if ( ssize < 0 )
356 ssize = 0;
358 /* Hand off to vsnprintf */
359 return vsnprintf ( buf, ssize, fmt, args );
363 * Version of vsnprintf() that accepts a signed buffer size
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
371 int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
372 va_list args;
373 int len;
375 /* Hand off to vssnprintf */
376 va_start ( args, fmt );
377 len = vssnprintf ( buf, ssize, fmt, args );
378 va_end ( args );
379 return len;
383 * Write character to console
385 * @v ctx Context
386 * @v c Character
388 static void printf_putchar ( struct printf_context *ctx __unused,
389 unsigned int c ) {
390 putchar ( c );
394 * Write a formatted string to the console
396 * @v fmt Format string
397 * @v args Arguments corresponding to the format string
398 * @ret len Length of formatted string
400 int vprintf ( const char *fmt, va_list args ) {
401 struct printf_context ctx;
403 /* Hand off to vcprintf */
404 ctx.handler = printf_putchar;
405 return vcprintf ( &ctx, fmt, args );
409 * Write a formatted string to the console.
411 * @v fmt Format string
412 * @v ... Arguments corresponding to the format string
413 * @ret len Length of formatted string
415 int printf ( const char *fmt, ... ) {
416 va_list args;
417 int i;
419 va_start ( args, fmt );
420 i = vprintf ( fmt, args );
421 va_end ( args );
422 return i;