|
0
|
1 //=========================================================================== |
|
|
2 // |
|
|
3 // vfnprintf.c |
|
|
4 // |
|
|
5 // I/O routines for vfnprintf() for use with ANSI C library |
|
|
6 // |
|
|
7 //=========================================================================== |
|
|
8 //####COPYRIGHTBEGIN#### |
|
|
9 // |
|
|
10 // ------------------------------------------- |
|
|
11 // The contents of this file are subject to the Cygnus eCos Public License |
|
|
12 // Version 1.0 (the "License"); you may not use this file except in |
|
|
13 // compliance with the License. You may obtain a copy of the License at |
|
|
14 // http://sourceware.cygnus.com/ecos |
|
|
15 // |
|
|
16 // Software distributed under the License is distributed on an "AS IS" |
|
|
17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the |
|
|
18 // License for the specific language governing rights and limitations under |
|
|
19 // the License. |
|
|
20 // |
|
|
21 // The Original Code is eCos - Embedded Cygnus Operating System, released |
|
|
22 // September 30, 1998. |
|
|
23 // |
|
|
24 // The Initial Developer of the Original Code is Cygnus. Portions created |
|
|
25 // by Cygnus are Copyright (C) 1998 Cygnus Solutions. All Rights Reserved. |
|
|
26 // ------------------------------------------- |
|
|
27 // |
|
|
28 //####COPYRIGHTEND#### |
|
|
29 //=========================================================================== |
|
|
30 //#####DESCRIPTIONBEGIN#### |
|
|
31 // |
|
|
32 // Author(s): jlarmour |
|
|
33 // Contributors: jlarmour@cygnus.co.uk |
|
|
34 // Date: 1998-02-13 |
|
|
35 // Purpose: |
|
|
36 // Description: |
|
|
37 // Usage: |
|
|
38 // |
|
|
39 //####DESCRIPTIONEND#### |
|
|
40 // |
|
|
41 //=========================================================================== |
|
|
42 // |
|
|
43 // This code is based on original code with the following copyright: |
|
|
44 // |
|
|
45 /*- |
|
|
46 * Copyright (c) 1990 The Regents of the University of California. |
|
|
47 * All rights reserved. |
|
|
48 * |
|
|
49 * This code is derived from software contributed to Berkeley by |
|
|
50 * Chris Torek. |
|
|
51 * |
|
|
52 * Redistribution and use in source and binary forms, with or without |
|
|
53 * modification, are permitted provided that the following conditions |
|
|
54 * are met: |
|
|
55 * 1. Redistributions of source code must retain the above copyright |
|
|
56 * notice, this list of conditions and the following disclaimer. |
|
|
57 * 2. Redistributions in binary form must reproduce the above copyright |
|
|
58 * notice, this list of conditions and the following disclaimer in the |
|
|
59 * documentation and/or other materials provided with the distribution. |
|
|
60 * 3. All advertising materials mentioning features or use of this software |
|
|
61 * must display the following acknowledgement: |
|
|
62 * This product includes software developed by the University of |
|
|
63 * California, Berkeley and its contributors. |
|
|
64 * 4. Neither the name of the University nor the names of its contributors |
|
|
65 * may be used to endorse or promote products derived from this software |
|
|
66 * without specific prior written permission. |
|
|
67 * |
|
|
68 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|
|
69 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
|
70 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
|
71 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|
|
72 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
|
73 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
|
74 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
|
75 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
|
76 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
|
77 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
|
78 * SUCH DAMAGE. |
|
|
79 */ |
|
|
80 |
|
|
81 |
|
|
82 // CONFIGURATION |
|
|
83 |
|
|
84 #include <pkgconf/libc.h> // Configuration header |
|
|
85 |
|
|
86 // Include the C library? And do we want the stdio stuff? |
|
|
87 #if defined(CYGPKG_LIBC) && defined(CYGPKG_LIBC_STDIO) |
|
|
88 |
|
|
89 // INCLUDES |
|
|
90 |
|
|
91 #include <cyg/infra/cyg_type.h> // Common type definitions and support |
|
|
92 #include <stdarg.h> // Variable argument definitions |
|
|
93 #include <stdio.h> // Standard header for all stdio files |
|
|
94 #include "clibincl/stringsupp.hxx"// _memchr() and _strlen() functions |
|
|
95 #include "clibincl/stream.hxx" // C library streams |
|
|
96 |
|
|
97 |
|
|
98 // EXPORTED SYMBOLS |
|
|
99 |
|
|
100 externC int |
|
|
101 vfnprintf ( FILE *, size_t, const char *, va_list ) |
|
|
102 CYGPRI_LIBC_WEAK_ALIAS("_vfnprintf"); |
|
|
103 |
|
|
104 |
|
|
105 #ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
106 |
|
|
107 # include <float.h> // for DBL_DIG etc. below |
|
|
108 # include <math.h> // for modf() |
|
|
109 # include <sys/ieeefp.h> // Cyg_libm_ieee_double_shape_type |
|
|
110 |
|
|
111 # define MAXFRACT DBL_DIG |
|
|
112 # define MAXEXP DBL_MAX_10_EXP |
|
|
113 |
|
|
114 # define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ |
|
|
115 # define DEFPREC 6 |
|
|
116 |
|
|
117 static int |
|
|
118 cvt( double, int, int, char *, int, char *, char * ); |
|
|
119 |
|
|
120 #else // ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
121 |
|
|
122 # define BUF 40 |
|
|
123 |
|
|
124 #endif // ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
125 |
|
|
126 /* |
|
|
127 * Actual printf innards. |
|
|
128 * |
|
|
129 * This code is large and complicated... |
|
|
130 */ |
|
|
131 |
|
|
132 |
|
|
133 |
|
|
134 /* |
|
|
135 * Macros for converting digits to letters and vice versa |
|
|
136 */ |
|
|
137 #define to_digit(c) ((c) - '0') |
|
|
138 #define is_digit(c) ((unsigned)to_digit(c) <= 9) |
|
|
139 #define to_char(n) ((n) + '0') |
|
|
140 |
|
|
141 /* |
|
|
142 * Flags used during conversion. |
|
|
143 */ |
|
|
144 #define ALT 0x001 /* alternate form */ |
|
|
145 #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ |
|
|
146 #define LADJUST 0x004 /* left adjustment */ |
|
|
147 #define LONGDBL 0x008 /* long double; unimplemented */ |
|
|
148 #define LONGINT 0x010 /* long integer */ |
|
|
149 #define QUADINT 0x020 /* quad integer */ |
|
|
150 #define SHORTINT 0x040 /* short integer */ |
|
|
151 #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ |
|
|
152 #define FPT 0x100 /* Floating point number */ |
|
|
153 |
|
|
154 static int |
|
|
155 __mbtowc(char *pwc, const char *s, size_t n) |
|
|
156 { |
|
|
157 if (s == NULL) |
|
|
158 return 0; |
|
|
159 if (n == 0) |
|
|
160 return -1; |
|
|
161 if (pwc) |
|
|
162 *pwc = (char) *s; |
|
|
163 return (*s != '\0'); |
|
|
164 } |
|
|
165 |
|
|
166 |
|
|
167 externC int |
|
|
168 _vfnprintf ( FILE *stream, size_t n, const char *format, va_list arg) |
|
|
169 { |
|
|
170 char *fmt; /* format string */ |
|
|
171 int ch; /* character from fmt */ |
|
|
172 int x, y; /* handy integers (short term usage) */ |
|
|
173 char *cp; /* handy char pointer (short term usage) */ |
|
|
174 int flags; /* flags as above */ |
|
|
175 int ret; /* return value accumulator */ |
|
|
176 int width; /* width from format (%8d), or 0 */ |
|
|
177 int prec; /* precision from format (%.3d), or -1 */ |
|
|
178 char sign; /* sign prefix (' ', '+', '-', or \0) */ |
|
|
179 char wc; |
|
|
180 |
|
|
181 #define quad_t long long |
|
|
182 #define u_quad_t unsigned long long |
|
|
183 |
|
|
184 u_quad_t _uquad; /* integer arguments %[diouxX] */ |
|
|
185 enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ |
|
|
186 int dprec; /* a copy of prec if [diouxX], 0 otherwise */ |
|
|
187 int fieldsz; /* field size expanded by sign, etc */ |
|
|
188 int realsz; /* field size expanded by dprec */ |
|
|
189 int size; /* size of converted field or string */ |
|
|
190 char *xdigs; /* digits for [xX] conversion */ |
|
|
191 #define NIOV 8 |
|
|
192 char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ |
|
|
193 char ox[2]; /* space for 0x hex-prefix */ |
|
|
194 #ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
195 char softsign; /* temporary negative sign for floats */ |
|
|
196 double _double; /* double precision arguments %[eEfgG] */ |
|
|
197 int fpprec; /* `extra' floating precision in [eEfgG] */ |
|
|
198 #endif |
|
|
199 |
|
|
200 /* |
|
|
201 * Choose PADSIZE to trade efficiency vs. size. If larger printf |
|
|
202 * fields occur frequently, increase PADSIZE and make the initialisers |
|
|
203 * below longer. |
|
|
204 */ |
|
|
205 #define PADSIZE 16 /* pad chunk size */ |
|
|
206 static char blanks[PADSIZE] = |
|
|
207 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; |
|
|
208 static char zeroes[PADSIZE] = |
|
|
209 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; |
|
|
210 |
|
|
211 /* |
|
|
212 * BEWARE, these `goto error' on error, and PAD uses `n'. |
|
|
213 */ |
|
|
214 #define PRINT(ptr, len) { cyg_ucount32 length = len; \ |
|
|
215 if (((Cyg_StdioStream *)stream)->write( (const cyg_uint8 *)ptr, \ |
|
|
216 (cyg_ucount32) len, &length )) \ |
|
|
217 goto error; } |
|
|
218 |
|
|
219 |
|
|
220 #define PAD(howmany, with) { \ |
|
|
221 if ((x = (howmany)) > 0) { \ |
|
|
222 while (x > PADSIZE) { \ |
|
|
223 PRINT(with, PADSIZE); \ |
|
|
224 x -= PADSIZE; \ |
|
|
225 } \ |
|
|
226 PRINT(with, x); \ |
|
|
227 } \ |
|
|
228 } |
|
|
229 #define FLUSH() { if (((Cyg_StdioStream *)stream)->flush_output()) \ |
|
|
230 goto error; } |
|
|
231 |
|
|
232 /* |
|
|
233 * To extend shorts properly, we need both signed and unsigned |
|
|
234 * argument extraction methods. |
|
|
235 */ |
|
|
236 |
|
|
237 #define SARG() \ |
|
|
238 (flags&QUADINT ? va_arg(arg, cyg_int64) : \ |
|
|
239 flags&LONGINT ? va_arg(arg, cyg_int32) : \ |
|
|
240 flags&SHORTINT ? (long)va_arg(arg, cyg_int32) : \ |
|
|
241 (long)va_arg(arg, cyg_int32)) |
|
|
242 #define UARG() \ |
|
|
243 (flags&QUADINT ? va_arg(arg, cyg_uint64) : \ |
|
|
244 flags&LONGINT ? va_arg(arg, cyg_uint32) : \ |
|
|
245 flags&SHORTINT ? (unsigned long)va_arg(arg, cyg_uint32) : \ |
|
|
246 (unsigned long)va_arg(arg, cyg_uint32)) |
|
|
247 |
|
|
248 |
|
|
249 xdigs = NULL; // stop compiler whinging |
|
|
250 fmt = (char *)format; |
|
|
251 ret = 0; |
|
|
252 |
|
|
253 /* |
|
|
254 * Scan the format for conversions (`%' character). |
|
|
255 */ |
|
|
256 for (;;) { |
|
|
257 cp = (char *)fmt; |
|
|
258 while ((x = __mbtowc(&wc, fmt, 1)) > 0) { |
|
|
259 fmt += x; |
|
|
260 if (wc == '%') { |
|
|
261 fmt--; |
|
|
262 break; |
|
|
263 } |
|
|
264 } |
|
|
265 if ((y = fmt - cp) != 0) { |
|
|
266 PRINT(cp, y); |
|
|
267 ret += y; |
|
|
268 } |
|
|
269 if ((x <= 0) || (ret >= (int)n)) // @@@ this check with n isn't good enough |
|
|
270 goto done; |
|
|
271 fmt++; /* skip over '%' */ |
|
|
272 |
|
|
273 flags = 0; |
|
|
274 dprec = 0; |
|
|
275 #ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
276 fpprec = 0; |
|
|
277 #endif |
|
|
278 width = 0; |
|
|
279 prec = -1; |
|
|
280 sign = '\0'; |
|
|
281 |
|
|
282 rflag: ch = *fmt++; |
|
|
283 reswitch: switch (ch) { |
|
|
284 case ' ': |
|
|
285 /* |
|
|
286 * ``If the space and + flags both appear, the space |
|
|
287 * flag will be ignored.'' |
|
|
288 * -- ANSI X3J11 |
|
|
289 */ |
|
|
290 if (!sign) |
|
|
291 sign = ' '; |
|
|
292 goto rflag; |
|
|
293 case '#': |
|
|
294 flags |= ALT; |
|
|
295 goto rflag; |
|
|
296 case '*': |
|
|
297 /* |
|
|
298 * ``A negative field width argument is taken as a |
|
|
299 * - flag followed by a positive field width.'' |
|
|
300 * -- ANSI X3J11 |
|
|
301 * They don't exclude field widths read from args. |
|
|
302 */ |
|
|
303 if ((width = va_arg(arg, int)) >= 0) |
|
|
304 goto rflag; |
|
|
305 width = -width; |
|
|
306 /* FALLTHROUGH */ |
|
|
307 case '-': |
|
|
308 flags |= LADJUST; |
|
|
309 goto rflag; |
|
|
310 case '+': |
|
|
311 sign = '+'; |
|
|
312 goto rflag; |
|
|
313 case '.': |
|
|
314 if ((ch = *fmt++) == '*') { |
|
|
315 x = va_arg(arg, int); |
|
|
316 prec = x < 0 ? -1 : x; |
|
|
317 goto rflag; |
|
|
318 } |
|
|
319 x = 0; |
|
|
320 while (is_digit(ch)) { |
|
|
321 x = 10 * x + to_digit(ch); |
|
|
322 ch = *fmt++; |
|
|
323 } |
|
|
324 prec = x < 0 ? -1 : x; |
|
|
325 goto reswitch; |
|
|
326 case '0': |
|
|
327 /* |
|
|
328 * ``Note that 0 is taken as a flag, not as the |
|
|
329 * beginning of a field width.'' |
|
|
330 * -- ANSI X3J11 |
|
|
331 */ |
|
|
332 flags |= ZEROPAD; |
|
|
333 goto rflag; |
|
|
334 case '1': case '2': case '3': case '4': |
|
|
335 case '5': case '6': case '7': case '8': case '9': |
|
|
336 x = 0; |
|
|
337 do { |
|
|
338 x = 10 * x + to_digit(ch); |
|
|
339 ch = *fmt++; |
|
|
340 } while (is_digit(ch)); |
|
|
341 width = x; |
|
|
342 goto reswitch; |
|
|
343 #ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
344 case 'L': |
|
|
345 flags |= LONGDBL; |
|
|
346 goto rflag; |
|
|
347 #endif |
|
|
348 case 'h': |
|
|
349 flags |= SHORTINT; |
|
|
350 goto rflag; |
|
|
351 case 'l': |
|
|
352 if (*fmt == 'l') { |
|
|
353 fmt++; |
|
|
354 flags |= QUADINT; |
|
|
355 } else { |
|
|
356 flags |= LONGINT; |
|
|
357 } |
|
|
358 goto rflag; |
|
|
359 case 'q': |
|
|
360 flags |= QUADINT; |
|
|
361 goto rflag; |
|
|
362 case 'c': |
|
|
363 *(cp = buf) = va_arg(arg, int); |
|
|
364 size = 1; |
|
|
365 sign = '\0'; |
|
|
366 break; |
|
|
367 case 'D': |
|
|
368 flags |= LONGINT; |
|
|
369 /*FALLTHROUGH*/ |
|
|
370 case 'd': |
|
|
371 case 'i': |
|
|
372 _uquad = SARG(); |
|
|
373 #ifndef _NO_LONGLONG |
|
|
374 if ((quad_t)_uquad < 0) |
|
|
375 #else |
|
|
376 if ((long) _uquad < 0) |
|
|
377 #endif |
|
|
378 { |
|
|
379 |
|
|
380 _uquad = -_uquad; |
|
|
381 sign = '-'; |
|
|
382 } |
|
|
383 base = DEC; |
|
|
384 goto number; |
|
|
385 |
|
|
386 #ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
387 case 'e': |
|
|
388 case 'E': |
|
|
389 case 'f': |
|
|
390 case 'g': |
|
|
391 case 'G': |
|
|
392 _double = va_arg(arg, double); |
|
|
393 /* |
|
|
394 * don't do unrealistic precision; just pad it with |
|
|
395 * zeroes later, so buffer size stays rational. |
|
|
396 */ |
|
|
397 if (prec > MAXFRACT) { |
|
|
398 if ((ch != 'g' && ch != 'G') || (flags&ALT)) |
|
|
399 fpprec = prec - MAXFRACT; |
|
|
400 prec = MAXFRACT; |
|
|
401 } else if (prec == -1) |
|
|
402 prec = DEFPREC; |
|
|
403 /* |
|
|
404 * cvt may have to round up before the "start" of |
|
|
405 * its buffer, i.e. ``intf("%.2f", (double)9.999);''; |
|
|
406 * if the first character is still NUL, it did. |
|
|
407 * softsign avoids negative 0 if _double < 0 but |
|
|
408 * no significant digits will be shown. |
|
|
409 */ |
|
|
410 cp = buf; |
|
|
411 *cp = '\0'; |
|
|
412 size = cvt(_double, prec, flags, &softsign, ch, |
|
|
413 cp, buf + sizeof(buf)); |
|
|
414 if (softsign) |
|
|
415 sign = '-'; |
|
|
416 if (*cp == '\0') |
|
|
417 cp++; |
|
|
418 break; |
|
|
419 #else |
|
|
420 case 'e': |
|
|
421 case 'E': |
|
|
422 case 'f': |
|
|
423 case 'g': |
|
|
424 case 'G': |
|
|
425 // Output nothing at all |
|
|
426 (void) va_arg(arg, double); // take off arg anyway |
|
|
427 cp = ""; |
|
|
428 size = 0; |
|
|
429 sign = '\0'; |
|
|
430 break; |
|
|
431 |
|
|
432 |
|
|
433 #endif // ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
434 |
|
|
435 case 'n': |
|
|
436 #ifndef _NO_LONGLONG |
|
|
437 if (flags & QUADINT) |
|
|
438 *va_arg(arg, quad_t *) = ret; |
|
|
439 else |
|
|
440 #endif |
|
|
441 if (flags & LONGINT) |
|
|
442 *va_arg(arg, long *) = ret; |
|
|
443 else if (flags & SHORTINT) |
|
|
444 *va_arg(arg, short *) = ret; |
|
|
445 else |
|
|
446 *va_arg(arg, int *) = ret; |
|
|
447 continue; /* no output */ |
|
|
448 case 'O': |
|
|
449 flags |= LONGINT; |
|
|
450 /*FALLTHROUGH*/ |
|
|
451 case 'o': |
|
|
452 _uquad = UARG(); |
|
|
453 base = OCT; |
|
|
454 goto nosign; |
|
|
455 case 'p': |
|
|
456 /* |
|
|
457 * ``The argument shall be a pointer to void. The |
|
|
458 * value of the pointer is converted to a sequence |
|
|
459 * of printable characters, in an implementation- |
|
|
460 * defined manner.'' |
|
|
461 * -- ANSI X3J11 |
|
|
462 */ |
|
|
463 /* NOSTRICT */ |
|
|
464 _uquad = (unsigned long)va_arg(arg, void *); |
|
|
465 base = HEX; |
|
|
466 xdigs = "0123456789abcdef"; |
|
|
467 flags |= HEXPREFIX; |
|
|
468 ch = 'x'; |
|
|
469 goto nosign; |
|
|
470 case 's': |
|
|
471 if ((cp = va_arg(arg, char *)) == NULL) |
|
|
472 cp = "(null)"; |
|
|
473 if (prec >= 0) { |
|
|
474 /* |
|
|
475 * can't use strlen; can only look for the |
|
|
476 * NUL in the first `prec' characters, and |
|
|
477 * strlen() will go further. |
|
|
478 */ |
|
|
479 char *p = (char *)_memchr(cp, 0, prec); |
|
|
480 |
|
|
481 if (p != NULL) { |
|
|
482 size = p - cp; |
|
|
483 if (size > prec) |
|
|
484 size = prec; |
|
|
485 } else |
|
|
486 size = prec; |
|
|
487 } else |
|
|
488 size = _strlen(cp); |
|
|
489 sign = '\0'; |
|
|
490 break; |
|
|
491 case 'U': |
|
|
492 flags |= LONGINT; |
|
|
493 /*FALLTHROUGH*/ |
|
|
494 case 'u': |
|
|
495 _uquad = UARG(); |
|
|
496 base = DEC; |
|
|
497 goto nosign; |
|
|
498 case 'X': |
|
|
499 xdigs = "0123456789ABCDEF"; |
|
|
500 goto hex; |
|
|
501 case 'x': |
|
|
502 xdigs = "0123456789abcdef"; |
|
|
503 hex: _uquad = UARG(); |
|
|
504 base = HEX; |
|
|
505 /* leading 0x/X only if non-zero */ |
|
|
506 if (flags & ALT && _uquad != 0) |
|
|
507 flags |= HEXPREFIX; |
|
|
508 |
|
|
509 /* unsigned conversions */ |
|
|
510 nosign: sign = '\0'; |
|
|
511 /* |
|
|
512 * ``... diouXx conversions ... if a precision is |
|
|
513 * specified, the 0 flag will be ignored.'' |
|
|
514 * -- ANSI X3J11 |
|
|
515 */ |
|
|
516 number: if ((dprec = prec) >= 0) |
|
|
517 flags &= ~ZEROPAD; |
|
|
518 |
|
|
519 /* |
|
|
520 * ``The result of converting a zero value with an |
|
|
521 * explicit precision of zero is no characters.'' |
|
|
522 * -- ANSI X3J11 |
|
|
523 */ |
|
|
524 cp = buf + BUF; |
|
|
525 if (_uquad != 0 || prec != 0) { |
|
|
526 /* |
|
|
527 * Unsigned mod is hard, and unsigned mod |
|
|
528 * by a constant is easier than that by |
|
|
529 * a variable; hence this switch. |
|
|
530 */ |
|
|
531 switch (base) { |
|
|
532 case OCT: |
|
|
533 do { |
|
|
534 *--cp = to_char(_uquad & 7); |
|
|
535 _uquad >>= 3; |
|
|
536 } while (_uquad); |
|
|
537 /* handle octal leading 0 */ |
|
|
538 if (flags & ALT && *cp != '0') |
|
|
539 *--cp = '0'; |
|
|
540 break; |
|
|
541 |
|
|
542 case DEC: |
|
|
543 /* many numbers are 1 digit */ |
|
|
544 while (_uquad >= 10) { |
|
|
545 *--cp = to_char(_uquad % 10); |
|
|
546 _uquad /= 10; |
|
|
547 } |
|
|
548 *--cp = to_char(_uquad); |
|
|
549 break; |
|
|
550 |
|
|
551 case HEX: |
|
|
552 do { |
|
|
553 *--cp = xdigs[_uquad & 15]; |
|
|
554 _uquad >>= 4; |
|
|
555 } while (_uquad); |
|
|
556 break; |
|
|
557 |
|
|
558 default: |
|
|
559 cp = "bug in vfprintf: bad base"; |
|
|
560 size = _strlen(cp); |
|
|
561 goto skipsize; |
|
|
562 } |
|
|
563 } |
|
|
564 size = buf + BUF - cp; |
|
|
565 skipsize: |
|
|
566 break; |
|
|
567 default: /* "%?" prints ?, unless ? is NUL */ |
|
|
568 if (ch == '\0') |
|
|
569 goto done; |
|
|
570 /* pretend it was %c with argument ch */ |
|
|
571 cp = buf; |
|
|
572 *cp = ch; |
|
|
573 size = 1; |
|
|
574 sign = '\0'; |
|
|
575 break; |
|
|
576 } |
|
|
577 |
|
|
578 /* |
|
|
579 * All reasonable formats wind up here. At this point, `cp' |
|
|
580 * points to a string which (if not flags&LADJUST) should be |
|
|
581 * padded out to `width' places. If flags&ZEROPAD, it should |
|
|
582 * first be prefixed by any sign or other prefix; otherwise, |
|
|
583 * it should be blank padded before the prefix is emitted. |
|
|
584 * After any left-hand padding and prefixing, emit zeroes |
|
|
585 * required by a decimal [diouxX] precision, then print the |
|
|
586 * string proper, then emit zeroes required by any leftover |
|
|
587 * floating precision; finally, if LADJUST, pad with blanks. |
|
|
588 * |
|
|
589 * Compute actual size, so we know how much to pad. |
|
|
590 * fieldsz excludes decimal prec; realsz includes it. |
|
|
591 */ |
|
|
592 #ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
593 fieldsz = size + fpprec; |
|
|
594 #else |
|
|
595 fieldsz = size; |
|
|
596 #endif |
|
|
597 if (sign) |
|
|
598 fieldsz++; |
|
|
599 else if (flags & HEXPREFIX) |
|
|
600 fieldsz+= 2; |
|
|
601 realsz = dprec > fieldsz ? dprec : fieldsz; |
|
|
602 |
|
|
603 /* right-adjusting blank padding */ |
|
|
604 if ((flags & (LADJUST|ZEROPAD)) == 0) |
|
|
605 PAD(width - realsz, blanks); |
|
|
606 |
|
|
607 /* prefix */ |
|
|
608 if (sign) { |
|
|
609 PRINT(&sign, 1); |
|
|
610 } else if (flags & HEXPREFIX) { |
|
|
611 ox[0] = '0'; |
|
|
612 ox[1] = ch; |
|
|
613 PRINT(ox, 2); |
|
|
614 } |
|
|
615 |
|
|
616 /* right-adjusting zero padding */ |
|
|
617 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) |
|
|
618 PAD(width - realsz, zeroes); |
|
|
619 |
|
|
620 /* leading zeroes from decimal precision */ |
|
|
621 PAD(dprec - fieldsz, zeroes); |
|
|
622 |
|
|
623 /* the string or number proper */ |
|
|
624 PRINT(cp, size); |
|
|
625 |
|
|
626 #ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
627 /* trailing f.p. zeroes */ |
|
|
628 PAD(fpprec, zeroes); |
|
|
629 #endif |
|
|
630 |
|
|
631 /* left-adjusting padding (always blank) */ |
|
|
632 if (flags & LADJUST) |
|
|
633 PAD(width - realsz, blanks); |
|
|
634 |
|
|
635 /* finally, adjust ret */ |
|
|
636 ret += width > realsz ? width : realsz; |
|
|
637 |
|
|
638 FLUSH(); /* copy out the I/O vectors */ |
|
|
639 } |
|
|
640 done: |
|
|
641 FLUSH(); |
|
|
642 error: |
|
|
643 return (((Cyg_StdioStream *) stream)->get_error() ? EOF : ret); |
|
|
644 /* NOTREACHED */ |
|
|
645 } |
|
|
646 |
|
|
647 |
|
|
648 #ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
649 |
|
|
650 static char * |
|
|
651 round(double fract, int *exp, char *start, char *end, char ch, char *signp) |
|
|
652 { |
|
|
653 double tmp; |
|
|
654 |
|
|
655 if (fract) |
|
|
656 (void)modf(fract * 10, &tmp); |
|
|
657 else |
|
|
658 tmp = to_digit(ch); |
|
|
659 if (tmp > 4) |
|
|
660 for (;; --end) { |
|
|
661 if (*end == '.') |
|
|
662 --end; |
|
|
663 if (++*end <= '9') |
|
|
664 break; |
|
|
665 *end = '0'; |
|
|
666 if (end == start) { |
|
|
667 if (exp) { /* e/E; increment exponent */ |
|
|
668 *end = '1'; |
|
|
669 ++*exp; |
|
|
670 } |
|
|
671 else { /* f; add extra digit */ |
|
|
672 *--end = '1'; |
|
|
673 --start; |
|
|
674 } |
|
|
675 break; |
|
|
676 } |
|
|
677 } |
|
|
678 /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ |
|
|
679 else if (*signp == '-') |
|
|
680 for (;; --end) { |
|
|
681 if (*end == '.') |
|
|
682 --end; |
|
|
683 if (*end != '0') |
|
|
684 break; |
|
|
685 if (end == start) |
|
|
686 *signp = 0; |
|
|
687 } |
|
|
688 return (start); |
|
|
689 } // round() |
|
|
690 |
|
|
691 |
|
|
692 static char * |
|
|
693 exponent(char *p, int exp, int fmtch) |
|
|
694 { |
|
|
695 char *t; |
|
|
696 char expbuf[MAXEXP]; |
|
|
697 |
|
|
698 *p++ = fmtch; |
|
|
699 if (exp < 0) { |
|
|
700 exp = -exp; |
|
|
701 *p++ = '-'; |
|
|
702 } |
|
|
703 else |
|
|
704 *p++ = '+'; |
|
|
705 t = expbuf + MAXEXP; |
|
|
706 if (exp > 9) { |
|
|
707 do { |
|
|
708 *--t = to_char(exp % 10); |
|
|
709 } while ((exp /= 10) > 9); |
|
|
710 *--t = to_char(exp); |
|
|
711 for (; t < expbuf + MAXEXP; *p++ = *t++); |
|
|
712 } |
|
|
713 else { |
|
|
714 *p++ = '0'; |
|
|
715 *p++ = to_char(exp); |
|
|
716 } |
|
|
717 return (p); |
|
|
718 } // exponent() |
|
|
719 |
|
|
720 |
|
|
721 static int |
|
|
722 cvt(double number, int prec, int flags, char *signp, int fmtch, char *startp, |
|
|
723 char *endp) |
|
|
724 { |
|
|
725 char *p, *t; |
|
|
726 double fract; |
|
|
727 int dotrim, expcnt, gformat; |
|
|
728 double integer, tmp; |
|
|
729 Cyg_libm_ieee_double_shape_type ieeefp; |
|
|
730 |
|
|
731 dotrim = expcnt = gformat = 0; |
|
|
732 ieeefp.value = number; |
|
|
733 if ( ieeefp.number.sign ){ // this checks for <0.0 and -0.0 |
|
|
734 number = -number; |
|
|
735 *signp = '-'; |
|
|
736 } else |
|
|
737 *signp = 0; |
|
|
738 |
|
|
739 fract = modf(number, &integer); |
|
|
740 |
|
|
741 /* get an extra slot for rounding. */ |
|
|
742 t = ++startp; |
|
|
743 |
|
|
744 /* |
|
|
745 * get integer portion of number; put into the end of the buffer; the |
|
|
746 * .01 is added for modf(356.0 / 10, &integer) returning .59999999... |
|
|
747 */ |
|
|
748 for (p = endp - 1; integer; ++expcnt) { |
|
|
749 tmp = modf(integer / 10, &integer); |
|
|
750 *p-- = to_char((int)((tmp + .01) * 10)); |
|
|
751 } |
|
|
752 switch (fmtch) { |
|
|
753 case 'f': |
|
|
754 /* reverse integer into beginning of buffer */ |
|
|
755 if (expcnt) |
|
|
756 for (; ++p < endp; *t++ = *p); |
|
|
757 else |
|
|
758 *t++ = '0'; |
|
|
759 /* |
|
|
760 * if precision required or alternate flag set, add in a |
|
|
761 * decimal point. |
|
|
762 */ |
|
|
763 if (prec || flags&ALT) |
|
|
764 *t++ = '.'; |
|
|
765 /* if requires more precision and some fraction left */ |
|
|
766 if (fract) { |
|
|
767 if (prec) |
|
|
768 do { |
|
|
769 fract = modf(fract * 10, &tmp); |
|
|
770 *t++ = to_char((int)tmp); |
|
|
771 } while (--prec && fract); |
|
|
772 if (fract) |
|
|
773 startp = round(fract, (int *)NULL, startp, |
|
|
774 t - 1, (char)0, signp); |
|
|
775 } |
|
|
776 for (; prec--; *t++ = '0'); |
|
|
777 break; |
|
|
778 case 'e': |
|
|
779 case 'E': |
|
|
780 eformat: if (expcnt) { |
|
|
781 *t++ = *++p; |
|
|
782 if (prec || flags&ALT) |
|
|
783 *t++ = '.'; |
|
|
784 /* if requires more precision and some integer left */ |
|
|
785 for (; prec && ++p < endp; --prec) |
|
|
786 *t++ = *p; |
|
|
787 /* |
|
|
788 * if done precision and more of the integer component, |
|
|
789 * round using it; adjust fract so we don't re-round |
|
|
790 * later. |
|
|
791 */ |
|
|
792 if (!prec && ++p < endp) { |
|
|
793 fract = 0; |
|
|
794 startp = round((double)0, &expcnt, startp, |
|
|
795 t - 1, *p, signp); |
|
|
796 } |
|
|
797 /* adjust expcnt for digit in front of decimal */ |
|
|
798 --expcnt; |
|
|
799 } |
|
|
800 /* until first fractional digit, decrement exponent */ |
|
|
801 else if (fract) { |
|
|
802 /* adjust expcnt for digit in front of decimal */ |
|
|
803 for (expcnt = -1;; --expcnt) { |
|
|
804 fract = modf(fract * 10, &tmp); |
|
|
805 if (tmp) |
|
|
806 break; |
|
|
807 } |
|
|
808 *t++ = to_char((int)tmp); |
|
|
809 if (prec || flags&ALT) |
|
|
810 *t++ = '.'; |
|
|
811 } |
|
|
812 else { |
|
|
813 *t++ = '0'; |
|
|
814 if (prec || flags&ALT) |
|
|
815 *t++ = '.'; |
|
|
816 } |
|
|
817 /* if requires more precision and some fraction left */ |
|
|
818 if (fract) { |
|
|
819 if (prec) |
|
|
820 do { |
|
|
821 fract = modf(fract * 10, &tmp); |
|
|
822 *t++ = to_char((int)tmp); |
|
|
823 } while (--prec && fract); |
|
|
824 if (fract) |
|
|
825 startp = round(fract, &expcnt, startp, |
|
|
826 t - 1, (char)0, signp); |
|
|
827 } |
|
|
828 /* if requires more precision */ |
|
|
829 for (; prec--; *t++ = '0'); |
|
|
830 |
|
|
831 /* unless alternate flag, trim any g/G format trailing 0's */ |
|
|
832 if (gformat && !(flags&ALT)) { |
|
|
833 while (t > startp && *--t == '0'); |
|
|
834 if (*t == '.') |
|
|
835 --t; |
|
|
836 ++t; |
|
|
837 } |
|
|
838 t = exponent(t, expcnt, fmtch); |
|
|
839 break; |
|
|
840 case 'g': |
|
|
841 case 'G': |
|
|
842 /* a precision of 0 is treated as a precision of 1. */ |
|
|
843 if (!prec) |
|
|
844 ++prec; |
|
|
845 /* |
|
|
846 * ``The style used depends on the value converted; style e |
|
|
847 * will be used only if the exponent resulting from the |
|
|
848 * conversion is less than -4 or greater than the precision.'' |
|
|
849 * -- ANSI X3J11 |
|
|
850 */ |
|
|
851 if (expcnt > prec || (!expcnt && fract && fract < .0001)) { |
|
|
852 /* |
|
|
853 * g/G format counts "significant digits, not digits of |
|
|
854 * precision; for the e/E format, this just causes an |
|
|
855 * off-by-one problem, i.e. g/G considers the digit |
|
|
856 * before the decimal point significant and e/E doesn't |
|
|
857 * count it as precision. |
|
|
858 */ |
|
|
859 --prec; |
|
|
860 fmtch -= 2; /* G->E, g->e */ |
|
|
861 gformat = 1; |
|
|
862 goto eformat; |
|
|
863 } |
|
|
864 /* |
|
|
865 * reverse integer into beginning of buffer, |
|
|
866 * note, decrement precision |
|
|
867 */ |
|
|
868 if (expcnt) |
|
|
869 for (; ++p < endp; *t++ = *p, --prec); |
|
|
870 else |
|
|
871 *t++ = '0'; |
|
|
872 /* |
|
|
873 * if precision required or alternate flag set, add in a |
|
|
874 * decimal point. If no digits yet, add in leading 0. |
|
|
875 */ |
|
|
876 if (prec || flags&ALT) { |
|
|
877 dotrim = 1; |
|
|
878 *t++ = '.'; |
|
|
879 } |
|
|
880 else |
|
|
881 dotrim = 0; |
|
|
882 /* if requires more precision and some fraction left */ |
|
|
883 if (fract) { |
|
|
884 if (prec) { |
|
|
885 do { |
|
|
886 fract = modf(fract * 10, &tmp); |
|
|
887 *t++ = to_char((int)tmp); |
|
|
888 } while(!tmp); |
|
|
889 while (--prec && fract) { |
|
|
890 fract = modf(fract * 10, &tmp); |
|
|
891 *t++ = to_char((int)tmp); |
|
|
892 } |
|
|
893 } |
|
|
894 if (fract) |
|
|
895 startp = round(fract, (int *)NULL, startp, |
|
|
896 t - 1, (char)0, signp); |
|
|
897 } |
|
|
898 /* alternate format, adds 0's for precision, else trim 0's */ |
|
|
899 if (flags&ALT) |
|
|
900 for (; prec--; *t++ = '0'); |
|
|
901 else if (dotrim) { |
|
|
902 while (t > startp && *--t == '0'); |
|
|
903 if (*t != '.') |
|
|
904 ++t; |
|
|
905 } |
|
|
906 } |
|
|
907 return (t - startp); |
|
|
908 } // cvt() |
|
|
909 |
|
|
910 #endif // ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT |
|
|
911 |
|
|
912 #endif // if defined(CYGPKG_LIBC) && defined(CYGPKG_LIBC_STDIO) |
|
|
913 |
|
|
914 // EOF vfnprintf.cxx |