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

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

use spinlock only on console output, not other print functions
cleanup

  • Property mode set to 100644
File size: 17.8 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 <arch.h>
43
44#define __PRINTF_FLAG_PREFIX 0x00000001 /**< show prefixes 0x or 0*/
45#define __PRINTF_FLAG_SIGNED 0x00000002 /**< signed / unsigned number */
46#define __PRINTF_FLAG_ZEROPADDED 0x00000004 /**< print leading zeroes */
47#define __PRINTF_FLAG_LEFTALIGNED 0x00000010 /**< align to left */
48#define __PRINTF_FLAG_SHOWPLUS 0x00000020 /**< always show + sign */
49#define __PRINTF_FLAG_SPACESIGN 0x00000040 /**< print space instead of plus */
50#define __PRINTF_FLAG_BIGCHARS 0x00000080 /**< show big characters */
51#define __PRINTF_FLAG_NEGATIVE 0x00000100 /**< number has - sign */
52
53#define PRINT_NUMBER_BUFFER_SIZE (64+5) /**< Buffer big enought for 64 bit number
54 * printed in base 2, sign, prefix and
55 * 0 to terminate string.. (last one is only for better testing
56 * end of buffer by zero-filling subroutine)*/
57
58/** Enumeration of possible arguments types.
59 */
60typedef enum {
61 PrintfQualifierByte = 0,
62 PrintfQualifierShort,
63 PrintfQualifierInt,
64 PrintfQualifierLong,
65 PrintfQualifierLongLong,
66 PrintfQualifierNative,
67 PrintfQualifierPointer
68} qualifier_t;
69
70static char digits_small[] = "0123456789abcdef"; /**< Small hexadecimal characters */
71static char digits_big[] = "0123456789ABCDEF"; /**< Big hexadecimal characters */
72
73/** Checks c for a digit.
74 * @param c One character.
75 * @return nonzero if c is from interval '0 to '9'.
76 */
77static inline int isdigit(int c)
78{
79 return ((c >= '0' )&&( c <= '9'));
80}
81
82/** Compute length of given zero terminated string.
83 * @param str Pointer to valid string.
84 * @return string length without trailing zero.
85 */
86static unative_t strlen(const char *str)
87{
88 unative_t counter = 0;
89
90 while (str[counter] != 0) {
91 counter++;
92 }
93
94 return counter;
95}
96
97/** Print count chars from buffer without adding newline
98 * @param buf Buffer with size at least count bytes - NULL pointer NOT allowed!
99 * @param count
100 * @param ps output method and its data
101 * @return number of printed characters
102 */
103static int printf_putnchars(const char * buf, size_t count, struct printf_spec *ps)
104{
105 return ps->write((void *)buf, count, ps->data);
106}
107
108/** Print string without added newline
109 * @param str string to print
110 * @param ps write function specification and support data
111 * @return number of printed characters
112 */
113static int printf_putstr(const char * str, struct printf_spec *ps)
114{
115 size_t count;
116
117 if (str == NULL) {
118 return printf_putnchars("(NULL)", 6, ps);
119 }
120
121 count = strlen(str);
122
123 return ps->write((void *) str, count, ps->data);
124}
125
126/** Print one character to output
127 * @param c one character
128 * @param ps output method
129 * @return number of printed characters
130 */
131static int printf_putchar(int c, struct printf_spec *ps)
132{
133 unsigned char ch = c;
134
135 return ps->write((void *) &ch, 1, ps->data);
136}
137
138/** Print one formatted character
139 * @param c character to print
140 * @param width
141 * @param flags
142 * @return number of printed characters, negative value on fail
143 */
144static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps)
145{
146 int counter = 0;
147
148 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
149 while (--width > 0) { /* one space is consumed by character itself hence predecrement */
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) { /* one space is consumed by character itself hence predecrement */
159 if (printf_putchar(' ', ps) > 0)
160 ++counter;
161 }
162
163 return ++counter;
164}
165
166/** Print one string
167 * @param s string
168 * @param width
169 * @param precision
170 * @param flags
171 * @return number of printed characters or negative value on fail
172 */
173
174static int print_string(char *s, int width, int precision, uint64_t flags, struct printf_spec *ps)
175{
176 int counter = 0;
177 size_t size;
178 int retval;
179
180 if (s == NULL) {
181 return printf_putstr("(NULL)", ps);
182 }
183
184 size = strlen(s);
185
186 /* print leading spaces */
187
188 if (precision == 0)
189 precision = size;
190
191 width -= precision;
192
193 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
194 while (width-- > 0) {
195 if (printf_putchar(' ', ps) == 1)
196 counter++;
197 }
198 }
199
200 while (precision > size) {
201 precision--;
202 if (printf_putchar(' ', ps) == 1)
203 ++counter;
204 }
205
206 if ((retval = printf_putnchars(s, 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 number in given base
221 *
222 * Print significant digits of a number in given
223 * base.
224 *
225 * @param num Number to print.
226 * @param width
227 * @param precision
228 * @param base Base to print the number in (should
229 * be in range 2 .. 16).
230 * @param flags output modifiers
231 * @return number of written characters or EOF
232 *
233 */
234static int print_number(uint64_t num, int width, int precision, int base , uint64_t flags, struct printf_spec *ps)
235{
236 char *digits = digits_small;
237 char d[PRINT_NUMBER_BUFFER_SIZE]; /* this is good enough even for base == 2, prefix and sign */
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 /* Collect sum of all prefixes/signs/... to calculate padding and leading zeroes */
263 if (flags & __PRINTF_FLAG_PREFIX) {
264 switch(base) {
265 case 2: /* Binary formating is not standard, but usefull */
266 size += 2;
267 break;
268 case 8:
269 size++;
270 break;
271 case 16:
272 size += 2;
273 break;
274 }
275 }
276
277 sgn = 0;
278 if (flags & __PRINTF_FLAG_SIGNED) {
279 if (flags & __PRINTF_FLAG_NEGATIVE) {
280 sgn = '-';
281 size++;
282 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
283 sgn = '+';
284 size++;
285 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
286 sgn = ' ';
287 size++;
288 }
289 }
290
291 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
292 flags &= ~__PRINTF_FLAG_ZEROPADDED;
293 }
294
295 /* if number is leftaligned or precision is specified then zeropadding is ignored */
296 if (flags & __PRINTF_FLAG_ZEROPADDED) {
297 if ((precision == 0) && (width > size)) {
298 precision = width - size + number_size;
299 }
300 }
301
302 /* print leading spaces */
303 if (number_size > precision) /* We must print whole number not only a part */
304 precision = number_size;
305
306 width -= precision + size - number_size;
307
308 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
309 while (width-- > 0) {
310 if (printf_putchar(' ', ps) == 1)
311 counter++;
312 }
313 }
314
315
316 /* print sign */
317 if (sgn) {
318 if (printf_putchar(sgn, ps) == 1)
319 counter++;
320 }
321
322 /* print prefix */
323
324 if (flags & __PRINTF_FLAG_PREFIX) {
325 switch(base) {
326 case 2: /* Binary formating is not standard, but usefull */
327 if (printf_putchar('0', ps) == 1)
328 counter++;
329 if (flags & __PRINTF_FLAG_BIGCHARS) {
330 if (printf_putchar('B', ps) == 1)
331 counter++;
332 } else {
333 if (printf_putchar('b', ps) == 1)
334 counter++;
335 }
336 break;
337 case 8:
338 if (printf_putchar('o', ps) == 1)
339 counter++;
340 break;
341 case 16:
342 if (printf_putchar('0', ps) == 1)
343 counter++;
344 if (flags & __PRINTF_FLAG_BIGCHARS) {
345 if (printf_putchar('X', ps) == 1)
346 counter++;
347 } else {
348 if (printf_putchar('x', ps) == 1)
349 counter++;
350 }
351 break;
352 }
353 }
354
355 /* print leading zeroes */
356 precision -= number_size;
357 while (precision-- > 0) {
358 if (printf_putchar('0', ps) == 1)
359 counter++;
360 }
361
362
363 /* print number itself */
364
365 if ((retval = printf_putstr(++ptr, ps)) > 0) {
366 counter += retval;
367 }
368
369 /* print ending spaces */
370
371 while (width-- > 0) {
372 if (printf_putchar(' ', ps) == 1)
373 counter++;
374 }
375
376 return counter;
377}
378
379
380/** Print formatted string.
381 *
382 * Print string formatted according to the fmt parameter
383 * and variadic arguments. Each formatting directive
384 * must have the following form:
385 *
386 * \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
387 *
388 * FLAGS:@n
389 * - "#" Force to print prefix.
390 * For conversion \%o the prefix is 0, for \%x and \%X prefixes are 0x and 0X
391 * and for conversion \%b the prefix is 0b.
392 *
393 * - "-" Align to left.
394 *
395 * - "+" Print positive sign just as negative.
396 *
397 * - " " If the printed number is positive and "+" flag is not set, print space in
398 * place of sign.
399 *
400 * - "0" Print 0 as padding instead of spaces. Zeroes are placed between sign and the
401 * rest of the number. This flag is ignored if "-" flag is specified.
402 *
403 * WIDTH:@n
404 * - Specify minimal width of printed argument. If it is bigger, width is ignored.
405 * If width is specified with a "*" character instead of number, width is taken from
406 * parameter list. And integer parameter is expected before parameter for processed
407 * conversion specification. If this value is negative its absolute value is taken
408 * and the "-" flag is set.
409 *
410 * PRECISION:@n
411 * - Value precision. For numbers it specifies minimum valid numbers.
412 * Smaller numbers are printed with leading zeroes. Bigger numbers are not affected.
413 * Strings with more than precision characters are cut off.
414 * Just as with width, an "*" can be used used instead of a number.
415 * An integer value is then expected in parameters. When both width and precision
416 * are specified using "*", the first parameter is used for width and the second one
417 * for precision.
418 *
419 * TYPE:@n
420 * - "hh" Signed or unsigned char.@n
421 * - "h" Signed or usigned short.@n
422 * - "" Signed or usigned int (default value).@n
423 * - "l" Signed or usigned long int.@n
424 * - "ll" Signed or usigned long long int.@n
425 * - "z" unative_t (non-standard extension).@n
426 *
427 *
428 * CONVERSION:@n
429 * - % Print percentile character itself.
430 *
431 * - c Print single character.
432 *
433 * - s Print zero terminated string. If a NULL value is passed as value, "(NULL)" is printed instead.
434 *
435 * - P, p Print value of a pointer. Void * value is expected and it is printed in hexadecimal notation with prefix
436 * (as with \%#X or \%#x for 32bit or \%#X / \%#x for 64bit long pointers).
437 *
438 * - b Print value as unsigned binary number. Prefix is not printed by default. (Nonstandard extension.)
439 *
440 * - o Print value as unsigned octal number. Prefix is not printed by default.
441 *
442 * - d,i Print signed decimal number. There is no difference between d and i conversion.
443 *
444 * - u Print unsigned decimal number.
445 *
446 * - X, x Print hexadecimal number with upper- or lower-case. Prefix is not printed by default.
447 *
448 * All other characters from fmt except the formatting directives
449 * are printed in verbatim.
450 *
451 * @param fmt Formatting NULL terminated string.
452 * @return Number of printed characters or negative value on failure.
453 */
454int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
455{
456 int i = 0, j = 0; /**< i is index of currently processed char from fmt, j is index to the first not printed nonformating character */
457 int end;
458 int counter; /**< counter of printed characters */
459 int retval; /**< used to store return values from called functions */
460 char c;
461 qualifier_t qualifier; /* type of argument */
462 int base; /**< base in which will be parameter (numbers only) printed */
463 uint64_t number; /**< argument value */
464 size_t size; /**< byte size of integer parameter */
465 int width, precision;
466 uint64_t flags;
467
468 counter = 0;
469
470 while ((c = fmt[i])) {
471 /* control character */
472 if (c == '%' ) {
473 /* print common characters if any processed */
474 if (i > j) {
475 if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */
476 counter = -counter;
477 goto out;
478 }
479 counter += retval;
480 }
481
482 j = i;
483 /* parse modifiers */
484 flags = 0;
485 end = 0;
486
487 do {
488 ++i;
489 switch (c = fmt[i]) {
490 case '#': flags |= __PRINTF_FLAG_PREFIX; break;
491 case '-': flags |= __PRINTF_FLAG_LEFTALIGNED; break;
492 case '+': flags |= __PRINTF_FLAG_SHOWPLUS; break;
493 case ' ': flags |= __PRINTF_FLAG_SPACESIGN; break;
494 case '0': flags |= __PRINTF_FLAG_ZEROPADDED; break;
495 default: end = 1;
496 };
497
498 } while (end == 0);
499
500 /* width & '*' operator */
501 width = 0;
502 if (isdigit(fmt[i])) {
503 while (isdigit(fmt[i])) {
504 width *= 10;
505 width += fmt[i++] - '0';
506 }
507 } else if (fmt[i] == '*') {
508 /* get width value from argument list*/
509 i++;
510 width = (int)va_arg(ap, int);
511 if (width < 0) {
512 /* negative width means to set '-' flag */
513 width *= -1;
514 flags |= __PRINTF_FLAG_LEFTALIGNED;
515 }
516 }
517
518 /* precision and '*' operator */
519 precision = 0;
520 if (fmt[i] == '.') {
521 ++i;
522 if (isdigit(fmt[i])) {
523 while (isdigit(fmt[i])) {
524 precision *= 10;
525 precision += fmt[i++] - '0';
526 }
527 } else if (fmt[i] == '*') {
528 /* get precision value from argument list*/
529 i++;
530 precision = (int)va_arg(ap, int);
531 if (precision < 0) {
532 /* negative precision means to ignore it */
533 precision = 0;
534 }
535 }
536 }
537
538 switch (fmt[i++]) {
539 /** TODO: unimplemented qualifiers:
540 * t ptrdiff_t - ISO C 99
541 */
542 case 'h': /* char or short */
543 qualifier = PrintfQualifierShort;
544 if (fmt[i] == 'h') {
545 i++;
546 qualifier = PrintfQualifierByte;
547 }
548 break;
549 case 'l': /* long or long long*/
550 qualifier = PrintfQualifierLong;
551 if (fmt[i] == 'l') {
552 i++;
553 qualifier = PrintfQualifierLongLong;
554 }
555 break;
556 case 'z': /* unative_t */
557 qualifier = PrintfQualifierNative;
558 break;
559 default:
560 qualifier = PrintfQualifierInt; /* default type */
561 --i;
562 }
563
564 base = 10;
565
566 switch (c = fmt[i]) {
567
568 /*
569 * String and character conversions.
570 */
571 case 's':
572 if ((retval = print_string(va_arg(ap, char*), width, precision, flags, ps)) < 0) {
573 counter = -counter;
574 goto out;
575 };
576
577 counter += retval;
578 j = i + 1;
579 goto next_char;
580 case 'c':
581 c = va_arg(ap, unsigned int);
582 if ((retval = print_char(c, width, flags, ps)) < 0) {
583 counter = -counter;
584 goto out;
585 };
586
587 counter += retval;
588 j = i + 1;
589 goto next_char;
590
591 /*
592 * Integer values
593 */
594 case 'P': /* pointer */
595 flags |= __PRINTF_FLAG_BIGCHARS;
596 case 'p':
597 flags |= __PRINTF_FLAG_PREFIX;
598 base = 16;
599 qualifier = PrintfQualifierPointer;
600 break;
601 case 'b':
602 base = 2;
603 break;
604 case 'o':
605 base = 8;
606 break;
607 case 'd':
608 case 'i':
609 flags |= __PRINTF_FLAG_SIGNED;
610 case 'u':
611 break;
612 case 'X':
613 flags |= __PRINTF_FLAG_BIGCHARS;
614 case 'x':
615 base = 16;
616 break;
617 /* percentile itself */
618 case '%':
619 j = i;
620 goto next_char;
621 /*
622 * Bad formatting.
623 */
624 default:
625 /* Unknown format
626 * now, j is index of '%' so we will
627 * print whole bad format sequence
628 */
629 goto next_char;
630 }
631
632
633 /* Print integers */
634 /* print number */
635 switch (qualifier) {
636 case PrintfQualifierByte:
637 size = sizeof(unsigned char);
638 number = (uint64_t)va_arg(ap, unsigned int);
639 break;
640 case PrintfQualifierShort:
641 size = sizeof(unsigned short);
642 number = (uint64_t)va_arg(ap, unsigned int);
643 break;
644 case PrintfQualifierInt:
645 size = sizeof(unsigned int);
646 number = (uint64_t)va_arg(ap, unsigned int);
647 break;
648 case PrintfQualifierLong:
649 size = sizeof(unsigned long);
650 number = (uint64_t)va_arg(ap, unsigned long);
651 break;
652 case PrintfQualifierLongLong:
653 size = sizeof(unsigned long long);
654 number = (uint64_t)va_arg(ap, unsigned long long);
655 break;
656 case PrintfQualifierPointer:
657 size = sizeof(void *);
658 number = (uint64_t)(unsigned long)va_arg(ap, void *);
659 break;
660 case PrintfQualifierNative:
661 size = sizeof(unative_t);
662 number = (uint64_t)va_arg(ap, unative_t);
663 break;
664 default: /* Unknown qualifier */
665 counter = -counter;
666 goto out;
667 }
668
669 if (flags & __PRINTF_FLAG_SIGNED) {
670 if (number & (0x1 << (size*8 - 1))) {
671 flags |= __PRINTF_FLAG_NEGATIVE;
672
673 if (size == sizeof(uint64_t)) {
674 number = -((int64_t)number);
675 } else {
676 number = ~number;
677 number &= (~((0xFFFFFFFFFFFFFFFFll) << (size * 8)));
678 number++;
679 }
680 }
681 }
682
683 if ((retval = print_number(number, width, precision, base, flags, ps)) < 0) {
684 counter = -counter;
685 goto out;
686 };
687
688 counter += retval;
689 j = i + 1;
690 }
691next_char:
692
693 ++i;
694 }
695
696 if (i > j) {
697 if ((retval = printf_putnchars(&fmt[j], (unative_t)(i - j), ps)) < 0) { /* error */
698 counter = -counter;
699 goto out;
700
701 }
702 counter += retval;
703 }
704
705out:
706
707 return counter;
708}
709
710/** @}
711 */
Note: See TracBrowser for help on using the repository browser.