source: mainline/libc/generic/io/printf_core.c@ f1249e1

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

Printf return value fix ported from kernel to uspace.

  • Property mode set to 100644
File size: 17.5 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/* For serialization */
41#include <async.h>
42
43#define __PRINTF_FLAG_PREFIX 0x00000001 /**< show prefixes 0x or 0*/
44#define __PRINTF_FLAG_SIGNED 0x00000002 /**< signed / unsigned number */
45#define __PRINTF_FLAG_ZEROPADDED 0x00000004 /**< print leading zeroes */
46#define __PRINTF_FLAG_LEFTALIGNED 0x00000010 /**< align to left */
47#define __PRINTF_FLAG_SHOWPLUS 0x00000020 /**< always show + sign */
48#define __PRINTF_FLAG_SPACESIGN 0x00000040 /**< print space instead of plus */
49#define __PRINTF_FLAG_BIGCHARS 0x00000080 /**< show big characters */
50#define __PRINTF_FLAG_NEGATIVE 0x00000100 /**< number has - sign */
51
52#define PRINT_NUMBER_BUFFER_SIZE (64+5) /**< Buffer big enought for 64 bit number
53 * printed in base 2, sign, prefix and
54 * 0 to terminate string.. (last one is only for better testing
55 * end of buffer by zero-filling subroutine)
56 */
57/** Enumeration of possible arguments types.
58 */
59typedef enum {
60 PrintfQualifierByte = 0,
61 PrintfQualifierShort,
62 PrintfQualifierInt,
63 PrintfQualifierLong,
64 PrintfQualifierLongLong,
65 PrintfQualifierSizeT,
66 PrintfQualifierPointer
67} qualifier_t;
68
69static char digits_small[] = "0123456789abcdef"; /**< Small hexadecimal characters */
70static char digits_big[] = "0123456789ABCDEF"; /**< Big hexadecimal characters */
71
72/** Print count chars from buffer without adding newline
73 * @param buf Buffer with size at least count bytes - NULL pointer NOT allowed!
74 * @param count
75 * @param ps output method and its data
76 * @return number of printed characters
77 */
78static int printf_putnchars(const char * buf, size_t count, struct printf_spec *ps)
79{
80 return ps->write((void *)buf, count, ps->data);
81}
82
83/** Print string without added newline
84 * @param str string to print
85 * @param ps write function specification and support data
86 * @return number of printed characters
87 */
88static int printf_putstr(const char * str, struct printf_spec *ps)
89{
90 size_t count;
91
92 if (str == NULL) {
93 return printf_putnchars("(NULL)", 6, ps);
94 }
95
96 for (count = 0; str[count] != 0; count++);
97
98 if (ps->write((void *) str, count, ps->data) == count) {
99 return 0;
100 }
101
102 return EOF;
103}
104
105/** Print one character to output
106 * @param c one character
107 * @param ps output method
108 * @return number of printed characters
109 */
110static int printf_putchar(int c, struct printf_spec *ps)
111{
112 unsigned char ch = c;
113
114 return ps->write((void *) &ch, 1, ps->data);
115}
116
117/** Print one formatted character
118 * @param c character to print
119 * @param width
120 * @param flags
121 * @return number of printed characters
122 */
123static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps)
124{
125 int counter = 0;
126
127 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
128 while (--width > 0) { /* one space is consumed by character itself hence predecrement */
129 if (printf_putchar(' ', ps) > 0)
130 ++counter;
131 }
132 }
133
134 if (printf_putchar(c, ps) > 0)
135 counter++;
136
137 while (--width > 0) { /* one space is consumed by character itself hence predecrement */
138 if (printf_putchar(' ', ps) > 0)
139 ++counter;
140 }
141
142 return ++counter;
143}
144
145/** Print one string
146 * @param s string
147 * @param width
148 * @param precision
149 * @param flags
150 * @return number of printed characters
151 */
152
153static int print_string(char *s, int width, int precision, uint64_t flags, struct printf_spec *ps)
154{
155 int counter = 0;
156 size_t size;
157 int retval;
158
159 if (s == NULL) {
160 return printf_putstr("(NULL)", ps);
161 }
162
163 size = strlen(s);
164
165 /* print leading spaces */
166
167 if (precision == 0)
168 precision = size;
169
170 width -= precision;
171
172 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
173 while (width-- > 0) {
174 if (printf_putchar(' ', ps) == 1)
175 counter++;
176 }
177 }
178
179 while (precision > size) {
180 precision--;
181 if (printf_putchar(' ', ps) == 1)
182 ++counter;
183 }
184
185 if ((retval = printf_putnchars(s, precision, ps)) < 0) {
186 return -counter;
187 }
188
189 counter += retval;
190
191 while (width-- > 0) {
192 if (printf_putchar(' ', ps) == 1)
193 ++counter;
194 }
195
196 return counter;
197}
198
199
200/** Print number in given base
201 *
202 * Print significant digits of a number in given
203 * base.
204 *
205 * @param num Number to print.
206 * @param width
207 * @param precision
208 * @param base Base to print the number in (should
209 * be in range 2 .. 16).
210 * @param flags output modifiers
211 * @return number of printed characters
212 *
213 */
214static int print_number(uint64_t num, int width, int precision, int base , uint64_t flags, struct printf_spec *ps)
215{
216 char *digits = digits_small;
217 char d[PRINT_NUMBER_BUFFER_SIZE]; /* this is good enough even for base == 2, prefix and sign */
218 char *ptr = &d[PRINT_NUMBER_BUFFER_SIZE - 1];
219 int size = 0; /* size of number with all prefixes and signs */
220 int number_size; /* size of plain number */
221 char sgn;
222 int retval;
223 int counter = 0;
224
225 if (flags & __PRINTF_FLAG_BIGCHARS)
226 digits = digits_big;
227
228 *ptr-- = 0; /* Put zero at end of string */
229
230 if (num == 0) {
231 *ptr-- = '0';
232 size++;
233 } else {
234 do {
235 *ptr-- = digits[num % base];
236 size++;
237 } while (num /= base);
238 }
239
240 number_size = size;
241
242 /* Collect sum of all prefixes/signs/... to calculate padding and leading zeroes */
243 if (flags & __PRINTF_FLAG_PREFIX) {
244 switch(base) {
245 case 2: /* Binary formating is not standard, but usefull */
246 size += 2;
247 break;
248 case 8:
249 size++;
250 break;
251 case 16:
252 size += 2;
253 break;
254 }
255 }
256
257 sgn = 0;
258 if (flags & __PRINTF_FLAG_SIGNED) {
259 if (flags & __PRINTF_FLAG_NEGATIVE) {
260 sgn = '-';
261 size++;
262 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
263 sgn = '+';
264 size++;
265 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
266 sgn = ' ';
267 size++;
268 }
269 }
270
271 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
272 flags &= ~__PRINTF_FLAG_ZEROPADDED;
273 }
274
275 /* if number is leftaligned or precision is specified then zeropadding is ignored */
276 if (flags & __PRINTF_FLAG_ZEROPADDED) {
277 if ((precision == 0) && (width > size)) {
278 precision = width - size + number_size;
279 }
280 }
281
282 /* print leading spaces */
283 if (number_size > precision) /* We must print whole number not only a part */
284 precision = number_size;
285
286 width -= precision + size - number_size;
287
288 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
289 while (width-- > 0) {
290 if (printf_putchar(' ', ps) == 1)
291 counter++;
292 }
293 }
294
295 /* print sign */
296 if (sgn) {
297 if (printf_putchar(sgn, ps) == 1)
298 counter++;
299 }
300
301 /* print prefix */
302
303 if (flags & __PRINTF_FLAG_PREFIX) {
304 switch(base) {
305 case 2: /* Binary formating is not standard, but usefull */
306 if (printf_putchar('0', ps) == 1)
307 counter++;
308 if (flags & __PRINTF_FLAG_BIGCHARS) {
309 if (printf_putchar('B', ps) == 1)
310 counter++;
311 } else {
312 if (printf_putchar('b', ps) == 1)
313 counter++;
314 }
315 break;
316 case 8:
317 if (printf_putchar('o', ps) == 1)
318 counter++;
319 break;
320 case 16:
321 if (printf_putchar('0', ps) == 1)
322 counter++;
323 if (flags & __PRINTF_FLAG_BIGCHARS) {
324 if (printf_putchar('X', ps) == 1)
325 counter++;
326 } else {
327 if (printf_putchar('x', ps) == 1)
328 counter++;
329 }
330 break;
331 }
332 }
333
334 /* print leading zeroes */
335 precision -= number_size;
336 while (precision-- > 0) {
337 if (printf_putchar('0', ps) == 1)
338 counter++;
339 }
340
341
342 /* print number itself */
343
344 if ((retval = printf_putstr(++ptr, ps)) > 0) {
345 counter += retval;
346 }
347
348 /* print ending spaces */
349
350 while (width-- > 0) {
351 if (printf_putchar(' ', ps) == 1)
352 counter++;
353 }
354
355 return counter;
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 /* Don't let other threads interfere */
448 async_serialize_start();
449
450 counter = 0;
451
452 while ((c = fmt[i])) {
453 /* control character */
454 if (c == '%' ) {
455 /* print common characters if any processed */
456 if (i > j) {
457 if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */
458 goto minus_out;
459 }
460 counter += retval;
461 }
462
463 j = i;
464 /* parse modifiers */
465 flags = 0;
466 end = 0;
467
468 do {
469 ++i;
470 switch (c = fmt[i]) {
471 case '#': flags |= __PRINTF_FLAG_PREFIX; break;
472 case '-': flags |= __PRINTF_FLAG_LEFTALIGNED; break;
473 case '+': flags |= __PRINTF_FLAG_SHOWPLUS; break;
474 case ' ': flags |= __PRINTF_FLAG_SPACESIGN; break;
475 case '0': flags |= __PRINTF_FLAG_ZEROPADDED; break;
476 default: end = 1;
477 };
478
479 } while (end == 0);
480
481 /* width & '*' operator */
482 width = 0;
483 if (isdigit(fmt[i])) {
484 while (isdigit(fmt[i])) {
485 width *= 10;
486 width += fmt[i++] - '0';
487 }
488 } else if (fmt[i] == '*') {
489 /* get width value from argument list*/
490 i++;
491 width = (int)va_arg(ap, int);
492 if (width < 0) {
493 /* negative width means to set '-' flag */
494 width *= -1;
495 flags |= __PRINTF_FLAG_LEFTALIGNED;
496 }
497 }
498
499 /* precision and '*' operator */
500 precision = 0;
501 if (fmt[i] == '.') {
502 ++i;
503 if (isdigit(fmt[i])) {
504 while (isdigit(fmt[i])) {
505 precision *= 10;
506 precision += fmt[i++] - '0';
507 }
508 } else if (fmt[i] == '*') {
509 /* get precision value from argument list*/
510 i++;
511 precision = (int)va_arg(ap, int);
512 if (precision < 0) {
513 /* negative precision means to ignore it */
514 precision = 0;
515 }
516 }
517 }
518
519 switch (fmt[i++]) {
520 /** TODO: unimplemented qualifiers:
521 * t ptrdiff_t - ISO C 99
522 */
523 case 'h': /* char or short */
524 qualifier = PrintfQualifierShort;
525 if (fmt[i] == 'h') {
526 i++;
527 qualifier = PrintfQualifierByte;
528 }
529 break;
530 case 'l': /* long or long long*/
531 qualifier = PrintfQualifierLong;
532 if (fmt[i] == 'l') {
533 i++;
534 qualifier = PrintfQualifierLongLong;
535 }
536 break;
537 case 'z': /* size_t */
538 qualifier = PrintfQualifierSizeT;
539 break;
540 default:
541 qualifier = PrintfQualifierInt; /* default type */
542 --i;
543 }
544
545 base = 10;
546
547 switch (c = fmt[i]) {
548
549 /*
550 * String and character conversions.
551 */
552 case 's':
553 if ((retval = print_string(va_arg(ap, char*), width, precision, flags, ps)) < 0) {
554 goto minus_out;
555 };
556
557 counter += retval;
558 j = i + 1;
559 goto next_char;
560 case 'c':
561 c = va_arg(ap, unsigned int);
562 if ((retval = print_char(c, width, flags, ps)) < 0) {
563 goto minus_out;
564 };
565
566 counter += retval;
567 j = i + 1;
568 goto next_char;
569
570 /*
571 * Integer values
572 */
573 case 'P': /* pointer */
574 flags |= __PRINTF_FLAG_BIGCHARS;
575 case 'p':
576 flags |= __PRINTF_FLAG_PREFIX;
577 base = 16;
578 qualifier = PrintfQualifierPointer;
579 break;
580 case 'b':
581 base = 2;
582 break;
583 case 'o':
584 base = 8;
585 break;
586 case 'd':
587 case 'i':
588 flags |= __PRINTF_FLAG_SIGNED;
589 case 'u':
590 break;
591 case 'X':
592 flags |= __PRINTF_FLAG_BIGCHARS;
593 case 'x':
594 base = 16;
595 break;
596 /* percentile itself */
597 case '%':
598 j = i;
599 goto next_char;
600 /*
601 * Bad formatting.
602 */
603 default:
604 /* Unknown format
605 * now, the j is index of '%' so we will
606 * print whole bad format sequence
607 */
608 goto next_char;
609 }
610
611
612 /* Print integers */
613 /* print number */
614 switch (qualifier) {
615 case PrintfQualifierByte:
616 size = sizeof(unsigned char);
617 number = (uint64_t)va_arg(ap, unsigned int);
618 break;
619 case PrintfQualifierShort:
620 size = sizeof(unsigned short);
621 number = (uint64_t)va_arg(ap, unsigned int);
622 break;
623 case PrintfQualifierInt:
624 size = sizeof(unsigned int);
625 number = (uint64_t)va_arg(ap, unsigned int);
626 break;
627 case PrintfQualifierLong:
628 size = sizeof(unsigned long);
629 number = (uint64_t)va_arg(ap, unsigned long);
630 break;
631 case PrintfQualifierLongLong:
632 size = sizeof(unsigned long long);
633 number = (uint64_t)va_arg(ap, unsigned long long);
634 break;
635 case PrintfQualifierPointer:
636 size = sizeof(void *);
637 number = (uint64_t)(unsigned long)va_arg(ap, void *);
638 break;
639 case PrintfQualifierSizeT:
640 size = sizeof(size_t);
641 number = (uint64_t)va_arg(ap, size_t);
642 break;
643 default: /* Unknown qualifier */
644 goto minus_out;
645
646 }
647
648 if (flags & __PRINTF_FLAG_SIGNED) {
649 if (number & (0x1 << (size*8 - 1))) {
650 flags |= __PRINTF_FLAG_NEGATIVE;
651
652 if (size == sizeof(uint64_t)) {
653 number = -((int64_t)number);
654 } else {
655 number = ~number;
656 number &= (~((0xFFFFFFFFFFFFFFFFll) << (size * 8)));
657 number++;
658 }
659 }
660 }
661
662 if ((retval = print_number(number, width, precision, base, flags, ps)) < 0 ) {
663 goto minus_out;
664 };
665
666 counter += retval;
667 j = i + 1;
668 }
669next_char:
670
671 ++i;
672 }
673
674 if (i > j) {
675 if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */
676 goto minus_out;
677 }
678 counter += retval;
679 }
680
681 async_serialize_end();
682 return counter;
683minus_out:
684 async_serialize_end();
685 return -counter;
686}
687
Note: See TracBrowser for help on using the repository browser.