Rather big indentation and formatting changes.
[helenos.git] / kernel / generic / src / printf / printf_core.c
bloba25421fbefc693ad36a2f8d7b640bbdd17845b61
1 /*
2 * Copyright (C) 2001-2004 Jakub Jermar
3 * Copyright (C) 2006 Josef Cejka
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
31 * @{
33 /**
34 * @file
35 * @brief Printing functions.
38 #include <printf/printf_core.h>
39 #include <putchar.h>
40 #include <print.h>
41 #include <synch/spinlock.h>
42 #include <arch/arg.h>
43 #include <arch/asm.h>
45 #include <arch.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.
65 typedef enum {
66 PrintfQualifierByte = 0,
67 PrintfQualifierShort,
68 PrintfQualifierInt,
69 PrintfQualifierLong,
70 PrintfQualifierLongLong,
71 PrintfQualifierNative,
72 PrintfQualifierPointer
73 } qualifier_t;
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) {
96 counter++;
99 return counter;
102 /** Print count chars from buffer without adding newline
103 * @param buf Buffer with size at least count bytes - NULL pointer NOT allowed!
104 * @param count
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)
120 size_t count;
122 if (str == NULL) {
123 return printf_putnchars("(NULL)", 6, ps);
126 count = strlen(str);
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
145 * @param width
146 * @param flags
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)
151 int counter = 0;
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)
156 ++counter;
160 if (printf_putchar(c, ps) > 0)
161 counter++;
163 while (--width > 0) { /* one space is consumed by character itself hence predecrement */
164 if (printf_putchar(' ', ps) > 0)
165 ++counter;
168 return ++counter;
171 /** Print one string
172 * @param s string
173 * @param width
174 * @param precision
175 * @param flags
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)
181 int counter = 0;
182 size_t size;
183 int retval;
185 if (s == NULL) {
186 return printf_putstr("(NULL)", ps);
189 size = strlen(s);
191 /* print leading spaces */
193 if (precision == 0)
194 precision = size;
196 width -= precision;
198 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
199 while (width-- > 0) {
200 if (printf_putchar(' ', ps) == 1)
201 counter++;
205 while (precision > size) {
206 precision--;
207 if (printf_putchar(' ', ps) == 1)
208 ++counter;
211 if ((retval = printf_putnchars(s, precision, ps)) < 0) {
212 return -counter;
214 counter += retval;
216 while (width-- > 0) {
217 if (printf_putchar(' ', ps) == 1)
218 ++counter;
221 return counter;
225 /** Print number in given base
227 * Print significant digits of a number in given
228 * base.
230 * @param num Number to print.
231 * @param width
232 * @param precision
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 */
246 char sgn;
247 int retval;
248 int counter = 0;
250 if (flags & __PRINTF_FLAG_BIGCHARS)
251 digits = digits_big;
253 *ptr-- = 0; /* Put zero at end of string */
255 if (num == 0) {
256 *ptr-- = '0';
257 size++;
258 } else {
259 do {
260 *ptr-- = digits[num % base];
261 size++;
262 } while (num /= base);
265 number_size = size;
267 /* Collect sum of all prefixes/signs/... to calculate padding and leading zeroes */
268 if (flags & __PRINTF_FLAG_PREFIX) {
269 switch(base) {
270 case 2: /* Binary formating is not standard, but usefull */
271 size += 2;
272 break;
273 case 8:
274 size++;
275 break;
276 case 16:
277 size += 2;
278 break;
282 sgn = 0;
283 if (flags & __PRINTF_FLAG_SIGNED) {
284 if (flags & __PRINTF_FLAG_NEGATIVE) {
285 sgn = '-';
286 size++;
287 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
288 sgn = '+';
289 size++;
290 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
291 sgn = ' ';
292 size++;
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)
316 counter++;
321 /* print sign */
322 if (sgn) {
323 if (printf_putchar(sgn, ps) == 1)
324 counter++;
327 /* print prefix */
329 if (flags & __PRINTF_FLAG_PREFIX) {
330 switch(base) {
331 case 2: /* Binary formating is not standard, but usefull */
332 if (printf_putchar('0', ps) == 1)
333 counter++;
334 if (flags & __PRINTF_FLAG_BIGCHARS) {
335 if (printf_putchar('B', ps) == 1)
336 counter++;
337 } else {
338 if (printf_putchar('b', ps) == 1)
339 counter++;
341 break;
342 case 8:
343 if (printf_putchar('o', ps) == 1)
344 counter++;
345 break;
346 case 16:
347 if (printf_putchar('0', ps) == 1)
348 counter++;
349 if (flags & __PRINTF_FLAG_BIGCHARS) {
350 if (printf_putchar('X', ps) == 1)
351 counter++;
352 } else {
353 if (printf_putchar('x', ps) == 1)
354 counter++;
356 break;
360 /* print leading zeroes */
361 precision -= number_size;
362 while (precision-- > 0) {
363 if (printf_putchar('0', ps) == 1)
364 counter++;
368 /* print number itself */
370 if ((retval = printf_putstr(++ptr, ps)) > 0) {
371 counter += retval;
374 /* print ending spaces */
376 while (width-- > 0) {
377 if (printf_putchar(' ', ps) == 1)
378 counter++;
381 return counter;
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
393 * FLAGS:@n
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
403 * place of sign.
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.
408 * WIDTH:@n
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.
415 * PRECISION:@n
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
422 * for precision.
424 * TYPE:@n
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
433 * CONVERSION:@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)
461 int irqpri;
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 */
463 int end;
464 int counter; /**< counter of printed characters */
465 int retval; /**< used to store return values from called functions */
466 char c;
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;
472 uint64_t flags;
474 counter = 0;
476 irqpri = interrupts_disable();
477 spinlock_lock(&printflock);
479 while ((c = fmt[i])) {
480 /* control character */
481 if (c == '%' ) {
482 /* print common characters if any processed */
483 if (i > j) {
484 if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */
485 counter = -counter;
486 goto out;
488 counter += retval;
491 j = i;
492 /* parse modifiers */
493 flags = 0;
494 end = 0;
496 do {
497 ++i;
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;
504 default: end = 1;
507 } while (end == 0);
509 /* width & '*' operator */
510 width = 0;
511 if (isdigit(fmt[i])) {
512 while (isdigit(fmt[i])) {
513 width *= 10;
514 width += fmt[i++] - '0';
516 } else if (fmt[i] == '*') {
517 /* get width value from argument list*/
518 i++;
519 width = (int)va_arg(ap, int);
520 if (width < 0) {
521 /* negative width means to set '-' flag */
522 width *= -1;
523 flags |= __PRINTF_FLAG_LEFTALIGNED;
527 /* precision and '*' operator */
528 precision = 0;
529 if (fmt[i] == '.') {
530 ++i;
531 if (isdigit(fmt[i])) {
532 while (isdigit(fmt[i])) {
533 precision *= 10;
534 precision += fmt[i++] - '0';
536 } else if (fmt[i] == '*') {
537 /* get precision value from argument list*/
538 i++;
539 precision = (int)va_arg(ap, int);
540 if (precision < 0) {
541 /* negative precision means to ignore it */
542 precision = 0;
547 switch (fmt[i++]) {
548 /** TODO: unimplemented qualifiers:
549 * t ptrdiff_t - ISO C 99
551 case 'h': /* char or short */
552 qualifier = PrintfQualifierShort;
553 if (fmt[i] == 'h') {
554 i++;
555 qualifier = PrintfQualifierByte;
557 break;
558 case 'l': /* long or long long*/
559 qualifier = PrintfQualifierLong;
560 if (fmt[i] == 'l') {
561 i++;
562 qualifier = PrintfQualifierLongLong;
564 break;
565 case 'z': /* unative_t */
566 qualifier = PrintfQualifierNative;
567 break;
568 default:
569 qualifier = PrintfQualifierInt; /* default type */
570 --i;
573 base = 10;
575 switch (c = fmt[i]) {
578 * String and character conversions.
580 case 's':
581 if ((retval = print_string(va_arg(ap, char*), width, precision, flags, ps)) < 0) {
582 counter = -counter;
583 goto out;
586 counter += retval;
587 j = i + 1;
588 goto next_char;
589 case 'c':
590 c = va_arg(ap, unsigned int);
591 if ((retval = print_char(c, width, flags, ps)) < 0) {
592 counter = -counter;
593 goto out;
596 counter += retval;
597 j = i + 1;
598 goto next_char;
601 * Integer values
603 case 'P': /* pointer */
604 flags |= __PRINTF_FLAG_BIGCHARS;
605 case 'p':
606 flags |= __PRINTF_FLAG_PREFIX;
607 base = 16;
608 qualifier = PrintfQualifierPointer;
609 break;
610 case 'b':
611 base = 2;
612 break;
613 case 'o':
614 base = 8;
615 break;
616 case 'd':
617 case 'i':
618 flags |= __PRINTF_FLAG_SIGNED;
619 case 'u':
620 break;
621 case 'X':
622 flags |= __PRINTF_FLAG_BIGCHARS;
623 case 'x':
624 base = 16;
625 break;
626 /* percentile itself */
627 case '%':
628 j = i;
629 goto next_char;
631 * Bad formatting.
633 default:
634 /* Unknown format
635 * now, j is index of '%' so we will
636 * print whole bad format sequence
638 goto next_char;
642 /* Print integers */
643 /* print number */
644 switch (qualifier) {
645 case PrintfQualifierByte:
646 size = sizeof(unsigned char);
647 number = (uint64_t)va_arg(ap, unsigned int);
648 break;
649 case PrintfQualifierShort:
650 size = sizeof(unsigned short);
651 number = (uint64_t)va_arg(ap, unsigned int);
652 break;
653 case PrintfQualifierInt:
654 size = sizeof(unsigned int);
655 number = (uint64_t)va_arg(ap, unsigned int);
656 break;
657 case PrintfQualifierLong:
658 size = sizeof(unsigned long);
659 number = (uint64_t)va_arg(ap, unsigned long);
660 break;
661 case PrintfQualifierLongLong:
662 size = sizeof(unsigned long long);
663 number = (uint64_t)va_arg(ap, unsigned long long);
664 break;
665 case PrintfQualifierPointer:
666 size = sizeof(void *);
667 number = (uint64_t)(unsigned long)va_arg(ap, void *);
668 break;
669 case PrintfQualifierNative:
670 size = sizeof(unative_t);
671 number = (uint64_t)va_arg(ap, unative_t);
672 break;
673 default: /* Unknown qualifier */
674 counter = -counter;
675 goto out;
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);
684 } else {
685 number = ~number;
686 number &= (~((0xFFFFFFFFFFFFFFFFll) << (size * 8)));
687 number++;
692 if ((retval = print_number(number, width, precision, base, flags, ps)) < 0) {
693 counter = -counter;
694 goto out;
697 counter += retval;
698 j = i + 1;
700 next_char:
702 ++i;
705 if (i > j) {
706 if ((retval = printf_putnchars(&fmt[j], (unative_t)(i - j), ps)) < 0) { /* error */
707 counter = -counter;
708 goto out;
711 counter += retval;
714 out:
715 spinlock_unlock(&printflock);
716 interrupts_restore(irqpri);
718 return counter;
721 /** @}