source: mainline/boot/generic/src/printf_core.c@ 002fd5f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 002fd5f was d735e2e, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Boot should define bool in stdbool.h

  • Property mode set to 100644
File size: 19.2 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * Copyright (c) 2006 Josef Cejka
4 * Copyright (c) 2009 Martin Decky
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @file
33 * @brief Printing functions.
34 */
35
36#include <printf_core.h>
37#include <printf.h>
38#include <stdarg.h>
39#include <stdbool.h>
40#include <stddef.h>
41#include <macros.h>
42#include <str.h>
43
44/** show prefixes 0x or 0 */
45#define __PRINTF_FLAG_PREFIX 0x00000001
46
47/** signed / unsigned number */
48#define __PRINTF_FLAG_SIGNED 0x00000002
49
50/** print leading zeroes */
51#define __PRINTF_FLAG_ZEROPADDED 0x00000004
52
53/** align to left */
54#define __PRINTF_FLAG_LEFTALIGNED 0x00000010
55
56/** always show + sign */
57#define __PRINTF_FLAG_SHOWPLUS 0x00000020
58
59/** print space instead of plus */
60#define __PRINTF_FLAG_SPACESIGN 0x00000040
61
62/** show big characters */
63#define __PRINTF_FLAG_BIGCHARS 0x00000080
64
65/** number has - sign */
66#define __PRINTF_FLAG_NEGATIVE 0x00000100
67
68/**
69 * Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
70 * to terminate string... (last one is only for better testing end of buffer by
71 * zero-filling subroutine)
72 */
73#define PRINT_NUMBER_BUFFER_SIZE (64 + 5)
74
75/** Get signed or unsigned integer argument */
76#define PRINTF_GET_INT_ARGUMENT(type, ap, flags) \
77 ({ \
78 unsigned type res; \
79 \
80 if ((flags) & __PRINTF_FLAG_SIGNED) { \
81 signed type arg = va_arg((ap), signed type); \
82 \
83 if (arg < 0) { \
84 res = -arg; \
85 (flags) |= __PRINTF_FLAG_NEGATIVE; \
86 } else \
87 res = arg; \
88 } else \
89 res = va_arg((ap), unsigned type); \
90 \
91 res; \
92 })
93
94/** Enumeration of possible arguments types.
95 */
96typedef enum {
97 PrintfQualifierByte = 0,
98 PrintfQualifierShort,
99 PrintfQualifierInt,
100 PrintfQualifierLong,
101 PrintfQualifierLongLong,
102 PrintfQualifierPointer,
103 PrintfQualifierSize
104} qualifier_t;
105
106static const char *nullstr = "(NULL)";
107static const char *digits_small = "0123456789abcdef";
108static const char *digits_big = "0123456789ABCDEF";
109static const char invalch = U_SPECIAL;
110
111/** Print one or more characters without adding newline.
112 *
113 * @param buf Buffer holding characters with size of
114 * at least size bytes. NULL is not allowed!
115 * @param size Size of the buffer in bytes.
116 * @param ps Output method and its data.
117 *
118 * @return Number of characters printed.
119 *
120 */
121static int printf_putnchars(const char *buf, size_t size,
122 printf_spec_t *ps)
123{
124 return ps->str_write((void *) buf, size, ps->data);
125}
126
127/** Print string without adding a newline.
128 *
129 * @param str String to print.
130 * @param ps Write function specification and support data.
131 *
132 * @return Number of characters printed.
133 *
134 */
135static int printf_putstr(const char *str, printf_spec_t *ps)
136{
137 if (str == NULL)
138 return printf_putnchars(nullstr, str_size(nullstr), ps);
139
140 return ps->str_write((void *) str, str_size(str), ps->data);
141}
142
143/** Print one ASCII character.
144 *
145 * @param c ASCII character to be printed.
146 * @param ps Output method.
147 *
148 * @return Number of characters printed.
149 *
150 */
151static int printf_putchar(const char ch, printf_spec_t *ps)
152{
153 if (!ascii_check(ch))
154 return ps->str_write((void *) &invalch, 1, ps->data);
155
156 return ps->str_write(&ch, 1, ps->data);
157}
158
159/** Print one formatted ASCII character.
160 *
161 * @param ch Character to print.
162 * @param width Width modifier.
163 * @param flags Flags that change the way the character is printed.
164 *
165 * @return Number of characters printed, negative value on failure.
166 *
167 */
168static int print_char(const char ch, int width, uint32_t flags, printf_spec_t *ps)
169{
170 size_t counter = 0;
171 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
172 while (--width > 0) {
173 /*
174 * One space is consumed by the character itself, hence
175 * the predecrement.
176 */
177 if (printf_putchar(' ', ps) > 0)
178 counter++;
179 }
180 }
181
182 if (printf_putchar(ch, ps) > 0)
183 counter++;
184
185 while (--width > 0) {
186 /*
187 * One space is consumed by the character itself, hence
188 * the predecrement.
189 */
190 if (printf_putchar(' ', ps) > 0)
191 counter++;
192 }
193
194 return (int) (counter);
195}
196
197/** Print string.
198 *
199 * @param str String to be printed.
200 * @param width Width modifier.
201 * @param precision Precision modifier.
202 * @param flags Flags that modify the way the string is printed.
203 *
204 * @return Number of characters printed, negative value on failure.
205 */
206static int print_str(char *str, int width, unsigned int precision,
207 uint32_t flags, printf_spec_t *ps)
208{
209 if (str == NULL)
210 return printf_putstr(nullstr, ps);
211
212 /* Print leading spaces. */
213 size_t strw = str_length(str);
214 if ((precision == 0) || (precision > strw))
215 precision = strw;
216
217 /* Left padding */
218 size_t counter = 0;
219 width -= precision;
220 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
221 while (width-- > 0) {
222 if (printf_putchar(' ', ps) == 1)
223 counter++;
224 }
225 }
226
227 /* Part of @a str fitting into the alloted space. */
228 int retval;
229 size_t size = str_lsize(str, precision);
230 if ((retval = printf_putnchars(str, size, ps)) < 0)
231 return -counter;
232
233 counter += retval;
234
235 /* Right padding */
236 while (width-- > 0) {
237 if (printf_putchar(' ', ps) == 1)
238 counter++;
239 }
240
241 return ((int) counter);
242}
243
244/** Print a number in a given base.
245 *
246 * Print significant digits of a number in given base.
247 *
248 * @param num Number to print.
249 * @param width Width modifier.
250 * @param precision Precision modifier.
251 * @param base Base to print the number in (must be between 2 and 16).
252 * @param flags Flags that modify the way the number is printed.
253 *
254 * @return Number of characters printed.
255 *
256 */
257static int print_number(uint64_t num, int width, int precision, int base,
258 uint32_t flags, printf_spec_t *ps)
259{
260 const char *digits;
261 if (flags & __PRINTF_FLAG_BIGCHARS)
262 digits = digits_big;
263 else
264 digits = digits_small;
265
266 char data[PRINT_NUMBER_BUFFER_SIZE];
267 char *ptr = &data[PRINT_NUMBER_BUFFER_SIZE - 1];
268
269 /* Size of number with all prefixes and signs */
270 int size = 0;
271
272 /* Put zero at end of string */
273 *ptr-- = 0;
274
275 if (num == 0) {
276 *ptr-- = '0';
277 size++;
278 } else {
279 do {
280 *ptr-- = digits[num % base];
281 size++;
282 } while (num /= base);
283 }
284
285 /* Size of plain number */
286 int number_size = size;
287
288 /*
289 * Collect the sum of all prefixes/signs/etc. to calculate padding and
290 * leading zeroes.
291 */
292 if (flags & __PRINTF_FLAG_PREFIX) {
293 switch (base) {
294 case 2:
295 /* Binary formating is not standard, but usefull */
296 size += 2;
297 break;
298 case 8:
299 size++;
300 break;
301 case 16:
302 size += 2;
303 break;
304 }
305 }
306
307 char sgn = 0;
308 if (flags & __PRINTF_FLAG_SIGNED) {
309 if (flags & __PRINTF_FLAG_NEGATIVE) {
310 sgn = '-';
311 size++;
312 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
313 sgn = '+';
314 size++;
315 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
316 sgn = ' ';
317 size++;
318 }
319 }
320
321 if (flags & __PRINTF_FLAG_LEFTALIGNED)
322 flags &= ~__PRINTF_FLAG_ZEROPADDED;
323
324 /*
325 * If the number is left-aligned or precision is specified then
326 * padding with zeros is ignored.
327 */
328 if (flags & __PRINTF_FLAG_ZEROPADDED) {
329 if ((precision == 0) && (width > size))
330 precision = width - size + number_size;
331 }
332
333 /* Print leading spaces */
334 if (number_size > precision) {
335 /* Print the whole number, not only a part */
336 precision = number_size;
337 }
338
339 width -= precision + size - number_size;
340 size_t counter = 0;
341
342 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
343 while (width-- > 0) {
344 if (printf_putchar(' ', ps) == 1)
345 counter++;
346 }
347 }
348
349 /* Print sign */
350 if (sgn) {
351 if (printf_putchar(sgn, ps) == 1)
352 counter++;
353 }
354
355 /* Print prefix */
356 if (flags & __PRINTF_FLAG_PREFIX) {
357 switch (base) {
358 case 2:
359 /* Binary formating is not standard, but usefull */
360 if (printf_putchar('0', ps) == 1)
361 counter++;
362 if (flags & __PRINTF_FLAG_BIGCHARS) {
363 if (printf_putchar('B', ps) == 1)
364 counter++;
365 } else {
366 if (printf_putchar('b', ps) == 1)
367 counter++;
368 }
369 break;
370 case 8:
371 if (printf_putchar('o', ps) == 1)
372 counter++;
373 break;
374 case 16:
375 if (printf_putchar('0', ps) == 1)
376 counter++;
377 if (flags & __PRINTF_FLAG_BIGCHARS) {
378 if (printf_putchar('X', ps) == 1)
379 counter++;
380 } else {
381 if (printf_putchar('x', ps) == 1)
382 counter++;
383 }
384 break;
385 }
386 }
387
388 /* Print leading zeroes */
389 precision -= number_size;
390 while (precision-- > 0) {
391 if (printf_putchar('0', ps) == 1)
392 counter++;
393 }
394
395 /* Print the number itself */
396 int retval;
397 if ((retval = printf_putstr(++ptr, ps)) > 0)
398 counter += retval;
399
400 /* Print trailing spaces */
401
402 while (width-- > 0) {
403 if (printf_putchar(' ', ps) == 1)
404 counter++;
405 }
406
407 return ((int) counter);
408}
409
410/** Print formatted string.
411 *
412 * Print string formatted according to the fmt parameter and variadic arguments.
413 * Each formatting directive must have the following form:
414 *
415 * \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
416 *
417 * FLAGS:@n
418 * - "#" Force to print prefix. For \%o conversion, the prefix is 0, for
419 * \%x and \%X prefixes are 0x and 0X and for conversion \%b the
420 * prefix is 0b.
421 *
422 * - "-" Align to left.
423 *
424 * - "+" Print positive sign just as negative.
425 *
426 * - " " If the printed number is positive and "+" flag is not set,
427 * print space in place of sign.
428 *
429 * - "0" Print 0 as padding instead of spaces. Zeroes are placed between
430 * sign and the rest of the number. This flag is ignored if "-"
431 * flag is specified.
432 *
433 * WIDTH:@n
434 * - Specify the minimal width of a printed argument. If it is bigger,
435 * width is ignored. If width is specified with a "*" character instead of
436 * number, width is taken from parameter list. And integer parameter is
437 * expected before parameter for processed conversion specification. If
438 * this value is negative its absolute value is taken and the "-" flag is
439 * set.
440 *
441 * PRECISION:@n
442 * - Value precision. For numbers it specifies minimum valid numbers.
443 * Smaller numbers are printed with leading zeroes. Bigger numbers are not
444 * affected. Strings with more than precision characters are cut off. Just
445 * as with width, an "*" can be used used instead of a number. An integer
446 * value is then expected in parameters. When both width and precision are
447 * specified using "*", the first parameter is used for width and the
448 * second one for precision.
449 *
450 * TYPE:@n
451 * - "hh" Signed or unsigned char.@n
452 * - "h" Signed or unsigned short.@n
453 * - "" Signed or unsigned int (default value).@n
454 * - "l" Signed or unsigned long int.@n
455 * - "ll" Signed or unsigned long long int.@n
456 *
457 * CONVERSION:@n
458 * - % Print percentile character itself.
459 *
460 * - c Print single character. The character is expected to be plain
461 * ASCII (e.g. only values 0 .. 127 are valid).@n
462 *
463 * - s Print zero terminated string. If a NULL value is passed as
464 * value, "(NULL)" is printed instead.@n
465 *
466 * - P, p Print value of a pointer. Void * value is expected and it is
467 * printed in hexadecimal notation with prefix (as with
468 * \%#0.8X / \%#0.8x for 32-bit or \%#0.16lX / \%#0.16lx for 64-bit
469 * long pointers).
470 *
471 * - b Print value as unsigned binary number. Prefix is not printed by
472 * default. (Nonstandard extension.)
473 *
474 * - o Print value as unsigned octal number. Prefix is not printed by
475 * default.
476 *
477 * - d, i Print signed decimal number. There is no difference between d
478 * and i conversion.
479 *
480 * - u Print unsigned decimal number.
481 *
482 * - X, x Print hexadecimal number with upper- or lower-case. Prefix is
483 * not printed by default.
484 *
485 * All other characters from fmt except the formatting directives are printed
486 * verbatim.
487 *
488 * @param fmt Format NULL-terminated string.
489 *
490 * @return Number of characters printed, negative value on failure.
491 *
492 */
493int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
494{
495 size_t i; /* Index of the currently processed character from fmt */
496 size_t nxt = 0; /* Index of the next character from fmt */
497 size_t j = 0; /* Index to the first not printed nonformating character */
498
499 size_t counter = 0; /* Number of characters printed */
500 int retval; /* Return values from nested functions */
501
502 while (true) {
503 i = nxt;
504 wchar_t uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
505
506 if (uc == 0)
507 break;
508
509 /* Control character */
510 if (uc == '%') {
511 /* Print common characters if any processed */
512 if (i > j) {
513 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
514 /* Error */
515 counter = -counter;
516 goto out;
517 }
518 counter += retval;
519 }
520
521 j = i;
522
523 /* Parse modifiers */
524 uint32_t flags = 0;
525 bool end = false;
526
527 do {
528 i = nxt;
529 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
530 switch (uc) {
531 case '#':
532 flags |= __PRINTF_FLAG_PREFIX;
533 break;
534 case '-':
535 flags |= __PRINTF_FLAG_LEFTALIGNED;
536 break;
537 case '+':
538 flags |= __PRINTF_FLAG_SHOWPLUS;
539 break;
540 case ' ':
541 flags |= __PRINTF_FLAG_SPACESIGN;
542 break;
543 case '0':
544 flags |= __PRINTF_FLAG_ZEROPADDED;
545 break;
546 default:
547 end = true;
548 };
549 } while (!end);
550
551 /* Width & '*' operator */
552 int width = 0;
553 if (isdigit(uc)) {
554 while (true) {
555 width *= 10;
556 width += uc - '0';
557
558 i = nxt;
559 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
560 if (uc == 0)
561 break;
562 if (!isdigit(uc))
563 break;
564 }
565 } else if (uc == '*') {
566 /* Get width value from argument list */
567 i = nxt;
568 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
569 width = (int) va_arg(ap, int);
570 if (width < 0) {
571 /* Negative width sets '-' flag */
572 width *= -1;
573 flags |= __PRINTF_FLAG_LEFTALIGNED;
574 }
575 }
576
577 /* Precision and '*' operator */
578 int precision = 0;
579 if (uc == '.') {
580 i = nxt;
581 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
582 if (isdigit(uc)) {
583 while (true) {
584 precision *= 10;
585 precision += uc - '0';
586
587 i = nxt;
588 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
589 if (uc == 0)
590 break;
591 if (!isdigit(uc))
592 break;
593 }
594 } else if (uc == '*') {
595 /* Get precision value from the argument list */
596 i = nxt;
597 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
598 precision = (int) va_arg(ap, int);
599 if (precision < 0) {
600 /* Ignore negative precision */
601 precision = 0;
602 }
603 }
604 }
605
606 qualifier_t qualifier;
607
608 switch (uc) {
609 case 't':
610 /* ptrdiff_t */
611 if (sizeof(ptrdiff_t) == sizeof(int32_t))
612 qualifier = PrintfQualifierInt;
613 else
614 qualifier = PrintfQualifierLongLong;
615 i = nxt;
616 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
617 break;
618 case 'h':
619 /* Char or short */
620 qualifier = PrintfQualifierShort;
621 i = nxt;
622 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
623 if (uc == 'h') {
624 i = nxt;
625 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
626 qualifier = PrintfQualifierByte;
627 }
628 break;
629 case 'l':
630 /* Long or long long */
631 qualifier = PrintfQualifierLong;
632 i = nxt;
633 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
634 if (uc == 'l') {
635 i = nxt;
636 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
637 qualifier = PrintfQualifierLongLong;
638 }
639 break;
640 case 'z':
641 qualifier = PrintfQualifierSize;
642 i = nxt;
643 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
644 break;
645 default:
646 /* Default type */
647 qualifier = PrintfQualifierInt;
648 }
649
650 unsigned int base = 10;
651
652 switch (uc) {
653 /*
654 * String and character conversions.
655 */
656 case 's':
657 retval = print_str(va_arg(ap, char *), width, precision, flags, ps);
658
659 if (retval < 0) {
660 counter = -counter;
661 goto out;
662 }
663
664 counter += retval;
665 j = nxt;
666 goto next_char;
667 case 'c':
668 retval = print_char(va_arg(ap, unsigned int), width, flags, ps);
669
670 if (retval < 0) {
671 counter = -counter;
672 goto out;
673 };
674
675 counter += retval;
676 j = nxt;
677 goto next_char;
678
679 /*
680 * Integer values
681 */
682 case 'P':
683 /* Pointer */
684 flags |= __PRINTF_FLAG_BIGCHARS;
685 /* Fallthrough */
686 case 'p':
687 flags |= __PRINTF_FLAG_PREFIX;
688 flags |= __PRINTF_FLAG_ZEROPADDED;
689 base = 16;
690 qualifier = PrintfQualifierPointer;
691 break;
692 case 'b':
693 base = 2;
694 break;
695 case 'o':
696 base = 8;
697 break;
698 case 'd':
699 case 'i':
700 flags |= __PRINTF_FLAG_SIGNED;
701 /* Fallthrough */
702 case 'u':
703 break;
704 case 'X':
705 flags |= __PRINTF_FLAG_BIGCHARS;
706 /* Fallthrough */
707 case 'x':
708 base = 16;
709 break;
710
711 /* Percentile itself */
712 case '%':
713 j = i;
714 goto next_char;
715
716 /*
717 * Bad formatting.
718 */
719 default:
720 /*
721 * Unknown format. Now, j is the index of '%'
722 * so we will print whole bad format sequence.
723 */
724 goto next_char;
725 }
726
727 /* Print integers */
728 size_t size;
729 uint64_t number;
730
731 switch (qualifier) {
732 case PrintfQualifierByte:
733 size = sizeof(unsigned char);
734 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
735 break;
736 case PrintfQualifierShort:
737 size = sizeof(unsigned short);
738 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
739 break;
740 case PrintfQualifierInt:
741 size = sizeof(unsigned int);
742 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
743 break;
744 case PrintfQualifierLong:
745 size = sizeof(unsigned long);
746 number = PRINTF_GET_INT_ARGUMENT(long, ap, flags);
747 break;
748 case PrintfQualifierLongLong:
749 size = sizeof(unsigned long long);
750 number = PRINTF_GET_INT_ARGUMENT(long long, ap, flags);
751 break;
752 case PrintfQualifierPointer:
753 size = sizeof(void *);
754 precision = size << 1;
755 number = (uint64_t) (uintptr_t) va_arg(ap, void *);
756 break;
757 case PrintfQualifierSize:
758 size = sizeof(size_t);
759 number = (uint64_t) va_arg(ap, size_t);
760 break;
761 default:
762 /* Unknown qualifier */
763 counter = -counter;
764 goto out;
765 }
766
767 if ((retval = print_number(number, width, precision,
768 base, flags, ps)) < 0) {
769 counter = -counter;
770 goto out;
771 }
772
773 counter += retval;
774 j = nxt;
775 }
776next_char:
777 ;
778 }
779
780 if (i > j) {
781 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
782 /* Error */
783 counter = -counter;
784 goto out;
785 }
786 counter += retval;
787 }
788
789out:
790 return ((int) counter);
791}
792
793/** @}
794 */
Note: See TracBrowser for help on using the repository browser.