source: mainline/kernel/generic/src/printf/printf_core.c@ e2b762ec

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e2b762ec was 16da5f8e, checked in by Jiri Svoboda <jirik.svoboda@…>, 16 years ago

String functions should be declared in string.h (and implemented in string.c) in the kernel.

  • Property mode set to 100644
File size: 17.5 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * Copyright (c) 2006 Josef Cejka
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup generic
31 * @{
32 */
33/**
34 * @file
35 * @brief Printing functions.
36 */
37
38#include <printf/printf_core.h>
39#include <putchar.h>
40#include <print.h>
41#include <arch/arg.h>
42#include <macros.h>
43#include <string.h>
44#include <arch.h>
45
46/** show prefixes 0x or 0 */
47#define __PRINTF_FLAG_PREFIX 0x00000001
48/** signed / unsigned number */
49#define __PRINTF_FLAG_SIGNED 0x00000002
50/** print leading zeroes */
51#define __PRINTF_FLAG_ZEROPADDED 0x00000004
52/** align to left */
53#define __PRINTF_FLAG_LEFTALIGNED 0x00000010
54/** always show + sign */
55#define __PRINTF_FLAG_SHOWPLUS 0x00000020
56/** print space instead of plus */
57#define __PRINTF_FLAG_SPACESIGN 0x00000040
58/** show big characters */
59#define __PRINTF_FLAG_BIGCHARS 0x00000080
60/** number has - sign */
61#define __PRINTF_FLAG_NEGATIVE 0x00000100
62
63/**
64 * Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
65 * to terminate string... (last one is only for better testing end of buffer by
66 * zero-filling subroutine)
67 */
68#define PRINT_NUMBER_BUFFER_SIZE (64 + 5)
69
70/** Enumeration of possible arguments types.
71 */
72typedef enum {
73 PrintfQualifierByte = 0,
74 PrintfQualifierShort,
75 PrintfQualifierInt,
76 PrintfQualifierLong,
77 PrintfQualifierLongLong,
78 PrintfQualifierPointer
79} qualifier_t;
80
81static char digits_small[] = "0123456789abcdef";
82static char digits_big[] = "0123456789ABCDEF";
83
84/** Print one or more characters without adding newline.
85 *
86 * @param buf Buffer with size at least count bytes. NULL pointer is
87 * not allowed!
88 * @param count Number of characters to print.
89 * @param ps Output method and its data.
90 * @return Number of characters printed.
91 */
92static int printf_putnchars(const char * buf, size_t count,
93 struct printf_spec *ps)
94{
95 return ps->write((void *) buf, count, ps->data);
96}
97
98/** Print a string without adding a newline.
99 *
100 * @param str String to print.
101 * @param ps Write function specification and support data.
102 * @return Number of characters printed.
103 */
104static int printf_putstr(const char * str, struct printf_spec *ps)
105{
106 size_t count;
107
108 if (str == NULL) {
109 char *nullstr = "(NULL)";
110 return printf_putnchars(nullstr, strlen(nullstr), ps);
111 }
112
113 count = strlen(str);
114
115 return ps->write((void *) str, count, ps->data);
116}
117
118/** Print one character.
119 *
120 * @param c Character to be printed.
121 * @param ps Output method.
122 *
123 * @return Number of characters printed.
124 */
125static int printf_putchar(int c, struct printf_spec *ps)
126{
127 unsigned char ch = c;
128
129 return ps->write((void *) &ch, 1, ps->data);
130}
131
132/** Print one formatted character.
133 *
134 * @param c Character to print.
135 * @param width Width modifier.
136 * @param flags Flags that change the way the character is printed.
137 *
138 * @return Number of characters printed, negative value on failure.
139 */
140static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps)
141{
142 int counter = 0;
143
144 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
145 while (--width > 0) {
146 /*
147 * One space is consumed by the character itself, hence
148 * the predecrement.
149 */
150 if (printf_putchar(' ', ps) > 0)
151 ++counter;
152 }
153 }
154
155 if (printf_putchar(c, ps) > 0)
156 counter++;
157
158 while (--width > 0) {
159 /*
160 * One space is consumed by the character itself, hence
161 * the predecrement.
162 */
163 if (printf_putchar(' ', ps) > 0)
164 ++counter;
165 }
166
167 return ++counter;
168}
169
170/** Print string.
171 *
172 * @param s String to be printed.
173 * @param width Width modifier.
174 * @param precision Precision modifier.
175 * @param flags Flags that modify the way the string is printed.
176 *
177 * @return Number of characters printed, negative value on failure.
178 */
179static int print_string(char *s, int width, unsigned int precision,
180 uint64_t flags, struct printf_spec *ps)
181{
182 int counter = 0;
183 size_t size;
184 int retval;
185
186 if (s == NULL) {
187 return printf_putstr("(NULL)", ps);
188 }
189
190 size = strlen(s);
191
192 /* print leading spaces */
193
194 if (precision == 0)
195 precision = size;
196
197 width -= precision;
198
199 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
200 while (width-- > 0) {
201 if (printf_putchar(' ', ps) == 1)
202 counter++;
203 }
204 }
205
206 if ((retval = printf_putnchars(s, min(size, precision), ps)) < 0) {
207 return -counter;
208 }
209 counter += retval;
210
211 while (width-- > 0) {
212 if (printf_putchar(' ', ps) == 1)
213 ++counter;
214 }
215
216 return counter;
217}
218
219
220/** Print a number in a given base.
221 *
222 * Print significant digits of a number in given base.
223 *
224 * @param num Number to print.
225 * @param widt Width modifier.h
226 * @param precision Precision modifier.
227 * @param base Base to print the number in (must be between 2 and 16).
228 * @param flags Flags that modify the way the number is printed.
229 *
230 * @return Number of characters printed.
231 *
232 */
233static int print_number(uint64_t num, int width, int precision, int base,
234 uint64_t flags, struct printf_spec *ps)
235{
236 char *digits = digits_small;
237 char d[PRINT_NUMBER_BUFFER_SIZE];
238 char *ptr = &d[PRINT_NUMBER_BUFFER_SIZE - 1];
239 int size = 0; /* size of number with all prefixes and signs */
240 int number_size; /* size of plain number */
241 char sgn;
242 int retval;
243 int counter = 0;
244
245 if (flags & __PRINTF_FLAG_BIGCHARS)
246 digits = digits_big;
247
248 *ptr-- = 0; /* Put zero at end of string */
249
250 if (num == 0) {
251 *ptr-- = '0';
252 size++;
253 } else {
254 do {
255 *ptr-- = digits[num % base];
256 size++;
257 } while (num /= base);
258 }
259
260 number_size = size;
261
262 /*
263 * Collect the sum of all prefixes/signs/... to calculate padding and
264 * leading zeroes.
265 */
266 if (flags & __PRINTF_FLAG_PREFIX) {
267 switch(base) {
268 case 2: /* Binary formating is not standard, but usefull */
269 size += 2;
270 break;
271 case 8:
272 size++;
273 break;
274 case 16:
275 size += 2;
276 break;
277 }
278 }
279
280 sgn = 0;
281 if (flags & __PRINTF_FLAG_SIGNED) {
282 if (flags & __PRINTF_FLAG_NEGATIVE) {
283 sgn = '-';
284 size++;
285 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
286 sgn = '+';
287 size++;
288 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
289 sgn = ' ';
290 size++;
291 }
292 }
293
294 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
295 flags &= ~__PRINTF_FLAG_ZEROPADDED;
296 }
297
298 /*
299 * If the number is leftaligned or precision is specified then
300 * zeropadding is ignored.
301 */
302 if (flags & __PRINTF_FLAG_ZEROPADDED) {
303 if ((precision == 0) && (width > size)) {
304 precision = width - size + number_size;
305 }
306 }
307
308 /* print leading spaces */
309 if (number_size > precision) {
310 /* print the whole number not only a part */
311 precision = number_size;
312 }
313
314 width -= precision + size - number_size;
315
316 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
317 while (width-- > 0) {
318 if (printf_putchar(' ', ps) == 1)
319 counter++;
320 }
321 }
322
323
324 /* print sign */
325 if (sgn) {
326 if (printf_putchar(sgn, ps) == 1)
327 counter++;
328 }
329
330 /* print prefix */
331
332 if (flags & __PRINTF_FLAG_PREFIX) {
333 switch(base) {
334 case 2: /* Binary formating is not standard, but usefull */
335 if (printf_putchar('0', ps) == 1)
336 counter++;
337 if (flags & __PRINTF_FLAG_BIGCHARS) {
338 if (printf_putchar('B', ps) == 1)
339 counter++;
340 } else {
341 if (printf_putchar('b', ps) == 1)
342 counter++;
343 }
344 break;
345 case 8:
346 if (printf_putchar('o', ps) == 1)
347 counter++;
348 break;
349 case 16:
350 if (printf_putchar('0', ps) == 1)
351 counter++;
352 if (flags & __PRINTF_FLAG_BIGCHARS) {
353 if (printf_putchar('X', ps) == 1)
354 counter++;
355 } else {
356 if (printf_putchar('x', ps) == 1)
357 counter++;
358 }
359 break;
360 }
361 }
362
363 /* print leading zeroes */
364 precision -= number_size;
365 while (precision-- > 0) {
366 if (printf_putchar('0', ps) == 1)
367 counter++;
368 }
369
370
371 /* print number itself */
372
373 if ((retval = printf_putstr(++ptr, ps)) > 0) {
374 counter += retval;
375 }
376
377 /* print ending spaces */
378
379 while (width-- > 0) {
380 if (printf_putchar(' ', ps) == 1)
381 counter++;
382 }
383
384 return counter;
385}
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 * - "ll" Signed or unsigned long long int.@n
434 *
435 *
436 * CONVERSION:@n
437 * - % Print percentile character itself.
438 *
439 * - c Print single character.
440 *
441 * - s Print zero terminated string. If a NULL value is passed as
442 * value, "(NULL)" is printed instead.
443 *
444 * - P, p Print value of a pointer. Void * value is expected and it is
445 * printed in hexadecimal notation with prefix (as with \%#X / \%#x
446 * for 32-bit or \%#X / \%#x for 64-bit long pointers).
447 *
448 * - b Print value as unsigned binary number. Prefix is not printed by
449 * default. (Nonstandard extension.)
450 *
451 * - o Print value as unsigned octal number. Prefix is not printed by
452 * default.
453 *
454 * - d, i Print signed decimal number. There is no difference between d
455 * and i conversion.
456 *
457 * - u Print unsigned decimal number.
458 *
459 * - X, x Print hexadecimal number with upper- or lower-case. Prefix is
460 * not printed by default.
461 *
462 * All other characters from fmt except the formatting directives are printed in
463 * verbatim.
464 *
465 * @param fmt Formatting NULL terminated string.
466 * @return Number of characters printed, negative value on failure.
467 */
468int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
469{
470 int i = 0; /* index of the currently processed char from fmt */
471 int j = 0; /* index to the first not printed nonformating character */
472 int end;
473 int counter; /* counter of printed characters */
474 int retval; /* used to store return values from called functions */
475 char c;
476 qualifier_t qualifier; /* type of argument */
477 int base; /* base in which a numeric parameter will be printed */
478 uint64_t number; /* argument value */
479 size_t size; /* byte size of integer parameter */
480 int width, precision;
481 uint64_t flags;
482
483 counter = 0;
484
485 while ((c = fmt[i])) {
486 /* control character */
487 if (c == '%') {
488 /* print common characters if any processed */
489 if (i > j) {
490 if ((retval = printf_putnchars(&fmt[j],
491 (size_t)(i - j), ps)) < 0) { /* error */
492 counter = -counter;
493 goto out;
494 }
495 counter += retval;
496 }
497
498 j = i;
499 /* parse modifiers */
500 flags = 0;
501 end = 0;
502
503 do {
504 ++i;
505 switch (c = fmt[i]) {
506 case '#':
507 flags |= __PRINTF_FLAG_PREFIX;
508 break;
509 case '-':
510 flags |= __PRINTF_FLAG_LEFTALIGNED;
511 break;
512 case '+':
513 flags |= __PRINTF_FLAG_SHOWPLUS;
514 break;
515 case ' ':
516 flags |= __PRINTF_FLAG_SPACESIGN;
517 break;
518 case '0':
519 flags |= __PRINTF_FLAG_ZEROPADDED;
520 break;
521 default:
522 end = 1;
523 };
524
525 } while (end == 0);
526
527 /* width & '*' operator */
528 width = 0;
529 if (isdigit(fmt[i])) {
530 while (isdigit(fmt[i])) {
531 width *= 10;
532 width += fmt[i++] - '0';
533 }
534 } else if (fmt[i] == '*') {
535 /* get width value from argument list */
536 i++;
537 width = (int) va_arg(ap, int);
538 if (width < 0) {
539 /* negative width sets '-' flag */
540 width *= -1;
541 flags |= __PRINTF_FLAG_LEFTALIGNED;
542 }
543 }
544
545 /* precision and '*' operator */
546 precision = 0;
547 if (fmt[i] == '.') {
548 ++i;
549 if (isdigit(fmt[i])) {
550 while (isdigit(fmt[i])) {
551 precision *= 10;
552 precision += fmt[i++] - '0';
553 }
554 } else if (fmt[i] == '*') {
555 /*
556 * Get precision value from the argument
557 * list.
558 */
559 i++;
560 precision = (int) va_arg(ap, int);
561 if (precision < 0) {
562 /* ignore negative precision */
563 precision = 0;
564 }
565 }
566 }
567
568 switch (fmt[i++]) {
569 /** @todo unimplemented qualifiers:
570 * t ptrdiff_t - ISO C 99
571 */
572 case 'h': /* char or short */
573 qualifier = PrintfQualifierShort;
574 if (fmt[i] == 'h') {
575 i++;
576 qualifier = PrintfQualifierByte;
577 }
578 break;
579 case 'l': /* long or long long*/
580 qualifier = PrintfQualifierLong;
581 if (fmt[i] == 'l') {
582 i++;
583 qualifier = PrintfQualifierLongLong;
584 }
585 break;
586 default:
587 /* default type */
588 qualifier = PrintfQualifierInt;
589 --i;
590 }
591
592 base = 10;
593
594 switch (c = fmt[i]) {
595
596 /*
597 * String and character conversions.
598 */
599 case 's':
600 if ((retval = print_string(va_arg(ap, char *),
601 width, precision, flags, ps)) < 0) {
602 counter = -counter;
603 goto out;
604 };
605
606 counter += retval;
607 j = i + 1;
608 goto next_char;
609 case 'c':
610 c = va_arg(ap, unsigned int);
611 retval = print_char(c, width, flags, ps);
612 if (retval < 0) {
613 counter = -counter;
614 goto out;
615 };
616
617 counter += retval;
618 j = i + 1;
619 goto next_char;
620
621 /*
622 * Integer values
623 */
624 case 'P': /* pointer */
625 flags |= __PRINTF_FLAG_BIGCHARS;
626 case 'p':
627 flags |= __PRINTF_FLAG_PREFIX;
628 base = 16;
629 qualifier = PrintfQualifierPointer;
630 break;
631 case 'b':
632 base = 2;
633 break;
634 case 'o':
635 base = 8;
636 break;
637 case 'd':
638 case 'i':
639 flags |= __PRINTF_FLAG_SIGNED;
640 case 'u':
641 break;
642 case 'X':
643 flags |= __PRINTF_FLAG_BIGCHARS;
644 case 'x':
645 base = 16;
646 break;
647 /* percentile itself */
648 case '%':
649 j = i;
650 goto next_char;
651 /*
652 * Bad formatting.
653 */
654 default:
655 /*
656 * Unknown format. Now, j is the index of '%'
657 * so we will print whole bad format sequence.
658 */
659 goto next_char;
660 }
661
662
663 /* Print integers */
664 /* print number */
665 switch (qualifier) {
666 case PrintfQualifierByte:
667 size = sizeof(unsigned char);
668 number = (uint64_t) va_arg(ap, unsigned int);
669 break;
670 case PrintfQualifierShort:
671 size = sizeof(unsigned short);
672 number = (uint64_t) va_arg(ap, unsigned int);
673 break;
674 case PrintfQualifierInt:
675 size = sizeof(unsigned int);
676 number = (uint64_t) va_arg(ap, unsigned int);
677 break;
678 case PrintfQualifierLong:
679 size = sizeof(unsigned long);
680 number = (uint64_t) va_arg(ap, unsigned long);
681 break;
682 case PrintfQualifierLongLong:
683 size = sizeof(unsigned long long);
684 number = (uint64_t) va_arg(ap, unsigned long long);
685 break;
686 case PrintfQualifierPointer:
687 size = sizeof(void *);
688 number = (uint64_t) (unsigned long) va_arg(ap, void *);
689 break;
690 default: /* Unknown qualifier */
691 counter = -counter;
692 goto out;
693 }
694
695 if (flags & __PRINTF_FLAG_SIGNED) {
696 if (number & (0x1 << (size * 8 - 1))) {
697 flags |= __PRINTF_FLAG_NEGATIVE;
698
699 if (size == sizeof(uint64_t)) {
700 number = -((int64_t) number);
701 } else {
702 number = ~number;
703 number &=
704 ~(0xFFFFFFFFFFFFFFFFll <<
705 (size * 8));
706 number++;
707 }
708 }
709 }
710
711 if ((retval = print_number(number, width, precision,
712 base, flags, ps)) < 0) {
713 counter = -counter;
714 goto out;
715 }
716
717 counter += retval;
718 j = i + 1;
719 }
720next_char:
721
722 ++i;
723 }
724
725 if (i > j) {
726 if ((retval = printf_putnchars(&fmt[j], (unative_t) (i - j),
727 ps)) < 0) { /* error */
728 counter = -counter;
729 goto out;
730
731 }
732 counter += retval;
733 }
734
735out:
736 return counter;
737}
738
739/** @}
740 */
Note: See TracBrowser for help on using the repository browser.