2 * Copyright (C) 2001-2004 Jakub Jermar
3 * Copyright (C) 2006 Josef Cejka
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
30 /** @addtogroup generic
35 * @brief Printing functions.
38 #include <printf/printf_core.h>
41 #include <synch/spinlock.h>
47 SPINLOCK_INITIALIZE(printflock
); /**< printf spinlock */
49 #define __PRINTF_FLAG_PREFIX 0x00000001 /**< show prefixes 0x or 0*/
50 #define __PRINTF_FLAG_SIGNED 0x00000002 /**< signed / unsigned number */
51 #define __PRINTF_FLAG_ZEROPADDED 0x00000004 /**< print leading zeroes */
52 #define __PRINTF_FLAG_LEFTALIGNED 0x00000010 /**< align to left */
53 #define __PRINTF_FLAG_SHOWPLUS 0x00000020 /**< always show + sign */
54 #define __PRINTF_FLAG_SPACESIGN 0x00000040 /**< print space instead of plus */
55 #define __PRINTF_FLAG_BIGCHARS 0x00000080 /**< show big characters */
56 #define __PRINTF_FLAG_NEGATIVE 0x00000100 /**< number has - sign */
58 #define PRINT_NUMBER_BUFFER_SIZE (64+5) /**< Buffer big enought for 64 bit number
59 * printed in base 2, sign, prefix and
60 * 0 to terminate string.. (last one is only for better testing
61 * end of buffer by zero-filling subroutine)*/
63 /** Enumeration of possible arguments types.
66 PrintfQualifierByte
= 0,
70 PrintfQualifierLongLong
,
71 PrintfQualifierNative
,
72 PrintfQualifierPointer
75 static char digits_small
[] = "0123456789abcdef"; /**< Small hexadecimal characters */
76 static char digits_big
[] = "0123456789ABCDEF"; /**< Big hexadecimal characters */
78 /** Checks c for a digit.
79 * @param c One character.
80 * @return nonzero if c is from interval '0 to '9'.
82 static inline int isdigit(int c
)
84 return ((c
>= '0' )&&( c
<= '9'));
87 /** Compute length of given zero terminated string.
88 * @param str Pointer to valid string.
89 * @return string length without trailing zero.
91 static unative_t
strlen(const char *str
)
93 unative_t counter
= 0;
95 while (str
[counter
] != 0) {
102 /** Print count chars from buffer without adding newline
103 * @param buf Buffer with size at least count bytes - NULL pointer NOT allowed!
105 * @param ps output method and its data
106 * @return number of printed characters
108 static int printf_putnchars(const char * buf
, size_t count
, struct printf_spec
*ps
)
110 return ps
->write((void *)buf
, count
, ps
->data
);
113 /** Print string without added newline
114 * @param str string to print
115 * @param ps write function specification and support data
116 * @return number of printed characters
118 static int printf_putstr(const char * str
, struct printf_spec
*ps
)
123 return printf_putnchars("(NULL)", 6, ps
);
128 return ps
->write((void *) str
, count
, ps
->data
);
131 /** Print one character to output
132 * @param c one character
133 * @param ps output method
134 * @return number of printed characters
136 static int printf_putchar(int c
, struct printf_spec
*ps
)
138 unsigned char ch
= c
;
140 return ps
->write((void *) &ch
, 1, ps
->data
);
143 /** Print one formatted character
144 * @param c character to print
147 * @return number of printed characters, negative value on fail
149 static int print_char(char c
, int width
, uint64_t flags
, struct printf_spec
*ps
)
153 if (!(flags
& __PRINTF_FLAG_LEFTALIGNED
)) {
154 while (--width
> 0) { /* one space is consumed by character itself hence predecrement */
155 if (printf_putchar(' ', ps
) > 0)
160 if (printf_putchar(c
, ps
) > 0)
163 while (--width
> 0) { /* one space is consumed by character itself hence predecrement */
164 if (printf_putchar(' ', ps
) > 0)
176 * @return number of printed characters or negative value on fail
179 static int print_string(char *s
, int width
, int precision
, uint64_t flags
, struct printf_spec
*ps
)
186 return printf_putstr("(NULL)", ps
);
191 /* print leading spaces */
198 if (!(flags
& __PRINTF_FLAG_LEFTALIGNED
)) {
199 while (width
-- > 0) {
200 if (printf_putchar(' ', ps
) == 1)
205 while (precision
> size
) {
207 if (printf_putchar(' ', ps
) == 1)
211 if ((retval
= printf_putnchars(s
, precision
, ps
)) < 0) {
216 while (width
-- > 0) {
217 if (printf_putchar(' ', ps
) == 1)
225 /** Print number in given base
227 * Print significant digits of a number in given
230 * @param num Number to print.
233 * @param base Base to print the number in (should
234 * be in range 2 .. 16).
235 * @param flags output modifiers
236 * @return number of written characters or EOF
239 static int print_number(uint64_t num
, int width
, int precision
, int base
, uint64_t flags
, struct printf_spec
*ps
)
241 char *digits
= digits_small
;
242 char d
[PRINT_NUMBER_BUFFER_SIZE
]; /* this is good enough even for base == 2, prefix and sign */
243 char *ptr
= &d
[PRINT_NUMBER_BUFFER_SIZE
- 1];
244 int size
= 0; /* size of number with all prefixes and signs */
245 int number_size
; /* size of plain number */
250 if (flags
& __PRINTF_FLAG_BIGCHARS
)
253 *ptr
-- = 0; /* Put zero at end of string */
260 *ptr
-- = digits
[num
% base
];
262 } while (num
/= base
);
267 /* Collect sum of all prefixes/signs/... to calculate padding and leading zeroes */
268 if (flags
& __PRINTF_FLAG_PREFIX
) {
270 case 2: /* Binary formating is not standard, but usefull */
283 if (flags
& __PRINTF_FLAG_SIGNED
) {
284 if (flags
& __PRINTF_FLAG_NEGATIVE
) {
287 } else if (flags
& __PRINTF_FLAG_SHOWPLUS
) {
290 } else if (flags
& __PRINTF_FLAG_SPACESIGN
) {
296 if (flags
& __PRINTF_FLAG_LEFTALIGNED
) {
297 flags
&= ~__PRINTF_FLAG_ZEROPADDED
;
300 /* if number is leftaligned or precision is specified then zeropadding is ignored */
301 if (flags
& __PRINTF_FLAG_ZEROPADDED
) {
302 if ((precision
== 0) && (width
> size
)) {
303 precision
= width
- size
+ number_size
;
307 /* print leading spaces */
308 if (number_size
> precision
) /* We must print whole number not only a part */
309 precision
= number_size
;
311 width
-= precision
+ size
- number_size
;
313 if (!(flags
& __PRINTF_FLAG_LEFTALIGNED
)) {
314 while (width
-- > 0) {
315 if (printf_putchar(' ', ps
) == 1)
323 if (printf_putchar(sgn
, ps
) == 1)
329 if (flags
& __PRINTF_FLAG_PREFIX
) {
331 case 2: /* Binary formating is not standard, but usefull */
332 if (printf_putchar('0', ps
) == 1)
334 if (flags
& __PRINTF_FLAG_BIGCHARS
) {
335 if (printf_putchar('B', ps
) == 1)
338 if (printf_putchar('b', ps
) == 1)
343 if (printf_putchar('o', ps
) == 1)
347 if (printf_putchar('0', ps
) == 1)
349 if (flags
& __PRINTF_FLAG_BIGCHARS
) {
350 if (printf_putchar('X', ps
) == 1)
353 if (printf_putchar('x', ps
) == 1)
360 /* print leading zeroes */
361 precision
-= number_size
;
362 while (precision
-- > 0) {
363 if (printf_putchar('0', ps
) == 1)
368 /* print number itself */
370 if ((retval
= printf_putstr(++ptr
, ps
)) > 0) {
374 /* print ending spaces */
376 while (width
-- > 0) {
377 if (printf_putchar(' ', ps
) == 1)
385 /** Print formatted string.
387 * Print string formatted according to the fmt parameter
388 * and variadic arguments. Each formatting directive
389 * must have the following form:
391 * \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
394 * - "#" Force to print prefix.
395 * For conversion \%o the prefix is 0, for \%x and \%X prefixes are 0x and 0X
396 * and for conversion \%b the prefix is 0b.
398 * - "-" Align to left.
400 * - "+" Print positive sign just as negative.
402 * - " " If the printed number is positive and "+" flag is not set, print space in
405 * - "0" Print 0 as padding instead of spaces. Zeroes are placed between sign and the
406 * rest of the number. This flag is ignored if "-" flag is specified.
409 * - Specify minimal width of printed argument. If it is bigger, width is ignored.
410 * If width is specified with a "*" character instead of number, width is taken from
411 * parameter list. And integer parameter is expected before parameter for processed
412 * conversion specification. If this value is negative its absolute value is taken
413 * and the "-" flag is set.
416 * - Value precision. For numbers it specifies minimum valid numbers.
417 * Smaller numbers are printed with leading zeroes. Bigger numbers are not affected.
418 * Strings with more than precision characters are cut off.
419 * Just as with width, an "*" can be used used instead of a number.
420 * An integer value is then expected in parameters. When both width and precision
421 * are specified using "*", the first parameter is used for width and the second one
425 * - "hh" Signed or unsigned char.@n
426 * - "h" Signed or usigned short.@n
427 * - "" Signed or usigned int (default value).@n
428 * - "l" Signed or usigned long int.@n
429 * - "ll" Signed or usigned long long int.@n
430 * - "z" unative_t (non-standard extension).@n
434 * - % Print percentile character itself.
436 * - c Print single character.
438 * - s Print zero terminated string. If a NULL value is passed as value, "(NULL)" is printed instead.
440 * - P, p Print value of a pointer. Void * value is expected and it is printed in hexadecimal notation with prefix
441 * (as with \%#X or \%#x for 32bit or \%#X / \%#x for 64bit long pointers).
443 * - b Print value as unsigned binary number. Prefix is not printed by default. (Nonstandard extension.)
445 * - o Print value as unsigned octal number. Prefix is not printed by default.
447 * - d,i Print signed decimal number. There is no difference between d and i conversion.
449 * - u Print unsigned decimal number.
451 * - X, x Print hexadecimal number with upper- or lower-case. Prefix is not printed by default.
453 * All other characters from fmt except the formatting directives
454 * are printed in verbatim.
456 * @param fmt Formatting NULL terminated string.
457 * @return Number of printed characters or negative value on failure.
459 int printf_core(const char *fmt
, struct printf_spec
*ps
, va_list ap
)
462 int i
= 0, j
= 0; /**< i is index of currently processed char from fmt, j is index to the first not printed nonformating character */
464 int counter
; /**< counter of printed characters */
465 int retval
; /**< used to store return values from called functions */
467 qualifier_t qualifier
; /* type of argument */
468 int base
; /**< base in which will be parameter (numbers only) printed */
469 uint64_t number
; /**< argument value */
470 size_t size
; /**< byte size of integer parameter */
471 int width
, precision
;
476 irqpri
= interrupts_disable();
477 spinlock_lock(&printflock
);
479 while ((c
= fmt
[i
])) {
480 /* control character */
482 /* print common characters if any processed */
484 if ((retval
= printf_putnchars(&fmt
[j
], (size_t)(i
- j
), ps
)) < 0) { /* error */
492 /* parse modifiers */
498 switch (c
= fmt
[i
]) {
499 case '#': flags
|= __PRINTF_FLAG_PREFIX
; break;
500 case '-': flags
|= __PRINTF_FLAG_LEFTALIGNED
; break;
501 case '+': flags
|= __PRINTF_FLAG_SHOWPLUS
; break;
502 case ' ': flags
|= __PRINTF_FLAG_SPACESIGN
; break;
503 case '0': flags
|= __PRINTF_FLAG_ZEROPADDED
; break;
509 /* width & '*' operator */
511 if (isdigit(fmt
[i
])) {
512 while (isdigit(fmt
[i
])) {
514 width
+= fmt
[i
++] - '0';
516 } else if (fmt
[i
] == '*') {
517 /* get width value from argument list*/
519 width
= (int)va_arg(ap
, int);
521 /* negative width means to set '-' flag */
523 flags
|= __PRINTF_FLAG_LEFTALIGNED
;
527 /* precision and '*' operator */
531 if (isdigit(fmt
[i
])) {
532 while (isdigit(fmt
[i
])) {
534 precision
+= fmt
[i
++] - '0';
536 } else if (fmt
[i
] == '*') {
537 /* get precision value from argument list*/
539 precision
= (int)va_arg(ap
, int);
541 /* negative precision means to ignore it */
548 /** TODO: unimplemented qualifiers:
549 * t ptrdiff_t - ISO C 99
551 case 'h': /* char or short */
552 qualifier
= PrintfQualifierShort
;
555 qualifier
= PrintfQualifierByte
;
558 case 'l': /* long or long long*/
559 qualifier
= PrintfQualifierLong
;
562 qualifier
= PrintfQualifierLongLong
;
565 case 'z': /* unative_t */
566 qualifier
= PrintfQualifierNative
;
569 qualifier
= PrintfQualifierInt
; /* default type */
575 switch (c
= fmt
[i
]) {
578 * String and character conversions.
581 if ((retval
= print_string(va_arg(ap
, char*), width
, precision
, flags
, ps
)) < 0) {
590 c
= va_arg(ap
, unsigned int);
591 if ((retval
= print_char(c
, width
, flags
, ps
)) < 0) {
603 case 'P': /* pointer */
604 flags
|= __PRINTF_FLAG_BIGCHARS
;
606 flags
|= __PRINTF_FLAG_PREFIX
;
608 qualifier
= PrintfQualifierPointer
;
618 flags
|= __PRINTF_FLAG_SIGNED
;
622 flags
|= __PRINTF_FLAG_BIGCHARS
;
626 /* percentile itself */
635 * now, j is index of '%' so we will
636 * print whole bad format sequence
645 case PrintfQualifierByte
:
646 size
= sizeof(unsigned char);
647 number
= (uint64_t)va_arg(ap
, unsigned int);
649 case PrintfQualifierShort
:
650 size
= sizeof(unsigned short);
651 number
= (uint64_t)va_arg(ap
, unsigned int);
653 case PrintfQualifierInt
:
654 size
= sizeof(unsigned int);
655 number
= (uint64_t)va_arg(ap
, unsigned int);
657 case PrintfQualifierLong
:
658 size
= sizeof(unsigned long);
659 number
= (uint64_t)va_arg(ap
, unsigned long);
661 case PrintfQualifierLongLong
:
662 size
= sizeof(unsigned long long);
663 number
= (uint64_t)va_arg(ap
, unsigned long long);
665 case PrintfQualifierPointer
:
666 size
= sizeof(void *);
667 number
= (uint64_t)(unsigned long)va_arg(ap
, void *);
669 case PrintfQualifierNative
:
670 size
= sizeof(unative_t
);
671 number
= (uint64_t)va_arg(ap
, unative_t
);
673 default: /* Unknown qualifier */
678 if (flags
& __PRINTF_FLAG_SIGNED
) {
679 if (number
& (0x1 << (size
*8 - 1))) {
680 flags
|= __PRINTF_FLAG_NEGATIVE
;
682 if (size
== sizeof(uint64_t)) {
683 number
= -((int64_t)number
);
686 number
&= (~((0xFFFFFFFFFFFFFFFFll
) << (size
* 8)));
692 if ((retval
= print_number(number
, width
, precision
, base
, flags
, ps
)) < 0) {
706 if ((retval
= printf_putnchars(&fmt
[j
], (unative_t
)(i
- j
), ps
)) < 0) { /* error */
715 spinlock_unlock(&printflock
);
716 interrupts_restore(irqpri
);