source: mainline/libc/generic/io/print.c@ f30e6a0b

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

Printf support for width and precision modifiers.
Several necessary helping functions added.

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