blob: cb623aa51a56cfbd79e4f80adcbb511f49ea1c24 [file] [log] [blame]
Uwe Hermannc52761b2008-03-20 00:02:07 +00001/*
2 * This file is part of the libpayload project.
3 *
4 * It has originally been taken from the HelenOS project
5 * (http://www.helenos.eu), and slightly modified for our purposes.
6 *
7 * Copyright (C) 2001-2004 Jakub Jermar
8 * Copyright (C) 2006 Josef Cejka
9 * Copyright (C) 2008 Uwe Hermann <uwe@hermann-uwe.de>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * - Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * - Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * - The name of the author may not be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#include <libpayload.h>
Patrick Georgi980a69b2010-06-24 11:16:10 +000037#include <ctype.h>
Uwe Hermannc52761b2008-03-20 00:02:07 +000038
Patrick Georgic977c7d2011-02-24 07:18:11 +000039static struct _FILE {
40} _stdout, _stdin, _stderr;
41
42FILE *stdout = &_stdout;
43FILE *stdin = &_stdin;
44FILE *stderr = &_stderr;
45
Uwe Hermannc52761b2008-03-20 00:02:07 +000046/** Structure for specifying output methods for different printf clones. */
47struct printf_spec {
48 /* Output function, returns count of printed characters or EOF. */
Mathias Krause6efbebd2012-04-03 21:02:33 +020049 int (*write) (const char *, size_t, void *);
Uwe Hermannc52761b2008-03-20 00:02:07 +000050 /* Support data - output stream specification, its state, locks, ... */
51 void *data;
52};
53
54/** Show prefixes 0x or 0. */
55#define __PRINTF_FLAG_PREFIX 0x00000001
56/** Signed / unsigned number. */
57#define __PRINTF_FLAG_SIGNED 0x00000002
58/** Print leading zeroes. */
59#define __PRINTF_FLAG_ZEROPADDED 0x00000004
60/** Align to left. */
61#define __PRINTF_FLAG_LEFTALIGNED 0x00000010
62/** Always show + sign. */
63#define __PRINTF_FLAG_SHOWPLUS 0x00000020
64/** Print space instead of plus. */
65#define __PRINTF_FLAG_SPACESIGN 0x00000040
66/** Show big characters. */
67#define __PRINTF_FLAG_BIGCHARS 0x00000080
68/** Number has - sign. */
69#define __PRINTF_FLAG_NEGATIVE 0x00000100
70
71/**
Julius Werner2fe505b2014-04-17 20:00:20 -070072 * Buffer big enough for 64-bit number printed in base 2, sign, and prefix.
73 * Add some more to support sane amounts of zero-padding.
Uwe Hermannc52761b2008-03-20 00:02:07 +000074 */
Julius Werner2fe505b2014-04-17 20:00:20 -070075#define PRINT_BUFFER_SIZE (64 + 1 + 2 + 13)
Uwe Hermannc52761b2008-03-20 00:02:07 +000076
77/** Enumeration of possible arguments types. */
78typedef enum {
79 PrintfQualifierByte = 0,
80 PrintfQualifierShort,
81 PrintfQualifierInt,
82 PrintfQualifierLong,
83 PrintfQualifierLongLong,
84 PrintfQualifierPointer,
85} qualifier_t;
86
Mathias Krause67997d32012-04-03 20:42:01 +020087static const char digits_small[] = "0123456789abcdef";
88static const char digits_big[] = "0123456789ABCDEF";
Uwe Hermannc52761b2008-03-20 00:02:07 +000089
90/**
91 * Print one or more characters without adding newline.
92 *
93 * @param buf Buffer of >= count bytesi size. NULL pointer is not allowed!
94 * @param count Number of characters to print.
95 * @param ps Output method and its data.
96 * @return Number of characters printed.
97 */
98static int printf_putnchars(const char *buf, size_t count,
99 struct printf_spec *ps)
100{
Mathias Krause6efbebd2012-04-03 21:02:33 +0200101 return ps->write(buf, count, ps->data);
Uwe Hermannc52761b2008-03-20 00:02:07 +0000102}
103
104/**
105 * Print a string without adding a newline.
106 *
107 * @param str String to print.
108 * @param ps Write function specification and support data.
109 * @return Number of characters printed.
110 */
Mathias Krause67997d32012-04-03 20:42:01 +0200111static inline int printf_putstr(const char *str, struct printf_spec *ps)
Uwe Hermannc52761b2008-03-20 00:02:07 +0000112{
Mathias Krause67997d32012-04-03 20:42:01 +0200113 return printf_putnchars(str, strlen(str), ps);
Uwe Hermannc52761b2008-03-20 00:02:07 +0000114}
115
116/**
117 * Print one character.
118 *
119 * @param c Character to be printed.
120 * @param ps Output method.
121 * @return Number of characters printed.
122 */
123static int printf_putchar(int c, struct printf_spec *ps)
124{
Mathias Krause6efbebd2012-04-03 21:02:33 +0200125 char ch = c;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000126
Mathias Krause6efbebd2012-04-03 21:02:33 +0200127 return ps->write(&ch, 1, ps->data);
Uwe Hermannc52761b2008-03-20 00:02:07 +0000128}
129
Julius Werner2fe505b2014-04-17 20:00:20 -0700130/* Print spaces for padding. Ignores negative counts. */
131static int print_spaces(int count, struct printf_spec *ps)
132{
133 int tmp, ret;
134 char buffer[PRINT_BUFFER_SIZE];
135
136 if (count <= 0)
137 return 0;
138
139 memset(buffer, ' ', MIN(PRINT_BUFFER_SIZE, count));
140 for (tmp = count; tmp > PRINT_BUFFER_SIZE; tmp -= PRINT_BUFFER_SIZE)
141 if ((ret = printf_putnchars(buffer, PRINT_BUFFER_SIZE, ps)) < 0)
142 return ret;
143
144 if ((ret = printf_putnchars(buffer, tmp, ps)) < 0)
145 return ret;
146
147 return count;
148}
149
Uwe Hermannc52761b2008-03-20 00:02:07 +0000150/**
151 * Print one formatted character.
152 *
153 * @param c Character to print.
154 * @param width Width modifier.
155 * @param flags Flags that change the way the character is printed.
Uwe Hermann54ec0af2008-08-26 19:37:37 +0000156 * @param ps Output methods spec for different printf clones.
Uwe Hermannc52761b2008-03-20 00:02:07 +0000157 * @return Number of characters printed, negative value on failure.
158 */
159static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps)
160{
Julius Werner2fe505b2014-04-17 20:00:20 -0700161 int retval;
162 int counter = 1;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000163
164 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
Julius Werner2fe505b2014-04-17 20:00:20 -0700165 if ((retval = print_spaces(width - 1, ps)) < 0)
166 return retval;
167 else
168 counter += retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000169 }
170
Julius Werner2fe505b2014-04-17 20:00:20 -0700171 if ((retval = printf_putchar(c, ps)) < 0)
172 return retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000173
Julius Werner2fe505b2014-04-17 20:00:20 -0700174 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
175 if ((retval = print_spaces(width - 1, ps)) < 0)
176 return retval;
177 else
178 counter += retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000179 }
180
Jordan Croused772e1e2008-04-25 23:10:23 +0000181 return counter;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000182}
183
184/**
185 * Print string.
186 *
187 * @param s String to be printed.
188 * @param width Width modifier.
189 * @param precision Precision modifier.
190 * @param flags Flags that modify the way the string is printed.
Uwe Hermann54ec0af2008-08-26 19:37:37 +0000191 * @param ps Output methods spec for different printf clones.
Uwe Hermannc52761b2008-03-20 00:02:07 +0000192 * @return Number of characters printed, negative value on failure.
193 */
Uwe Hermann54ec0af2008-08-26 19:37:37 +0000194/** Structure for specifying output methods for different printf clones. */
Uwe Hermannc52761b2008-03-20 00:02:07 +0000195static int print_string(char *s, int width, unsigned int precision,
196 uint64_t flags, struct printf_spec *ps)
197{
198 int counter = 0, retval;
199 size_t size;
200
201 if (s == NULL)
202 return printf_putstr("(NULL)", ps);
203 size = strlen(s);
204 /* Print leading spaces. */
205 if (precision == 0)
206 precision = size;
207 width -= precision;
208
209 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
Julius Werner2fe505b2014-04-17 20:00:20 -0700210 if ((retval = print_spaces(width, ps)) < 0)
211 return retval;
212 else
213 counter += retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000214 }
215
216 if ((retval = printf_putnchars(s, MIN(size, precision), ps)) < 0)
Julius Werner2fe505b2014-04-17 20:00:20 -0700217 return retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000218 counter += retval;
219
Julius Werner2fe505b2014-04-17 20:00:20 -0700220 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
221 if ((retval = print_spaces(width, ps)) < 0)
222 return retval;
223 else
224 counter += retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000225 }
226
227 return counter;
228}
229
230/**
231 * Print a number in a given base.
232 *
233 * Print significant digits of a number in given base.
234 *
235 * @param num Number to print.
Julius Werner2fe505b2014-04-17 20:00:20 -0700236 * @param width Width modifier.
Uwe Hermannc52761b2008-03-20 00:02:07 +0000237 * @param precision Precision modifier.
238 * @param base Base to print the number in (must be between 2 and 16).
239 * @param flags Flags that modify the way the number is printed.
Uwe Hermann54ec0af2008-08-26 19:37:37 +0000240 * @param ps Output methods spec for different printf clones.
Uwe Hermannc52761b2008-03-20 00:02:07 +0000241 * @return Number of characters printed.
242 */
243static int print_number(uint64_t num, int width, int precision, int base,
244 uint64_t flags, struct printf_spec *ps)
245{
Mathias Krause67997d32012-04-03 20:42:01 +0200246 const char *digits = digits_small;
Julius Werner2fe505b2014-04-17 20:00:20 -0700247 char d[PRINT_BUFFER_SIZE];
248 char *ptr = &d[PRINT_BUFFER_SIZE];
249 int size = 0; /* Size of the string in ptr */
250 int counter = 0; /* Amount of actually printed bytes. */
Uwe Hermannc52761b2008-03-20 00:02:07 +0000251 char sgn;
252 int retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000253
254 if (flags & __PRINTF_FLAG_BIGCHARS)
255 digits = digits_big;
256
Uwe Hermannc52761b2008-03-20 00:02:07 +0000257 if (num == 0) {
Julius Werner2fe505b2014-04-17 20:00:20 -0700258 *--ptr = '0';
Uwe Hermannc52761b2008-03-20 00:02:07 +0000259 size++;
260 } else {
261 do {
Julius Werner2fe505b2014-04-17 20:00:20 -0700262 *--ptr = digits[num % base];
Uwe Hermannc52761b2008-03-20 00:02:07 +0000263 size++;
264 } while (num /= base);
265 }
266
Julius Werner2fe505b2014-04-17 20:00:20 -0700267 /* Both precision and LEFTALIGNED overrule ZEROPADDED. */
268 if ((flags & __PRINTF_FLAG_LEFTALIGNED) || precision)
269 flags &= ~__PRINTF_FLAG_ZEROPADDED;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000270
Julius Werner2fe505b2014-04-17 20:00:20 -0700271 /* Fix precision now since it doesn't count prefixes/signs. */
272 precision -= size;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000273
Julius Werner2fe505b2014-04-17 20:00:20 -0700274 /* Reserve size for prefixes/signs before filling up padding. */
Uwe Hermannc52761b2008-03-20 00:02:07 +0000275 sgn = 0;
276 if (flags & __PRINTF_FLAG_SIGNED) {
277 if (flags & __PRINTF_FLAG_NEGATIVE) {
278 sgn = '-';
279 size++;
280 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
281 sgn = '+';
282 size++;
283 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
284 sgn = ' ';
285 size++;
286 }
287 }
Uwe Hermannc52761b2008-03-20 00:02:07 +0000288 if (flags & __PRINTF_FLAG_PREFIX) {
289 switch (base) {
290 case 2: /* Binary formating is not standard, but useful. */
Julius Werner2fe505b2014-04-17 20:00:20 -0700291 size += 2;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000292 break;
293 case 8:
Julius Werner2fe505b2014-04-17 20:00:20 -0700294 size++;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000295 break;
296 case 16:
Julius Werner2fe505b2014-04-17 20:00:20 -0700297 size += 2;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000298 break;
299 }
300 }
301
Julius Werner2fe505b2014-04-17 20:00:20 -0700302 /* If this is still set we didn't have a precision, so repurpose it */
303 if (flags & __PRINTF_FLAG_ZEROPADDED)
304 precision = width - size;
305
306 /* Pad smaller numbers with 0 (larger numbers lead to precision < 0). */
307 if (precision > 0) {
308 precision = MIN(precision, PRINT_BUFFER_SIZE - size);
309 ptr -= precision;
310 size += precision;
311 memset(ptr, '0', precision);
Uwe Hermannc52761b2008-03-20 00:02:07 +0000312 }
313
Julius Werner2fe505b2014-04-17 20:00:20 -0700314 /* Add sign and prefix (we adjusted size for this beforehand). */
315 if (flags & __PRINTF_FLAG_PREFIX) {
316 switch (base) {
317 case 2: /* Binary formating is not standard, but useful. */
318 *--ptr = (flags & __PRINTF_FLAG_BIGCHARS) ? 'B' : 'b';
319 *--ptr = '0';
320 break;
321 case 8:
322 *--ptr = '0';
323 break;
324 case 16:
325 *--ptr = (flags & __PRINTF_FLAG_BIGCHARS) ? 'X' : 'x';
326 *--ptr = '0';
327 break;
328 }
329 }
330 if (sgn)
331 *--ptr = sgn;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000332
Julius Werner2fe505b2014-04-17 20:00:20 -0700333 /* Pad with spaces up to width, try to avoid extra putnchar if we can */
334 width -= size;
335 if (width > 0 && !(flags & __PRINTF_FLAG_LEFTALIGNED)) {
336 int tmp = MIN(width, PRINT_BUFFER_SIZE - size);
337 ptr -= tmp;
338 size += tmp;
339 memset(ptr, ' ', tmp);
340 if ((retval = print_spaces(width - tmp, ps)) < 0)
341 return retval;
342 else
343 counter += retval;
344 }
345
346 /* Now print the whole thing at once. */
347 if ((retval = printf_putnchars(ptr, size, ps)) < 0)
348 return retval;
349 counter += retval;
350
351 /* Edge case: left-aligned with width (should be rare). */
352 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
353 if ((retval = print_spaces(width, ps)) < 0)
354 return retval;
355 else
356 counter += retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000357 }
358
359 return counter;
360}
361
Uwe Hermann54ec0af2008-08-26 19:37:37 +0000362/**
363 * Print formatted string.
Uwe Hermannc52761b2008-03-20 00:02:07 +0000364 *
365 * Print string formatted according to the fmt parameter and variadic arguments.
366 * Each formatting directive must have the following form:
Stefan Reinauer14e22772010-04-27 06:56:47 +0000367 *
Uwe Hermannc52761b2008-03-20 00:02:07 +0000368 * \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
369 *
370 * FLAGS:@n
371 * - "#" Force to print prefix.For \%o conversion, the prefix is 0, for
372 * \%x and \%X prefixes are 0x and 0X and for conversion \%b the
373 * prefix is 0b.
374 *
375 * - "-" Align to left.
376 *
377 * - "+" Print positive sign just as negative.
378 *
379 * - " " If the printed number is positive and "+" flag is not set,
380 * print space in place of sign.
381 *
382 * - "0" Print 0 as padding instead of spaces. Zeroes are placed between
383 * sign and the rest of the number. This flag is ignored if "-"
384 * flag is specified.
Stefan Reinauer14e22772010-04-27 06:56:47 +0000385 *
Uwe Hermannc52761b2008-03-20 00:02:07 +0000386 * WIDTH:@n
387 * - Specify the minimal width of a printed argument. If it is bigger,
388 * width is ignored. If width is specified with a "*" character instead of
389 * number, width is taken from parameter list. And integer parameter is
390 * expected before parameter for processed conversion specification. If
391 * this value is negative its absolute value is taken and the "-" flag is
392 * set.
393 *
394 * PRECISION:@n
395 * - Value precision. For numbers it specifies minimum valid numbers.
396 * Smaller numbers are printed with leading zeroes. Bigger numbers are not
397 * affected. Strings with more than precision characters are cut off. Just
398 * as with width, an "*" can be used used instead of a number. An integer
399 * value is then expected in parameters. When both width and precision are
400 * specified using "*", the first parameter is used for width and the
401 * second one for precision.
Stefan Reinauer14e22772010-04-27 06:56:47 +0000402 *
Uwe Hermannc52761b2008-03-20 00:02:07 +0000403 * TYPE:@n
404 * - "hh" Signed or unsigned char.@n
405 * - "h" Signed or unsigned short.@n
406 * - "" Signed or unsigned int (default value).@n
407 * - "l" Signed or unsigned long int.@n
408 * - "ll" Signed or unsigned long long int.@n
Stefan Reinauer14e22772010-04-27 06:56:47 +0000409 *
410 *
Uwe Hermannc52761b2008-03-20 00:02:07 +0000411 * CONVERSION:@n
412 * - % Print percentile character itself.
413 *
414 * - c Print single character.
415 *
416 * - s Print zero terminated string. If a NULL value is passed as
417 * value, "(NULL)" is printed instead.
Stefan Reinauer14e22772010-04-27 06:56:47 +0000418 *
Uwe Hermannc52761b2008-03-20 00:02:07 +0000419 * - P, p Print value of a pointer. Void * value is expected and it is
420 * printed in hexadecimal notation with prefix (as with \%#X / \%#x
421 * for 32-bit or \%#X / \%#x for 64-bit long pointers).
422 *
423 * - b Print value as unsigned binary number. Prefix is not printed by
424 * default. (Nonstandard extension.)
Stefan Reinauer14e22772010-04-27 06:56:47 +0000425 *
Uwe Hermannc52761b2008-03-20 00:02:07 +0000426 * - o Print value as unsigned octal number. Prefix is not printed by
Stefan Reinauer14e22772010-04-27 06:56:47 +0000427 * default.
Uwe Hermannc52761b2008-03-20 00:02:07 +0000428 *
429 * - d, i Print signed decimal number. There is no difference between d
430 * and i conversion.
431 *
432 * - u Print unsigned decimal number.
433 *
434 * - X, x Print hexadecimal number with upper- or lower-case. Prefix is
435 * not printed by default.
Stefan Reinauer14e22772010-04-27 06:56:47 +0000436 *
Uwe Hermannc52761b2008-03-20 00:02:07 +0000437 * All other characters from fmt except the formatting directives are printed in
438 * verbatim.
439 *
440 * @param fmt Formatting NULL terminated string.
441 * @param ps TODO.
442 * @param ap TODO.
443 * @return Number of characters printed, negative value on failure.
444 */
445static int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
446{
447 int i = 0; /* Index of the currently processed char from fmt */
448 int j = 0; /* Index to the first not printed nonformating character */
449 int end;
450 int counter; /* Counter of printed characters */
451 int retval; /* Used to store return values from called functions */
452 char c;
453 qualifier_t qualifier; /* Type of argument */
454 int base; /* Base in which a numeric parameter will be printed */
455 uint64_t number; /* Argument value */
456 size_t size; /* Byte size of integer parameter */
457 int width, precision;
458 uint64_t flags;
459
460 counter = 0;
461
462 while ((c = fmt[i])) {
463 /* Control character. */
464 if (c == '%') {
465 /* Print common characters if any processed. */
466 if (i > j) {
467 if ((retval = printf_putnchars(&fmt[j],
Julius Werner2fe505b2014-04-17 20:00:20 -0700468 (size_t) (i - j), ps)) < 0)
469 return retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000470 counter += retval;
471 }
472
473 j = i;
474 /* Parse modifiers. */
475 flags = 0;
476 end = 0;
477
478 do {
479 ++i;
480 switch (c = fmt[i]) {
481 case '#':
482 flags |= __PRINTF_FLAG_PREFIX;
483 break;
484 case '-':
485 flags |= __PRINTF_FLAG_LEFTALIGNED;
486 break;
487 case '+':
488 flags |= __PRINTF_FLAG_SHOWPLUS;
489 break;
490 case ' ':
491 flags |= __PRINTF_FLAG_SPACESIGN;
492 break;
493 case '0':
494 flags |= __PRINTF_FLAG_ZEROPADDED;
495 break;
496 default:
497 end = 1;
498 };
499
500 } while (end == 0);
501
502 /* Width & '*' operator. */
503 width = 0;
504 if (isdigit(fmt[i])) {
505 while (isdigit(fmt[i])) {
506 width *= 10;
507 width += fmt[i++] - '0';
508 }
509 } else if (fmt[i] == '*') {
510 /* Get width value from argument list. */
511 i++;
512 width = (int)va_arg(ap, int);
513 if (width < 0) {
514 /* Negative width sets '-' flag. */
515 width *= -1;
516 flags |= __PRINTF_FLAG_LEFTALIGNED;
517 }
518 }
519
520 /* Precision and '*' operator. */
521 precision = 0;
522 if (fmt[i] == '.') {
523 ++i;
524 if (isdigit(fmt[i])) {
525 while (isdigit(fmt[i])) {
526 precision *= 10;
527 precision += fmt[i++] - '0';
528 }
529 } else if (fmt[i] == '*') {
530 /* Get precision from argument list. */
531 i++;
532 precision = (int)va_arg(ap, int);
533 /* Ignore negative precision. */
534 if (precision < 0)
535 precision = 0;
536 }
537 }
538
539 switch (fmt[i++]) {
540 /** @todo unimplemented qualifiers:
541 * t ptrdiff_t - ISO C 99
542 */
543 case 'h': /* char or short */
544 qualifier = PrintfQualifierShort;
545 if (fmt[i] == 'h') {
546 i++;
547 qualifier = PrintfQualifierByte;
548 }
549 break;
Stefan Reinauere21f5e12013-03-25 15:13:20 -0700550 case 'z': /* size_t or ssize_t */
551 qualifier = PrintfQualifierLong;
552 break;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000553 case 'l': /* long or long long */
554 qualifier = PrintfQualifierLong;
555 if (fmt[i] == 'l') {
556 i++;
557 qualifier = PrintfQualifierLongLong;
558 }
559 break;
560 default:
561 /* default type */
562 qualifier = PrintfQualifierInt;
563 --i;
564 }
565
566 base = 10;
567
568 switch (c = fmt[i]) {
569 /* String and character conversions */
570 case 's':
571 if ((retval = print_string(va_arg(ap, char *),
Julius Werner2fe505b2014-04-17 20:00:20 -0700572 width, precision, flags, ps)) < 0)
573 return retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000574 counter += retval;
575 j = i + 1;
576 goto next_char;
577 case 'c':
578 c = va_arg(ap, unsigned int);
Julius Werner2fe505b2014-04-17 20:00:20 -0700579 if ((retval = print_char(c, width, flags, ps)) < 0)
580 return retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000581 counter += retval;
582 j = i + 1;
583 goto next_char;
584
585 /* Integer values */
586 case 'P': /* pointer */
587 flags |= __PRINTF_FLAG_BIGCHARS;
588 case 'p':
589 flags |= __PRINTF_FLAG_PREFIX;
590 base = 16;
591 qualifier = PrintfQualifierPointer;
592 break;
593 case 'b':
594 base = 2;
595 break;
596 case 'o':
597 base = 8;
598 break;
599 case 'd':
600 case 'i':
601 flags |= __PRINTF_FLAG_SIGNED;
602 case 'u':
603 break;
604 case 'X':
605 flags |= __PRINTF_FLAG_BIGCHARS;
606 case 'x':
607 base = 16;
608 break;
609 case '%': /* percentile itself */
610 j = i;
611 goto next_char;
612 default: /* Bad formatting */
613 /*
614 * Unknown format. Now, j is the index of '%'
615 * so we will print whole bad format sequence.
616 */
617 goto next_char;
618 }
619
620 /* Print integers. */
621 /* Print number. */
622 switch (qualifier) {
623 case PrintfQualifierByte:
624 size = sizeof(unsigned char);
625 number = (uint64_t) va_arg(ap, unsigned int);
626 break;
627 case PrintfQualifierShort:
628 size = sizeof(unsigned short);
629 number = (uint64_t) va_arg(ap, unsigned int);
630 break;
631 case PrintfQualifierInt:
632 size = sizeof(unsigned int);
633 number = (uint64_t) va_arg(ap, unsigned int);
634 break;
635 case PrintfQualifierLong:
636 size = sizeof(unsigned long);
637 number = (uint64_t) va_arg(ap, unsigned long);
638 break;
639 case PrintfQualifierLongLong:
640 size = sizeof(unsigned long long);
641 number = (uint64_t) va_arg(ap, unsigned long long);
642 break;
643 case PrintfQualifierPointer:
644 size = sizeof(void *);
645 number = (uint64_t) (unsigned long)va_arg(ap, void *);
646 break;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000647 }
648
649 if (flags & __PRINTF_FLAG_SIGNED) {
Patrick Georgi7086ea92014-12-29 19:53:51 +0100650 if (number & (0x1ULL << (size * 8 - 1))) {
Uwe Hermannc52761b2008-03-20 00:02:07 +0000651 flags |= __PRINTF_FLAG_NEGATIVE;
652
653 if (size == sizeof(uint64_t)) {
654 number = -((int64_t) number);
655 } else {
656 number = ~number;
657 number &= ~(0xFFFFFFFFFFFFFFFFll << (size * 8));
658 number++;
659 }
660 }
661 }
662
663 if ((retval = print_number(number, width, precision,
Julius Werner2fe505b2014-04-17 20:00:20 -0700664 base, flags, ps)) < 0)
665 return retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000666
667 counter += retval;
668 j = i + 1;
669 }
670next_char:
671 ++i;
672 }
673
674 if (i > j) {
675 if ((retval = printf_putnchars(&fmt[j],
Julius Werner2fe505b2014-04-17 20:00:20 -0700676 (u64) (i - j), ps)) < 0)
677 return retval;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000678 counter += retval;
679 }
680
Uwe Hermannc52761b2008-03-20 00:02:07 +0000681 return counter;
682}
683
Uwe Hermann0a896252008-04-02 12:35:45 +0000684int snprintf(char *str, size_t size, const char *fmt, ...)
685{
686 int ret;
687 va_list args;
688
689 va_start(args, fmt);
690 ret = vsnprintf(str, size, fmt, args);
691 va_end(args);
692
693 return ret;
694}
695
Uwe Hermannc52761b2008-03-20 00:02:07 +0000696int sprintf(char *str, const char *fmt, ...)
697{
698 int ret;
699 va_list args;
700
701 va_start(args, fmt);
702 ret = vsprintf(str, fmt, args);
703 va_end(args);
704
705 return ret;
706}
707
Patrick Georgicd913bd2011-02-14 19:25:27 +0000708int fprintf(FILE *file, const char *fmt, ...)
709{
710 int ret;
711 if ((file == stdout) || (file == stderr)) {
712 va_list args;
713 va_start(args, fmt);
714 ret = vprintf(fmt, args);
715 va_end(args);
716
717 return ret;
718 }
719 return -1;
720}
721
Uwe Hermannc52761b2008-03-20 00:02:07 +0000722struct vsnprintf_data {
723 size_t size; /* Total space for string */
724 size_t len; /* Count of currently used characters */
725 char *string; /* Destination string */
726};
727
728/**
729 * Write string to given buffer.
730 *
731 * Write at most data->size characters including trailing zero. According to
732 * C99, snprintf() has to return number of characters that would have been
733 * written if enough space had been available. Hence the return value is not
734 * number of really printed characters but size of the input string.
735 * Number of really used characters is stored in data->len.
736 *
737 * @param str Source string to print.
738 * @param count Size of source string.
Mathias Krause6efbebd2012-04-03 21:02:33 +0200739 * @param _data Structure with destination string, counter of used space
Uwe Hermannc52761b2008-03-20 00:02:07 +0000740 * and total string size.
741 * @return Number of characters to print (not characters really printed!).
742 */
Mathias Krause6efbebd2012-04-03 21:02:33 +0200743static int vsnprintf_write(const char *str, size_t count, void *_data)
Uwe Hermannc52761b2008-03-20 00:02:07 +0000744{
Mathias Krause6efbebd2012-04-03 21:02:33 +0200745 struct vsnprintf_data *data = _data;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000746 size_t i;
747
748 i = data->size - data->len;
749 if (i == 0)
750 return count;
751
752 /* We have only one free byte left in buffer => write trailing zero. */
753 if (i == 1) {
754 data->string[data->size - 1] = 0;
755 data->len = data->size;
756 return count;
757 }
758
759 /*
760 * We have not enough space for whole string with the trailing
761 * zero => print only a part of string.
762 */
763 if (i <= count) {
764 memcpy((void *)(data->string + data->len), (void *)str, i - 1);
765 data->string[data->size - 1] = 0;
766 data->len = data->size;
767 return count;
768 }
769
770 /* Buffer is big enough to print whole string. */
771 memcpy((void *)(data->string + data->len), (void *)str, count);
772 data->len += count;
773 /*
774 * Put trailing zero at end, but not count it into data->len so
775 * it could be rewritten next time.
776 */
777 data->string[data->len] = 0;
778
779 return count;
780}
781
782int vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
783{
784 struct vsnprintf_data data = { size, 0, str };
Mathias Krause6efbebd2012-04-03 21:02:33 +0200785 struct printf_spec ps = { vsnprintf_write, &data };
Uwe Hermannc52761b2008-03-20 00:02:07 +0000786
787 /* Print 0 at end of string - fix case that nothing will be printed. */
788 if (size > 0)
789 str[0] = 0;
790
791 /* vsnprintf_write() ensures that str will be terminated by zero. */
792 return printf_core(fmt, &ps, ap);
793}
794
795int vsprintf(char *str, const char *fmt, va_list ap)
796{
797 return vsnprintf(str, (size_t) - 1, fmt, ap);
798}
799
800int printf(const char *fmt, ...)
801{
802 int ret;
803 va_list args;
804
805 va_start(args, fmt);
806 ret = vprintf(fmt, args);
807 va_end(args);
808
809 return ret;
810}
811
812static int vprintf_write(const char *str, size_t count, void *unused)
813{
Julius Werner2fe505b2014-04-17 20:00:20 -0700814 console_write(str, count);
815 return count;
Uwe Hermannc52761b2008-03-20 00:02:07 +0000816}
817
818int vprintf(const char *fmt, va_list ap)
819{
Mathias Krause6efbebd2012-04-03 21:02:33 +0200820 struct printf_spec ps = { vprintf_write, NULL };
Uwe Hermannc52761b2008-03-20 00:02:07 +0000821
822 return printf_core(fmt, &ps, ap);
823}