source: mainline/libc/generic/io/print.c@ 11a4fbf

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

Userspace printf was rewritten to support standard format. Not all needed features implemented yet.

  • Property mode set to 100644
File size: 11.1 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
35#define __PRINTF_FLAG_PREFIX 0x00000001 /* show prefixes 0x or 0*/
36#define __PRINTF_FLAG_SIGNED 0x00000002 /* signed / unsigned number */
37#define __PRINTF_FLAG_ZEROPADDED 0x00000004 /* print leading zeroes */
38#define __PRINTF_FLAG_LEFTALIGNED 0x00000010 /* align to left */
39#define __PRINTF_FLAG_SHOWPLUS 0x00000020 /* always show + sign */
40#define __PRINTF_FLAG_SPACESIGN 0x00000040 /* print space instead of plus */
41#define __PRINTF_FLAG_BIGCHARS 0x00000080 /* show big characters */
42#define __PRINTF_FLAG_NEGATIVE 0x00000100 /* number has - sign */
43
44#define PRINT_NUMBER_BUFFER_SIZE (64+5) /* Buffer big enought for 64 bit number
45 * printed in base 2, sign, prefix and
46 * 0 to terminate string.. (last one is only for better testing
47 * end of buffer by zero-filling subroutine)
48 */
49typedef enum {
50 PrintfQualifierByte = 0,
51 PrintfQualifierShort,
52 PrintfQualifierInt,
53 PrintfQualifierLong,
54 PrintfQualifierLongLong,
55 PrintfQualifierSizeT,
56 PrintfQualifierPointer
57} qualifier_t;
58
59static char digits_small[] = "0123456789abcdef"; /* Small hexadecimal characters */
60static char digits_big[] = "0123456789ABCDEF"; /* Big hexadecimal characters */
61
62/** Print number in given base
63 *
64 * Print significant digits of a number in given
65 * base.
66 *
67 * @param num Number to print.
68 * @param size not used, in future releases will be replaced with precision and width params
69 * @param base Base to print the number in (should
70 * be in range 2 .. 16).
71 * @param flags output modifiers
72 * @return number of written characters or EOF
73 *
74 */
75static int print_number(uint64_t num, size_t size, int base , uint64_t flags)
76{
77 /* FIXME: This is only first version.
78 * Printf does not have support for specification of size
79 * and precision, so this function writes with parameters defined by
80 * their type size.
81 */
82 char *digits = digits_small;
83 char d[PRINT_NUMBER_BUFFER_SIZE]; /* this is good enough even for base == 2, prefix and sign */
84 char *ptr = &d[PRINT_NUMBER_BUFFER_SIZE - 1];
85
86 if (flags & __PRINTF_FLAG_BIGCHARS)
87 digits = digits_big;
88
89 *ptr-- = 0; /* Put zero at end of string */
90
91 if (num == 0) {
92 *ptr-- = '0';
93 } else {
94 do {
95 *ptr-- = digits[num % base];
96 } while (num /= base);
97 }
98 if (flags & __PRINTF_FLAG_PREFIX) { /*FIXME*/
99 switch(base) {
100 case 2: /* Binary formating is not standard, but usefull */
101 *ptr = 'b';
102 if (flags & __PRINTF_FLAG_BIGCHARS) *ptr = 'B';
103 ptr--;
104 *ptr-- = '0';
105 break;
106 case 8:
107 *ptr-- = 'o';
108 break;
109 case 16:
110 *ptr = 'x';
111 if (flags & __PRINTF_FLAG_BIGCHARS) *ptr = 'X';
112 ptr--;
113 *ptr-- = '0';
114 break;
115 }
116 }
117
118 if (flags & __PRINTF_FLAG_SIGNED) {
119 if (flags & __PRINTF_FLAG_NEGATIVE) {
120 *ptr-- = '-';
121 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
122 *ptr-- = '+';
123 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
124 *ptr-- = ' ';
125 }
126 }
127
128 /* Print leading zeroes */
129
130 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
131 flags &= ~__PRINTF_FLAG_ZEROPADDED;
132 }
133 if (flags & __PRINTF_FLAG_ZEROPADDED) {
134 while (ptr != d ) {
135 *ptr-- = '0';
136 }
137 }
138
139 return putstr(++ptr);
140}
141
142
143
144/** General formatted text print
145 *
146 * Print text formatted according the fmt parameter
147 * and variant arguments. Each formatting directive
148 * begins with \% (percentage) character and one of the
149 * following character:
150 *
151 * \% Prints the percentage character.
152 *
153 * s The next variant argument is treated as char*
154 * and printed as a NULL terminated string.
155 *
156 * c The next variant argument is treated as a single char.
157 *
158 * p The next variant argument is treated as a maximum
159 * bit-width integer with respect to architecture
160 * and printed in full hexadecimal width.
161 *
162 * P As with 'p', but '0x' is prefixed.
163 *
164 * q The next variant argument is treated as a 64b integer
165 * and printed in full hexadecimal width.
166 *
167 * Q As with 'q', but '0x' is prefixed.
168 *
169 * l The next variant argument is treated as a 32b integer
170 * and printed in full hexadecimal width.
171 *
172 * L As with 'l', but '0x' is prefixed.
173 *
174 * w The next variant argument is treated as a 16b integer
175 * and printed in full hexadecimal width.
176 *
177 * W As with 'w', but '0x' is prefixed.
178 *
179 * b The next variant argument is treated as a 8b integer
180 * and printed in full hexadecimal width.
181 *
182 * B As with 'b', but '0x' is prefixed.
183 *
184 * d The next variant argument is treated as integer
185 * and printed in standard decimal format (only significant
186 * digits).
187 *
188 * x The next variant argument is treated as integer
189 * and printed in standard hexadecimal format (only significant
190 * digits).
191 *
192 * X As with 'x', but '0x' is prefixed.
193 *
194 * All other characters from fmt except the formatting directives
195 * are printed in verbatim.
196 *
197 * @param fmt Formatting NULL terminated string.
198 */
199int printf(const char *fmt, ...)
200{
201 int i = 0, j = 0; /* i is index of currently processed char from fmt, j is index to the first not printed nonformating character */
202 int end;
203 int counter; /* counter of printed characters */
204 int retval; /* used to store return values from called functions */
205 va_list ap;
206 char c;
207 qualifier_t qualifier; /* type of argument */
208 int base; /* base in which will be parameter (numbers only) printed */
209 uint64_t number; /* argument value */
210 size_t size; /* byte size of integer parameter */
211 uint64_t flags;
212
213 counter = 0;
214 va_start(ap, fmt);
215
216 while ((c = fmt[i])) {
217 /* control character */
218 if (c == '%' ) {
219 /* print common characters if any processed */
220 if (i > j) {
221 if ((retval = putnchars(&fmt[j], (size_t)(i - j))) == EOF) { /* error */
222 return -counter;
223 }
224 counter += retval;
225 }
226
227 j = i;
228 /* parse modifiers */
229 flags = 0;
230 end = 0;
231
232 do {
233 ++i;
234 switch (c = fmt[i]) {
235 case '#': flags |= __PRINTF_FLAG_PREFIX; break;
236 case '-': flags |= __PRINTF_FLAG_LEFTALIGNED; break;
237 case '+': flags |= __PRINTF_FLAG_SHOWPLUS; break;
238 case ' ': flags |= __PRINTF_FLAG_SPACESIGN; break;
239 case '0': flags |= __PRINTF_FLAG_ZEROPADDED; break;
240 default: end = 1;
241 };
242
243 } while (end == 0);
244 /* TODO: width & '*' operator */
245 /* TODO: precision and '*' operator */
246
247 switch (fmt[i++]) {
248 case 'h': /* char or short */
249 qualifier = PrintfQualifierShort;
250 if (fmt[i] == 'h') {
251 i++;
252 qualifier = PrintfQualifierByte;
253 }
254 break;
255 case 'l': /* long or long long*/
256 qualifier = PrintfQualifierLong;
257 if (fmt[i] == 'l') {
258 i++;
259 qualifier = PrintfQualifierLongLong;
260 }
261 break;
262 case 'z': /* size_t */
263 qualifier = PrintfQualifierSizeT;
264 break;
265 default:
266 qualifier = PrintfQualifierInt; /* default type */
267 --i;
268 }
269
270 base = 10;
271
272 switch (c = fmt[i]) {
273
274 /*
275 * String and character conversions.
276 */
277 case 's':
278 if ((retval = putstr(va_arg(ap, char*))) == EOF) {
279 return -counter;
280 };
281
282 counter += retval;
283 j = i + 1;
284 goto next_char;
285 case 'c':
286 c = va_arg(ap, unsigned long);
287 if ((retval = putnchars(&c, sizeof(char))) == EOF) {
288 return -counter;
289 };
290
291 counter += retval;
292 j = i + 1;
293 goto next_char;
294
295 /*
296 * Integer values
297 */
298 case 'P': /* pointer */
299 flags |= __PRINTF_FLAG_BIGCHARS;
300 case 'p':
301 flags |= __PRINTF_FLAG_PREFIX;
302 base = 16;
303 qualifier = PrintfQualifierPointer;
304 break;
305 case 'b':
306 base = 2;
307 break;
308 case 'o':
309 base = 8;
310 break;
311 case 'd':
312 case 'i':
313 flags |= __PRINTF_FLAG_SIGNED;
314 case 'u':
315 break;
316 case 'X':
317 flags |= __PRINTF_FLAG_BIGCHARS;
318 case 'x':
319 base = 16;
320 break;
321 /* percentile itself */
322 case '%':
323 j = i;
324 goto next_char;
325 /*
326 * Bad formatting.
327 */
328 default:
329 /* Unknown format
330 * now, the j is index of '%' so we will
331 * print whole bad format sequence
332 */
333 goto next_char;
334 }
335
336
337 /* Print integers */
338 /* print number */
339 switch (qualifier) {
340 case PrintfQualifierByte:
341 size = sizeof(unsigned char);
342 number = (uint64_t)va_arg(ap, unsigned int);
343 break;
344 case PrintfQualifierShort:
345 size = sizeof(unsigned short);
346 number = (uint64_t)va_arg(ap, unsigned int);
347 break;
348 case PrintfQualifierInt:
349 size = sizeof(unsigned int);
350 number = (uint64_t)va_arg(ap, unsigned int);
351 break;
352 case PrintfQualifierLong:
353 size = sizeof(unsigned long);
354 number = (uint64_t)va_arg(ap, unsigned long);
355 break;
356 case PrintfQualifierLongLong:
357 size = sizeof(unsigned long long);
358 number = (uint64_t)va_arg(ap, unsigned long long);
359 break;
360 case PrintfQualifierPointer:
361 size = sizeof(void *);
362 number = (uint64_t)(unsigned long)va_arg(ap, void *);
363 break;
364 case PrintfQualifierSizeT:
365 size = sizeof(size_t);
366 number = (uint64_t)va_arg(ap, size_t);
367 break;
368 default: /* Unknown qualifier */
369 return -counter;
370
371 }
372
373 if (flags & __PRINTF_FLAG_SIGNED) {
374 if (number & (0x1 << (size*8 - 1))) {
375 flags |= __PRINTF_FLAG_NEGATIVE;
376
377 if (size == sizeof(uint64_t)) {
378 number = -((int64_t)number);
379 } else {
380 number = ~number;
381 number &= (~((0xFFFFFFFFFFFFFFFFll) << (size * 8)));
382 number++;
383 }
384 }
385 }
386
387 if ((retval = print_number(number, size, base, flags)) == EOF ) {
388 return -counter;
389 };
390
391 counter += retval;
392 j = i + 1;
393 }
394next_char:
395
396 ++i;
397 }
398
399 if (i > j) {
400 if ((retval = putnchars(&fmt[j], (size_t)(i - j))) == EOF) { /* error */
401 return -counter;
402 }
403 counter += retval;
404 }
405
406 va_end(ap);
407 return counter;
408}
409
Note: See TracBrowser for help on using the repository browser.