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

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