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