Added netdev_nullify to natsemi_remove()
[gpxe.git] / src / core / vsprintf.c
blob4457fe4f46293b6b49cb2ecbf034977e2c759237
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 #include <stddef.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <console.h>
23 #include <errno.h>
24 #include <gpxe/vsprintf.h>
26 /** @file */
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 */
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 ),
44 /**
45 * Use lower-case for hexadecimal digits
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.)
51 #define LCASE 0x20
53 /**
54 * Use "alternate form"
56 * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
57 * the number.
59 #define ALT_FORM 0x02
61 /**
62 * Format a hexadecimal number
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
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.
74 * There must be enough space in the buffer to contain the largest
75 * number that this function can format.
77 static char * format_hex ( char *end, unsigned long long num, int width,
78 int flags ) {
79 char *ptr = end;
80 int case_mod;
82 /* Generate the number */
83 case_mod = flags & LCASE;
84 do {
85 *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
86 num >>= 4;
87 } while ( num );
89 /* Zero-pad to width */
90 while ( ( end - ptr ) < width )
91 *(--ptr) = '0';
93 /* Add "0x" or "0X" if alternate form specified */
94 if ( flags & ALT_FORM ) {
95 *(--ptr) = 'X' | case_mod;
96 *(--ptr) = '0';
99 return ptr;
103 * Format a decimal number
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
110 * Fills a buffer in reverse order with a formatted decimal number.
111 * The number will be space-padded to the specified width.
113 * There must be enough space in the buffer to contain the largest
114 * number that this function can format.
116 static char * format_decimal ( char *end, signed long num, int width ) {
117 char *ptr = end;
118 int negative = 0;
120 /* Generate the number */
121 if ( num < 0 ) {
122 negative = 1;
123 num = -num;
125 do {
126 *(--ptr) = '0' + ( num % 10 );
127 num /= 10;
128 } while ( num );
130 /* Add "-" if necessary */
131 if ( negative )
132 *(--ptr) = '-';
134 /* Space-pad to width */
135 while ( ( end - ptr ) < width )
136 *(--ptr) = ' ';
138 return ptr;
142 * Print character via a printf context
144 * @v ctx Context
145 * @v c Character
147 * Call's the printf_context::handler() method and increments
148 * printf_context::len.
150 static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
151 ctx->handler ( ctx, c );
152 ++ctx->len;
156 * Write a formatted string to a printf context
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
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. */
171 /* Initialise context */
172 ctx->len = 0;
174 for ( ; *fmt ; fmt++ ) {
175 /* Pass through ordinary characters */
176 if ( *fmt != '%' ) {
177 cputchar ( ctx, *fmt );
178 continue;
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;
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;
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;
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;
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;
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 );
242 ptr = format_hex ( ptr, hex, width, flags );
243 } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
244 signed long decimal;
246 if ( *length >= sizeof ( signed long ) ) {
247 decimal = va_arg ( args, signed long );
248 } else {
249 decimal = va_arg ( args, signed int );
251 ptr = format_decimal ( ptr, decimal, width );
252 } else {
253 *(--ptr) = *fmt;
255 /* Write out conversion result */
256 for ( ; *ptr ; ptr++ ) {
257 cputchar ( ctx, *ptr );
261 return ctx->len;
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;
274 * Write character to buffer
276 * @v ctx Context
277 * @v c Character
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 );
283 if ( ctx->len < sctx->max_len )
284 sctx->buf[ctx->len] = c;
288 * Write a formatted string to a buffer
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
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.
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;
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 );
311 /* Add trailing NUL */
312 if ( size ) {
313 end = size - 1;
314 if ( len < end )
315 end = len;
316 buf[end] = '\0';
319 return len;
323 * Write a formatted string to a buffer
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
331 int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
332 va_list args;
333 int i;
335 va_start ( args, fmt );
336 i = vsnprintf ( buf, size, fmt, args );
337 va_end ( args );
338 return i;
342 * Version of vsnprintf() that accepts a signed buffer size
344 * @v buf Buffer into which to write the string
345 * @v size Size of buffer
346 * @v fmt Format string
347 * @v args Arguments corresponding to the format string
348 * @ret len Length of formatted string
350 int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
352 /* Treat negative buffer size as zero buffer size */
353 if ( ssize < 0 )
354 ssize = 0;
356 /* Hand off to vsnprintf */
357 return vsnprintf ( buf, ssize, fmt, args );
361 * Version of vsnprintf() that accepts a signed buffer size
363 * @v buf Buffer into which to write the string
364 * @v size Size of buffer
365 * @v fmt Format string
366 * @v ... Arguments corresponding to the format string
367 * @ret len Length of formatted string
369 int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
370 va_list args;
371 int len;
373 /* Hand off to vssnprintf */
374 va_start ( args, fmt );
375 len = vssnprintf ( buf, ssize, fmt, args );
376 va_end ( args );
377 return len;
381 * Write character to console
383 * @v ctx Context
384 * @v c Character
386 static void printf_putchar ( struct printf_context *ctx __unused,
387 unsigned int c ) {
388 putchar ( c );
392 * Write a formatted string to the console
394 * @v fmt Format string
395 * @v args Arguments corresponding to the format string
396 * @ret len Length of formatted string
398 int vprintf ( const char *fmt, va_list args ) {
399 struct printf_context ctx;
401 /* Hand off to vcprintf */
402 ctx.handler = printf_putchar;
403 return vcprintf ( &ctx, fmt, args );
407 * Write a formatted string to the console.
409 * @v fmt Format string
410 * @v ... Arguments corresponding to the format string
411 * @ret len Length of formatted string
413 int printf ( const char *fmt, ... ) {
414 va_list args;
415 int i;
417 va_start ( args, fmt );
418 i = vprintf ( fmt, args );
419 va_end ( args );
420 return i;