source: mainline/boot/generic/src/printf_core.c@ 52c60b6

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

unify printf implementations

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