source: mainline/generic/src/printf/printf_core.c@ dd054bc2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since dd054bc2 was 4ddeace, checked in by Josef Cejka <malyzelenyhnus@…>, 20 years ago

Fixed printf return value.

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