source: mainline/libc/generic/io/print.c@ 523fad8

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

Bugfix in printf, some support for testing.

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