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

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

new boot infrastructure

  • more code and metadata unification
  • import of up-to-date implementations from the kernel
  • the boot loaders should behave more similarly on all platforms
  • support for deflate compressed (LZ77) boot components
    • this again allows feasible boot images to be created on mips32
  • IA64 is still not booting
    • the broken forked GNU EFI library has been removed, a replacement of the functionality is on its way
  • 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
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 * If conversion is "c", the character is wchar_t (wide character).@n
435 * If conversion is "s", the string is wchar_t * (wide string).@n
436 * - "ll" Signed or unsigned long long int.@n
437 *
438 * CONVERSION:@n
439 * - % Print percentile character itself.
440 *
441 * - c Print single character. The character is expected to be plain
442 * ASCII (e.g. only values 0 .. 127 are valid).@n
443 *
444 * - s Print zero terminated string. If a NULL value is passed as
445 * value, "(NULL)" is printed instead.@n
446 *
447 * - P, p Print value of a pointer. Void * value is expected and it is
448 * printed in hexadecimal notation with prefix (as with
449 * \%#0.8X / \%#0.8x for 32-bit or \%#0.16lX / \%#0.16lx for 64-bit
450 * long pointers).
451 *
452 * - b Print value as unsigned binary number. Prefix is not printed by
453 * default. (Nonstandard extension.)
454 *
455 * - o Print value as unsigned octal number. Prefix is not printed by
456 * default.
457 *
458 * - d, i Print signed decimal number. There is no difference between d
459 * and i conversion.
460 *
461 * - u Print unsigned decimal number.
462 *
463 * - X, x Print hexadecimal number with upper- or lower-case. Prefix is
464 * not printed by default.
465 *
466 * All other characters from fmt except the formatting directives are printed
467 * verbatim.
468 *
469 * @param fmt Format NULL-terminated string.
470 *
471 * @return Number of characters printed, negative value on failure.
472 *
473 */
474int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
475{
476 size_t i; /* Index of the currently processed character from fmt */
477 size_t nxt = 0; /* Index of the next character from fmt */
478 size_t j = 0; /* Index to the first not printed nonformating character */
479
480 size_t counter = 0; /* Number of characters printed */
481 int retval; /* Return values from nested functions */
482
483 while (true) {
484 i = nxt;
485 wchar_t uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
486
487 if (uc == 0)
488 break;
489
490 /* Control character */
491 if (uc == '%') {
492 /* Print common characters if any processed */
493 if (i > j) {
494 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
495 /* Error */
496 counter = -counter;
497 goto out;
498 }
499 counter += retval;
500 }
501
502 j = i;
503
504 /* Parse modifiers */
505 uint32_t flags = 0;
506 bool end = false;
507
508 do {
509 i = nxt;
510 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
511 switch (uc) {
512 case '#':
513 flags |= __PRINTF_FLAG_PREFIX;
514 break;
515 case '-':
516 flags |= __PRINTF_FLAG_LEFTALIGNED;
517 break;
518 case '+':
519 flags |= __PRINTF_FLAG_SHOWPLUS;
520 break;
521 case ' ':
522 flags |= __PRINTF_FLAG_SPACESIGN;
523 break;
524 case '0':
525 flags |= __PRINTF_FLAG_ZEROPADDED;
526 break;
527 default:
528 end = true;
529 };
530 } while (!end);
531
532 /* Width & '*' operator */
533 int width = 0;
534 if (isdigit(uc)) {
535 while (true) {
536 width *= 10;
537 width += uc - '0';
538
539 i = nxt;
540 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
541 if (uc == 0)
542 break;
543 if (!isdigit(uc))
544 break;
545 }
546 } else if (uc == '*') {
547 /* Get width value from argument list */
548 i = nxt;
549 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
550 width = (int) va_arg(ap, int);
551 if (width < 0) {
552 /* Negative width sets '-' flag */
553 width *= -1;
554 flags |= __PRINTF_FLAG_LEFTALIGNED;
555 }
556 }
557
558 /* Precision and '*' operator */
559 int precision = 0;
560 if (uc == '.') {
561 i = nxt;
562 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
563 if (isdigit(uc)) {
564 while (true) {
565 precision *= 10;
566 precision += uc - '0';
567
568 i = nxt;
569 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
570 if (uc == 0)
571 break;
572 if (!isdigit(uc))
573 break;
574 }
575 } else if (uc == '*') {
576 /* Get precision value from the argument list */
577 i = nxt;
578 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
579 precision = (int) va_arg(ap, int);
580 if (precision < 0) {
581 /* Ignore negative precision */
582 precision = 0;
583 }
584 }
585 }
586
587 qualifier_t qualifier;
588
589 switch (uc) {
590 /** @todo Unimplemented qualifiers:
591 * t ptrdiff_t - ISO C 99
592 */
593 case 'h':
594 /* Char or short */
595 qualifier = PrintfQualifierShort;
596 i = nxt;
597 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
598 if (uc == 'h') {
599 i = nxt;
600 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
601 qualifier = PrintfQualifierByte;
602 }
603 break;
604 case 'l':
605 /* Long or long long */
606 qualifier = PrintfQualifierLong;
607 i = nxt;
608 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
609 if (uc == 'l') {
610 i = nxt;
611 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
612 qualifier = PrintfQualifierLongLong;
613 }
614 break;
615 default:
616 /* Default type */
617 qualifier = PrintfQualifierInt;
618 }
619
620 unsigned int base = 10;
621
622 switch (uc) {
623 /*
624 * String and character conversions.
625 */
626 case 's':
627 retval = print_str(va_arg(ap, char *), width, precision, flags, ps);
628
629 if (retval < 0) {
630 counter = -counter;
631 goto out;
632 }
633
634 counter += retval;
635 j = nxt;
636 goto next_char;
637 case 'c':
638 retval = print_char(va_arg(ap, unsigned int), width, flags, ps);
639
640 if (retval < 0) {
641 counter = -counter;
642 goto out;
643 };
644
645 counter += retval;
646 j = nxt;
647 goto next_char;
648
649 /*
650 * Integer values
651 */
652 case 'P':
653 /* Pointer */
654 flags |= __PRINTF_FLAG_BIGCHARS;
655 case 'p':
656 flags |= __PRINTF_FLAG_PREFIX;
657 flags |= __PRINTF_FLAG_ZEROPADDED;
658 base = 16;
659 qualifier = PrintfQualifierPointer;
660 break;
661 case 'b':
662 base = 2;
663 break;
664 case 'o':
665 base = 8;
666 break;
667 case 'd':
668 case 'i':
669 flags |= __PRINTF_FLAG_SIGNED;
670 case 'u':
671 break;
672 case 'X':
673 flags |= __PRINTF_FLAG_BIGCHARS;
674 case 'x':
675 base = 16;
676 break;
677
678 /* Percentile itself */
679 case '%':
680 j = i;
681 goto next_char;
682
683 /*
684 * Bad formatting.
685 */
686 default:
687 /*
688 * Unknown format. Now, j is the index of '%'
689 * so we will print whole bad format sequence.
690 */
691 goto next_char;
692 }
693
694 /* Print integers */
695 size_t size;
696 uint64_t number;
697 switch (qualifier) {
698 case PrintfQualifierByte:
699 size = sizeof(unsigned char);
700 number = (uint64_t) va_arg(ap, unsigned int);
701 break;
702 case PrintfQualifierShort:
703 size = sizeof(unsigned short);
704 number = (uint64_t) va_arg(ap, unsigned int);
705 break;
706 case PrintfQualifierInt:
707 size = sizeof(unsigned int);
708 number = (uint64_t) va_arg(ap, unsigned int);
709 break;
710 case PrintfQualifierLong:
711 size = sizeof(unsigned long);
712 number = (uint64_t) va_arg(ap, unsigned long);
713 break;
714 case PrintfQualifierLongLong:
715 size = sizeof(unsigned long long);
716 number = (uint64_t) va_arg(ap, unsigned long long);
717 break;
718 case PrintfQualifierPointer:
719 size = sizeof(void *);
720 precision = size << 1;
721 number = (uint64_t) (uintptr_t) va_arg(ap, void *);
722 break;
723 default:
724 /* Unknown qualifier */
725 counter = -counter;
726 goto out;
727 }
728
729 if (flags & __PRINTF_FLAG_SIGNED) {
730 if (number & (0x1 << (size * 8 - 1))) {
731 flags |= __PRINTF_FLAG_NEGATIVE;
732
733 if (size == sizeof(uint64_t)) {
734 number = -((int64_t) number);
735 } else {
736 number = ~number;
737 number &=
738 ~(0xFFFFFFFFFFFFFFFFll <<
739 (size * 8));
740 number++;
741 }
742 }
743 }
744
745 if ((retval = print_number(number, width, precision,
746 base, flags, ps)) < 0) {
747 counter = -counter;
748 goto out;
749 }
750
751 counter += retval;
752 j = nxt;
753 }
754next_char:
755 ;
756 }
757
758 if (i > j) {
759 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
760 /* Error */
761 counter = -counter;
762 goto out;
763 }
764 counter += retval;
765 }
766
767out:
768 return ((int) counter);
769}
770
771/** @}
772 */
Note: See TracBrowser for help on using the repository browser.