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

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

Rather big indentation and formatting changes.
More inteligent long line wrapping.

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