source: mainline/boot/generic/src/printf_core.c@ 907d91a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 907d91a was 907d91a, checked in by Jakub Jermar <jakub@…>, 8 years ago

Add missing breaks and decode the next character

  • Property mode set to 100644
File size: 19.0 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 case 'p':
684 flags |= __PRINTF_FLAG_PREFIX;
685 flags |= __PRINTF_FLAG_ZEROPADDED;
686 base = 16;
687 qualifier = PrintfQualifierPointer;
688 break;
689 case 'b':
690 base = 2;
691 break;
692 case 'o':
693 base = 8;
694 break;
695 case 'd':
696 case 'i':
697 flags |= __PRINTF_FLAG_SIGNED;
698 case 'u':
699 break;
700 case 'X':
701 flags |= __PRINTF_FLAG_BIGCHARS;
702 case 'x':
703 base = 16;
704 break;
705
706 /* Percentile itself */
707 case '%':
708 j = i;
709 goto next_char;
710
711 /*
712 * Bad formatting.
713 */
714 default:
715 /*
716 * Unknown format. Now, j is the index of '%'
717 * so we will print whole bad format sequence.
718 */
719 goto next_char;
720 }
721
722 /* Print integers */
723 size_t size;
724 uint64_t number;
725
726 switch (qualifier) {
727 case PrintfQualifierByte:
728 size = sizeof(unsigned char);
729 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
730 break;
731 case PrintfQualifierShort:
732 size = sizeof(unsigned short);
733 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
734 break;
735 case PrintfQualifierInt:
736 size = sizeof(unsigned int);
737 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
738 break;
739 case PrintfQualifierLong:
740 size = sizeof(unsigned long);
741 number = PRINTF_GET_INT_ARGUMENT(long, ap, flags);
742 break;
743 case PrintfQualifierLongLong:
744 size = sizeof(unsigned long long);
745 number = PRINTF_GET_INT_ARGUMENT(long long, ap, flags);
746 break;
747 case PrintfQualifierPointer:
748 size = sizeof(void *);
749 precision = size << 1;
750 number = (uint64_t) (uintptr_t) va_arg(ap, void *);
751 break;
752 case PrintfQualifierSize:
753 size = sizeof(size_t);
754 number = (uint64_t) va_arg(ap, size_t);
755 break;
756 default:
757 /* Unknown qualifier */
758 counter = -counter;
759 goto out;
760 }
761
762 if ((retval = print_number(number, width, precision,
763 base, flags, ps)) < 0) {
764 counter = -counter;
765 goto out;
766 }
767
768 counter += retval;
769 j = nxt;
770 }
771next_char:
772 ;
773 }
774
775 if (i > j) {
776 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
777 /* Error */
778 counter = -counter;
779 goto out;
780 }
781 counter += retval;
782 }
783
784out:
785 return ((int) counter);
786}
787
788/** @}
789 */
Note: See TracBrowser for help on using the repository browser.