1 /* $Id$ */
2
3 /*-
4 * Copyright (c) 1990 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Chris Torek.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 /*
40 *
41 * Copyright (C) 1998 David Mazieres (dm@uun.org)
42 *
43 * This program is free software; you can redistribute it and/or
44 * modify it under the terms of the GNU General Public License as
45 * published by the Free Software Foundation; either version 2, or (at
46 * your option) any later version.
47 *
48 * This program is distributed in the hope that it will be useful, but
49 * WITHOUT ANY WARRANTY; without even the implied warranty of
50 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
51 * General Public License for more details.
52 *
53 * You should have received a copy of the GNU General Public License
54 * along with this program; if not, write to the Free Software
55 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
56 * USA
57 *
58 */
59
60 #undef FLOATING_POINT
61
62 #include "suio++.h"
63
64 #ifdef FLOATING_POINT
65 #include <locale.h>
66 #include <math.h>
67
68 /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
69 #define MAXEXP 308
70 /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
71 #define MAXFRACT 39
72
73 #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
74 #define DEFPREC 6
75
76 static char *cvt (double, int, int, char *, int *, int, int *);
77 static int exponent (char *, int, int);
78
79 #else /* ! FLOATING_POINT */
80
81 #define BUF 40
82
83 #endif /* ! FLOATING_POINT */
84
85
86 /*
87 * Macros for converting digits to letters and vice versa
88 */
89 #define to_digit(c) ((c) - '0')
90 #define is_digit(c) ((unsigned)to_digit(c) <= 9)
91 #define to_char(n) ((n) + '0')
92
93 /*
94 * Flags used during conversion.
95 */
96 #define ALT 0x001 /* alternate form */
97 #define HEXPREFIX 0x002 /* add 0x or 0X prefix */
98 #define LADJUST 0x004 /* left adjustment */
99 #define LONGDBL 0x008 /* long double; unimplemented */
100 #define LONGINT 0x010 /* long integer */
101 #define QUADINT 0x020 /* quad integer */
102 #define SHORTINT 0x040 /* short integer */
103 #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
104 #define FPT 0x100 /* Floating point number */
105 #define NOCOPY 0x200 /* String contents doesn't have to be copied */
106
107 void
108 #ifndef DSPRINTF_DEBUG
suio_vuprintf(struct suio * uio,const char * _fmt,va_list ap)109 suio_vuprintf (struct suio *uio, const char *_fmt, va_list ap)
110 #else /* DSPRINTF_DEBUG */
111 __suio_vuprintf (const char *line, struct suio *uio,
112 const char *_fmt, va_list ap)
113 #endif /* DSPRINTF_DEBUG */
114 {
115 char *fmt = (char *) _fmt;
116 register int ch; /* character from fmt */
117 register int n, m; /* handy integers (short term usage) */
118 register char *cp; /* handy char pointer (short term usage) */
119 register int flags; /* flags as above */
120 int width; /* width from format (%8d), or 0 */
121 int prec; /* precision from format (%.3d), or -1 */
122 char sign; /* sign prefix (' ', '+', '-', or \0) */
123 #ifdef FLOATING_POINT
124 char *decimal_point = ".";
125 char softsign; /* temporary negative sign for floats */
126 double _double = 0.0; /* double precision arguments %[eEfgG] */
127 int expt; /* integer value of exponent */
128 int expsize = 0; /* character count for expstr */
129 int ndig; /* actual number of digits returned by cvt */
130 char expstr[7]; /* buffer for exponent string */
131 #endif
132
133 #define quad_t int64_t
134 #define u_quad_t u_int64_t
135 u_quad_t _uquad; /* integer arguments %[diouxX] */
136 enum {
137 OCT, DEC, HEX
138 } base; /* base for [diouxX] conversion */
139 int dprec; /* a copy of prec if [diouxX], 0 otherwise */
140 int realsz; /* field size expanded by dprec */
141 int size; /* size of converted field or string */
142 const char *xdigs = ""; /* digits for [xX] conversion */
143
144 char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
145 char ox[2]; /* space for 0x hex-prefix */
146
147 /*
148 * To extend shorts properly, we need both signed and unsigned
149 * argument extraction methods.
150 */
151 #define SARG() \
152 (flags&QUADINT ? va_arg(ap, quad_t) : \
153 flags&LONGINT ? va_arg(ap, long) : \
154 flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
155 (long)va_arg(ap, int))
156 #define UARG() \
157 (flags&QUADINT ? va_arg(ap, u_quad_t) : \
158 flags&LONGINT ? va_arg(ap, u_long) : \
159 flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
160 (u_long)va_arg(ap, u_int))
161
162 /*
163 * Scan the format for conversions (`%' character).
164 */
165 for (;;) {
166 cp = fmt;
167 while (*fmt > 0) {
168 if (*fmt == '%')
169 break;
170 fmt++;
171 }
172 if ((m = fmt - cp) != 0)
173 suio_print (uio, cp, m);
174 if (!*fmt)
175 goto done;
176 fmt++; /* skip over '%' */
177
178 flags = 0;
179 dprec = 0;
180 width = 0;
181 prec = -1;
182 sign = '\0';
183
184 rflag:
185 ch = *fmt++;
186 reswitch:
187 switch (ch) {
188 case ' ':
189 /*
190 * ``If the space and + flags both appear, the space
191 * flag will be ignored.''
192 * -- ANSI X3J11
193 */
194 if (!sign)
195 sign = ' ';
196 goto rflag;
197 case '#':
198 flags |= ALT;
199 goto rflag;
200 case '*':
201 /*
202 * ``A negative field width argument is taken as a
203 * - flag followed by a positive field width.''
204 * -- ANSI X3J11
205 * They don't exclude field widths read from args.
206 */
207 if ((width = va_arg (ap, int)) >= 0)
208 goto rflag;
209 width = -width;
210 /* FALLTHROUGH */
211 case '-':
212 flags |= LADJUST;
213 goto rflag;
214 case '+':
215 sign = '+';
216 goto rflag;
217 case '.':
218 if ((ch = *fmt++) == '*') {
219 n = va_arg (ap, int);
220 prec = n < 0 ? -1 : n;
221 goto rflag;
222 }
223 n = 0;
224 while (is_digit (ch)) {
225 n = 10 * n + to_digit (ch);
226 ch = *fmt++;
227 }
228 prec = n < 0 ? -1 : n;
229 goto reswitch;
230 case '0':
231 /*
232 * ``Note that 0 is taken as a flag, not as the
233 * beginning of a field width.''
234 * -- ANSI X3J11
235 */
236 flags |= ZEROPAD;
237 goto rflag;
238 case '1':
239 case '2':
240 case '3':
241 case '4':
242 case '5':
243 case '6':
244 case '7':
245 case '8':
246 case '9':
247 n = 0;
248 do {
249 n = 10 * n + to_digit (ch);
250 ch = *fmt++;
251 } while (is_digit (ch));
252 width = n;
253 goto reswitch;
254 #ifdef FLOATING_POINT
255 case 'L':
256 flags |= LONGDBL;
257 goto rflag;
258 #endif
259 case 'h':
260 flags |= SHORTINT;
261 goto rflag;
262 case 'l':
263 if (*fmt == 'l') {
264 fmt++;
265 flags |= QUADINT;
266 }
267 else {
268 flags |= LONGINT;
269 }
270 goto rflag;
271 case 'q':
272 flags |= QUADINT;
273 goto rflag;
274 case 'c':
275 *(cp = buf) = va_arg (ap, int);
276 size = 1;
277 sign = '\0';
278 break;
279 case 'D':
280 flags |= LONGINT;
281 /*FALLTHROUGH */
282 case 'd':
283 case 'i':
284 _uquad = SARG ();
285 if ((quad_t) _uquad < 0) {
286 _uquad = -_uquad;
287 sign = '-';
288 }
289 base = DEC;
290 goto number;
291 #ifdef FLOATING_POINT
292 case 'e':
293 case 'E':
294 case 'f':
295 case 'g':
296 case 'G':
297 if (prec == -1) {
298 prec = DEFPREC;
299 }
300 else if ((ch == 'g' || ch == 'G') && prec == 0) {
301 prec = 1;
302 }
303
304 if (flags & LONGDBL) {
305 _double = (double) va_arg (ap, long double);
306 }
307 else {
308 _double = va_arg (ap, double);
309 }
310
311 /* do this before tricky precision changes */
312 if (isinf (_double)) {
313 if (_double < 0)
314 sign = '-';
315 cp = "Inf";
316 size = 3;
317 break;
318 }
319 if (isnan (_double)) {
320 cp = "NaN";
321 size = 3;
322 break;
323 }
324
325 flags |= FPT;
326 cp = cvt (_double, prec, flags, &softsign,
327 &expt, ch, &ndig);
328 if (ch == 'g' || ch == 'G') {
329 if (expt <= -4 || expt > prec)
330 ch = (ch == 'g') ? 'e' : 'E';
331 else
332 ch = 'g';
333 }
334 if (ch <= 'e') { /* 'e' or 'E' fmt */
335 --expt;
336 expsize = exponent (expstr, expt, ch);
337 size = expsize + ndig;
338 if (ndig > 1 || flags & ALT)
339 ++size;
340 }
341 else if (ch == 'f') { /* f fmt */
342 if (expt > 0) {
343 size = expt;
344 if (prec || flags & ALT)
345 size += prec + 1;
346 }
347 else /* "0.X" */
348 size = prec + 2;
349 }
350 else if (expt >= ndig) { /* fixed g fmt */
351 size = expt;
352 if (flags & ALT)
353 ++size;
354 }
355 else
356 size = ndig + (expt > 0 ?
357 1 : 2 - expt);
358
359 if (softsign)
360 sign = '-';
361 break;
362 #endif /* FLOATING_POINT */
363 #if 0
364 /* Disable "%n", as we don't need it--you can always print to a
365 * suio and use suio::resid () to get the buffer size. */
366 case 'n':
367 if (flags & QUADINT)
368 *va_arg (ap, quad_t *) = uio->resid ();
369 else if (flags & LONGINT)
370 *va_arg (ap, long *) = uio->resid ();
371 else if (flags & SHORTINT)
372 *va_arg (ap, short *) = uio->resid ();
373 else
374 *va_arg (ap, int *) = uio->resid ();
375 continue; /* no output */
376 #endif
377 case 'O':
378 flags |= LONGINT;
379 /*FALLTHROUGH */
380 case 'o':
381 _uquad = UARG ();
382 base = OCT;
383 goto nosign;
384 case 'p':
385 /*
386 * ``The argument shall be a pointer to void. The
387 * value of the pointer is converted to a sequence
388 * of printable characters, in an implementation-
389 * defined manner.''
390 * -- ANSI X3J11
391 */
392 /* NOSTRICT */
393 _uquad = (u_long) va_arg (ap, void *);
394 base = HEX;
395 xdigs = "0123456789abcdef";
396 flags |= HEXPREFIX;
397 ch = 'x';
398 goto nosign;
399 case 'm':
400 cp = strerror (errno);
401 goto gotcp;
402 case 's':
403 cp = va_arg (ap, char *);
404 gotcp:
405 if (cp == NULL)
406 panic ("suio_vuprintf: NULL pointer\n");
407 //cp = "(null)";
408 if (prec >= 0) {
409 /*
410 * can't use strlen; can only look for the
411 * NUL in the first `prec' characters, and
412 * strlen() will go further.
413 */
414 char *p = (char *) memchr (cp, 0, prec);
415
416 if (p != NULL) {
417 size = p - cp;
418 if (size > prec)
419 size = prec;
420 }
421 else
422 size = prec;
423 }
424 else
425 size = strlen (cp);
426 sign = '\0';
427 if (!(flags & ALT))
428 flags |= NOCOPY;
429 break;
430 case 'U':
431 flags |= LONGINT;
432 /*FALLTHROUGH */
433 case 'u':
434 _uquad = UARG ();
435 base = DEC;
436 goto nosign;
437 case 'X':
438 xdigs = "0123456789ABCDEF";
439 goto hex;
440 case 'x':
441 xdigs = "0123456789abcdef";
442 hex:
443 _uquad = UARG ();
444 base = HEX;
445 /* leading 0x/X only if non-zero */
446 if (flags & ALT && _uquad != 0)
447 flags |= HEXPREFIX;
448
449 /* unsigned conversions */
450 nosign:
451 sign = '\0';
452 /*
453 * ``... diouXx conversions ... if a precision is
454 * specified, the 0 flag will be ignored.''
455 * -- ANSI X3J11
456 */
457 number:
458 if ((dprec = prec) >= 0)
459 flags &= ~ZEROPAD;
460
461 /*
462 * ``The result of converting a zero value with an
463 * explicit precision of zero is no characters.''
464 * -- ANSI X3J11
465 */
466 cp = buf + BUF;
467 if (_uquad != 0 || prec != 0) {
468 /*
469 * Unsigned mod is hard, and unsigned mod
470 * by a constant is easier than that by
471 * a variable; hence this switch.
472 */
473 switch (base) {
474 case OCT:
475 do {
476 *--cp = to_char (_uquad & 7);
477 _uquad >>= 3;
478 } while (_uquad);
479 /* handle octal leading 0 */
480 if (flags & ALT && *cp != '0')
481 *--cp = '0';
482 break;
483
484 case DEC:
485 /* many numbers are 1 digit */
486 while (_uquad >= 10) {
487 *--cp = to_char (_uquad % 10);
488 _uquad /= 10;
489 }
490 *--cp = to_char (_uquad);
491 break;
492
493 case HEX:
494 do {
495 *--cp = xdigs[_uquad & 15];
496 _uquad >>= 4;
497 } while (_uquad);
498 break;
499
500 default:
501 cp = const_cast<char *> ("bug in vfprintf: bad base");
502 size = strlen (cp);
503 goto skipsize;
504 }
505 }
506 size = buf + BUF - cp;
507 skipsize:
508 break;
509 default: /* "%?" prints ?, unless ? is NUL */
510 if (ch == '\0')
511 goto done;
512 /* pretend it was %c with argument ch */
513 cp = buf;
514 *cp = ch;
515 size = 1;
516 sign = '\0';
517 break;
518 }
519
520 /*
521 * All reasonable formats wind up here. At this point, `cp'
522 * points to a string which (if not flags&LADJUST) should be
523 * padded out to `width' places. If flags&ZEROPAD, it should
524 * first be prefixed by any sign or other prefix; otherwise,
525 * it should be blank padded before the prefix is emitted.
526 * After any left-hand padding and prefixing, emit zeroes
527 * required by a decimal [diouxX] precision, then print the
528 * string proper, then emit zeroes required by any leftover
529 * floating precision; finally, if LADJUST, pad with blanks.
530 *
531 * Compute actual size, so we know how much to pad.
532 * size excludes decimal prec; realsz includes it.
533 */
534 realsz = dprec > size ? dprec : size;
535 if (sign)
536 realsz++;
537 else if (flags & HEXPREFIX)
538 realsz += 2;
539
540 /* right-adjusting blank padding */
541 if ((flags & (LADJUST | ZEROPAD)) == 0)
542 suio_fill (uio, ' ', width - realsz);
543
544 /* prefix */
545 if (sign) {
546 suio_copy (uio, &sign, 1);
547 }
548 else if (flags & HEXPREFIX) {
549 ox[0] = '0';
550 ox[1] = ch;
551 suio_copy (uio, ox, 2);
552 }
553
554 /* right-adjusting zero padding */
555 if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD)
556 suio_fill (uio, '0', width - realsz);
557
558 /* leading zeroes from decimal precision */
559 suio_fill (uio, '0', dprec - size);
560
561 /* the string or number proper */
562 #ifdef FLOATING_POINT
563 if ((flags & FPT) == 0) {
564 if (flags & NOCOPY)
565 __suio_printcheck (line, uio, cp, size);
566 else
567 suio_copy (uio, cp, size);
568 }
569 else { /* glue together f_p fragments */
570 if (ch >= 'f') { /* 'f' or 'g' */
571 if (_double == 0) {
572 /* kludge for __dtoa irregularity */
573 suio_print (uio, "0", 1);
574 if (expt < ndig || (flags & ALT) != 0) {
575 suio_print (uio, decimal_point, 1);
576 suio_fill (uio, '0', ndig - 1);
577 }
578 }
579 else if (expt <= 0) {
580 suio_print (uio, "0", 1);
581 suio_print (uio, decimal_point, 1);
582 suio_fill (uio, '0', -expt);
583 suio_copy (uio, cp, ndig);
584 }
585 else if (expt >= ndig) {
586 suio_copy (uio, cp, ndig);
587 suio_fill (uio, '0', expt - ndig);
588 if (flags & ALT)
589 suio_print (uio, ".", 1);
590 }
591 else {
592 suio_copy (uio, cp, expt);
593 cp += expt;
594 suio_print (uio, ".", 1);
595 suio_copy (uio, cp, ndig - expt);
596 }
597 }
598 else { /* 'e' or 'E' */
599 if (ndig > 1 || flags & ALT) {
600 ox[0] = *cp++;
601 ox[1] = '.';
602 suio_copy (uio, ox, 2);
603 if (_double || (flags & ALT) == 0) {
604 suio_copy (uio, cp, ndig - 1);
605 }
606 else /* 0.[0..] */
607 /* __dtoa irregularity */
608 suio_fill (uio, '0', ndig - 1);
609 }
610 else /* XeYYY */
611 suio_copy (uio, cp, 1);
612 suio_copy (uio, expstr, expsize);
613 }
614 }
615 #else
616 if (flags & NOCOPY)
617 #ifndef DSPRINTF_DEBUG
618 suio_print (uio, cp, size);
619 #else /* DSPRINTF_DEBUG */
620 __suio_printcheck (line, uio, cp, size);
621 #endif /* DSPRINTF_DEBUG */
622 else
623 suio_copy (uio, cp, size);
624 #endif
625 /* left-adjusting padding (always blank) */
626 if (flags & LADJUST)
627 suio_fill (uio, ' ', width - realsz);
628 }
629 done:
630 return;
631 }
632
633 #ifdef FLOATING_POINT
634
635 extern char *__dtoa (double, int, int, int *, int *, char **);
636
637 static char *
cvt(value,ndigits,flags,sign,decpt,ch,length)638 cvt (value, ndigits, flags, sign, decpt, ch, length)
639 double value;
640 int ndigits, flags, *decpt, ch, *length;
641 char *sign;
642 {
643 int mode, dsgn;
644 char *digits, *bp, *rve;
645
646 if (ch == 'f') {
647 mode = 3; /* ndigits after the decimal point */
648 }
649 else {
650 /* To obtain ndigits after the decimal point for the 'e'
651 * and 'E' formats, round to ndigits + 1 significant
652 * figures.
653 */
654 if (ch == 'e' || ch == 'E') {
655 ndigits++;
656 }
657 mode = 2; /* ndigits significant digits */
658 }
659
660 if (value < 0) {
661 value = -value;
662 *sign = '-';
663 }
664 else
665 *sign = '\000';
666 digits = __dtoa (value, mode, ndigits, decpt, &dsgn, &rve);
667 if ((ch != 'g' && ch != 'G') || flags & ALT) { /* Print trailing zeros */
668 bp = digits + ndigits;
669 if (ch == 'f') {
670 if (*digits == '0' && value)
671 *decpt = -ndigits + 1;
672 bp += *decpt;
673 }
674 if (value == 0) /* kludge for __dtoa irregularity */
675 rve = bp;
676 while (rve < bp)
677 *rve++ = '0';
678 }
679 *length = rve - digits;
680 return (digits);
681 }
682
683 static int
exponent(p0,exp,fmtch)684 exponent (p0, exp, fmtch)
685 char *p0;
686 int exp, fmtch;
687 {
688 register char *p, *t;
689 char expbuf[MAXEXP];
690
691 p = p0;
692 *p++ = fmtch;
693 if (exp < 0) {
694 exp = -exp;
695 *p++ = '-';
696 }
697 else
698 *p++ = '+';
699 t = expbuf + MAXEXP;
700 if (exp > 9) {
701 do {
702 *--t = to_char (exp % 10);
703 } while ((exp /= 10) > 9);
704 *--t = to_char (exp);
705 for (; t < expbuf + MAXEXP; *p++ = *t++);
706 }
707 else {
708 *p++ = '0';
709 *p++ = to_char (exp);
710 }
711 return (p - p0);
712 }
713 #endif /* FLOATING_POINT */
714
715 void
716 #ifndef DSPRINTF_DEBUG
suio_uprintf(struct suio * uio,const char * fmt,...)717 suio_uprintf (struct suio *uio, const char *fmt, ...)
718 #else /* DSPRINTF_DEBUG */
719 __suio_uprintf (const char *line, struct suio *uio, const char *fmt, ...)
720 #endif /* DSPRINTF_DEBUG */
721 {
722 va_list ap;
723 va_start (ap, fmt);
724 #ifndef DSPRINTF_DEBUG
725 suio_vuprintf (uio, fmt, ap);
726 #else /* DSPRINTF_DEBUG */
727 __suio_vuprintf (line, uio, fmt, ap);
728 #endif /* DSPRINTF_DEBUG */
729 va_end (ap);
730 }
731