source: mainline/boot/generic/src/printf_core.c@ de1712e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since de1712e was dc12262, checked in by Martin Decky <martin@…>, 8 years ago

add standardized case fallthrough comment annotations, add actual missing breaks

GCC 7.1's attribute((fallthrough)) would be more elegant, but unfortunatelly this annotation is incompatible with previous versions of GCC (it generates an empty declaration error)

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