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 FILE_LICENCE ( GPL2_OR_LATER );
20 
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>
27 
28 /** @file */
29 
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 */
36 
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 ),
44 };
45 
46 /**
47  * Use lower-case for hexadecimal digits
48  *
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.)
52  */
53 #define LCASE 0x20
54 
55 /**
56  * Use "alternate form"
57  *
58  * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
59  * the number.
60  */
61 #define ALT_FORM 0x02
62 
63 /**
64  * Format a hexadecimal number
65  *
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
70  *
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.
75  *
76  * There must be enough space in the buffer to contain the largest
77  * number that this function can format.
78  */
format_hex(char * end,unsigned long long num,int width,int flags)79 static char * format_hex ( char *end, unsigned long long num, int width,
80 			   int flags ) {
81 	char *ptr = end;
82 	int case_mod;
83 
84 	/* Generate the number */
85 	case_mod = flags & LCASE;
86 	do {
87 		*(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
88 		num >>= 4;
89 	} while ( num );
90 
91 	/* Zero-pad to width */
92 	while ( ( end - ptr ) < width )
93 		*(--ptr) = '0';
94 
95 	/* Add "0x" or "0X" if alternate form specified */
96 	if ( flags & ALT_FORM ) {
97 		*(--ptr) = 'X' | case_mod;
98 		*(--ptr) = '0';
99 	}
100 
101 	return ptr;
102 }
103 
104 /**
105  * Format a decimal number
106  *
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
111  *
112  * Fills a buffer in reverse order with a formatted decimal number.
113  * The number will be space-padded to the specified width.
114  *
115  * There must be enough space in the buffer to contain the largest
116  * number that this function can format.
117  */
format_decimal(char * end,signed long num,int width)118 static char * format_decimal ( char *end, signed long num, int width ) {
119 	char *ptr = end;
120 	int negative = 0;
121 
122 	/* Generate the number */
123 	if ( num < 0 ) {
124 		negative = 1;
125 		num = -num;
126 	}
127 	do {
128 		*(--ptr) = '0' + ( num % 10 );
129 		num /= 10;
130 	} while ( num );
131 
132 	/* Add "-" if necessary */
133 	if ( negative )
134 		*(--ptr) = '-';
135 
136 	/* Space-pad to width */
137 	while ( ( end - ptr ) < width )
138 		*(--ptr) = ' ';
139 
140 	return ptr;
141 }
142 
143 /**
144  * Print character via a printf context
145  *
146  * @v ctx		Context
147  * @v c			Character
148  *
149  * Call's the printf_context::handler() method and increments
150  * printf_context::len.
151  */
cputchar(struct printf_context * ctx,unsigned int c)152 static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
153 	ctx->handler ( ctx, c );
154 	++ctx->len;
155 }
156 
157 /**
158  * Write a formatted string to a printf context
159  *
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
164  */
vcprintf(struct printf_context * ctx,const char * fmt,va_list args)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. */
172 
173 	/* Initialise context */
174 	ctx->len = 0;
175 
176 	for ( ; *fmt ; fmt++ ) {
177 		/* Pass through ordinary characters */
178 		if ( *fmt != '%' ) {
179 			cputchar ( ctx, *fmt );
180 			continue;
181 		}
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;
193 			}
194 		}
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;
202 			}
203 		}
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;
216 			}
217 		}
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;
229 
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;
235 
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 );
243 			}
244 			ptr = format_hex ( ptr, hex, width, flags );
245 		} else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
246 			signed long decimal;
247 
248 			if ( *length >= sizeof ( signed long ) ) {
249 				decimal = va_arg ( args, signed long );
250 			} else {
251 				decimal = va_arg ( args, signed int );
252 			}
253 			ptr = format_decimal ( ptr, decimal, width );
254 		} else {
255 			*(--ptr) = *fmt;
256 		}
257 		/* Write out conversion result */
258 		for ( ; *ptr ; ptr++ ) {
259 			cputchar ( ctx, *ptr );
260 		}
261 	}
262 
263 	return ctx->len;
264 }
265 
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;
273 };
274 
275 /**
276  * Write character to buffer
277  *
278  * @v ctx		Context
279  * @v c			Character
280  */
printf_sputc(struct printf_context * ctx,unsigned int c)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 );
284 
285 	if ( ctx->len < sctx->max_len )
286 		sctx->buf[ctx->len] = c;
287 }
288 
289 /**
290  * Write a formatted string to a buffer
291  *
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
297  *
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.
301  */
vsnprintf(char * buf,size_t size,const char * fmt,va_list args)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;
306 
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 );
312 
313 	/* Add trailing NUL */
314 	if ( size ) {
315 		end = size - 1;
316 		if ( len < end )
317 			end = len;
318 		buf[end] = '\0';
319 	}
320 
321 	return len;
322 }
323 
324 /**
325  * Write a formatted string to a buffer
326  *
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
332  */
snprintf(char * buf,size_t size,const char * fmt,...)333 int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
334 	va_list args;
335 	int i;
336 
337 	va_start ( args, fmt );
338 	i = vsnprintf ( buf, size, fmt, args );
339 	va_end ( args );
340 	return i;
341 }
342 
343 /**
344  * Version of vsnprintf() that accepts a signed buffer size
345  *
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
351  */
vssnprintf(char * buf,ssize_t ssize,const char * fmt,va_list args)352 int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
353 
354 	/* Treat negative buffer size as zero buffer size */
355 	if ( ssize < 0 )
356 		ssize = 0;
357 
358 	/* Hand off to vsnprintf */
359 	return vsnprintf ( buf, ssize, fmt, args );
360 }
361 
362 /**
363  * Version of vsnprintf() that accepts a signed buffer size
364  *
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
370  */
ssnprintf(char * buf,ssize_t ssize,const char * fmt,...)371 int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
372 	va_list args;
373 	int len;
374 
375 	/* Hand off to vssnprintf */
376 	va_start ( args, fmt );
377 	len = vssnprintf ( buf, ssize, fmt, args );
378 	va_end ( args );
379 	return len;
380 }
381 
382 /**
383  * Write character to console
384  *
385  * @v ctx		Context
386  * @v c			Character
387  */
printf_putchar(struct printf_context * ctx __unused,unsigned int c)388 static void printf_putchar ( struct printf_context *ctx __unused,
389 			     unsigned int c ) {
390 	putchar ( c );
391 }
392 
393 /**
394  * Write a formatted string to the console
395  *
396  * @v fmt		Format string
397  * @v args		Arguments corresponding to the format string
398  * @ret len		Length of formatted string
399  */
vprintf(const char * fmt,va_list args)400 int vprintf ( const char *fmt, va_list args ) {
401 	struct printf_context ctx;
402 
403 	/* Hand off to vcprintf */
404 	ctx.handler = printf_putchar;
405 	return vcprintf ( &ctx, fmt, args );
406 }
407 
408 /**
409  * Write a formatted string to the console.
410  *
411  * @v fmt		Format string
412  * @v ...		Arguments corresponding to the format string
413  * @ret	len		Length of formatted string
414  */
printf(const char * fmt,...)415 int printf ( const char *fmt, ... ) {
416 	va_list args;
417 	int i;
418 
419 	va_start ( args, fmt );
420 	i = vprintf ( fmt, args );
421 	va_end ( args );
422 	return i;
423 }
424