source: mainline/uspace/libc/generic/io/printf_core.c@ 6765c07

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6765c07 was 183788f1, checked in by Jakub Jermar <jakub@…>, 19 years ago

Remove printf() serialization from thread1 test in tester.
The agreement among developers seems to be that:

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