source: mainline/libc/generic/io/printf_core.c@ 0bc36ba

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

Comments update.

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