|
|
1.1 root 1: /* ident "@(#)ctrans:src/doprint.c 1.2" */
2: /*
3: ************* obviously, have to do something here!!!
4:
5: * Copyright (c) 1988 Regents of the University of California.
6: * All rights reserved.
7: *
8: * Redistribution and use in source and binary forms are permitted
9: * provided that the above copyright notice and this paragraph are
10: * duplicated in all such forms and that any documentation,
11: * advertising materials, and other materials related to such
12: * distribution and use acknowledge that the software was developed
13: * by the University of California, Berkeley. The name of the
14: * University may not be used to endorse or promote products derived
15: * from this software without specific prior written permission.
16: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18: * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19: */
20:
21: /*
22: $Header: /usr2/odi/lib.src/RCS/doprint.C,v 1.3 89/09/19 12:00:34 benson Exp $
23:
24: Copyright (c) 1989 by Object Design, Inc., Burlington, Mass.
25: All rights reserved.
26:
27: */
28:
29: #include <sys/types.h>
30: #include <stdarg.h>
31: #include <ctype.h>
32: #include <iostream.h>
33: #include <strstream.h>
34: #include <memory.h>
35: #include <math.h>
36: #include <string.h>
37: #include "print_self.h"
38:
39: /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
40: #define MAXEXP 308
41: /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
42: #define MAXFRACT 39
43:
44: #define DEFPREC 6
45:
46: #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
47:
48: #define PUTC(ch) (void) fp.put(ch)
49:
50: #define ARG() \
51: _ulong = flags&LONGINT ? va_arg(argp, long) : \
52: flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
53:
54: #define todigit(c) ((c) - '0')
55: #define tochar(n) ((n) + '0')
56:
57: #define LONGINT 0x01 /* long integer */
58: #define LONGDBL 0x02 /* long double; unimplemented */
59: #define SHORTINT 0x04 /* short integer */
60: #define ALT 0x08 /* alternate form */
61: #define LADJUST 0x10 /* left adjustment */
62: #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */
63: #define HEXPREFIX 0x40 /* add 0x or 0X prefix */
64:
65:
66: static char * exponent(char * p, int exp, u_char fmtch);
67: static char * round(double fract,
68: int * exp,
69: char * start,
70: char * end,
71: char ch,
72: char * signp);
73:
74:
75:
76: static
77: cvt(double number,
78: register int prec,
79: int flags,
80: char * signp,
81: u_char fmtch,
82: char * startp,
83: char * endp)
84: {
85: register char *p, *t;
86: register double fract;
87: int dotrim, expcnt, gformat;
88: double integer, tmp;
89:
90: dotrim = expcnt = gformat = 0;
91: fract = modf(number, &integer);
92:
93: /* get an extra slot for rounding. */
94: t = ++startp;
95:
96: /*
97: * get integer portion of number; put into the end of the buffer; the
98: * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
99: */
100: for (p = endp - 1; integer; ++expcnt) {
101: tmp = modf(integer / 10, &integer);
102: *p-- = tochar((int)((tmp + .01) * 10));
103: }
104: switch(fmtch) {
105: case 'f':
106: /* reverse integer into beginning of buffer */
107: if (expcnt)
108: for (; ++p < endp; *t++ = *p);
109: else
110: *t++ = '0';
111: /*
112: * if precision required or alternate flag set, add in a
113: * decimal point.
114: */
115: if (prec || flags&ALT)
116: *t++ = '.';
117: /* if requires more precision and some fraction left */
118: if (fract) {
119: if (prec)
120: do {
121: fract = modf(fract * 10, &tmp);
122: *t++ = tochar((int)tmp);
123: } while (--prec && fract);
124: if (fract)
125: startp = round(fract, (int *)NULL, startp,
126: t - 1, (char)0, signp);
127: }
128: for (; prec--; *t++ = '0');
129: break;
130: case 'e':
131: case 'E':
132: eformat: if (expcnt) {
133: *t++ = *++p;
134: if (prec || flags&ALT)
135: *t++ = '.';
136: /* if requires more precision and some integer left */
137: for (; prec && ++p < endp; --prec)
138: *t++ = *p;
139: /*
140: * if done precision and more of the integer component,
141: * round using it; adjust fract so we don't re-round
142: * later.
143: */
144: if (!prec && ++p < endp) {
145: fract = 0;
146: startp = round((double)0, &expcnt, startp,
147: t - 1, *p, signp);
148: }
149: /* adjust expcnt for digit in front of decimal */
150: --expcnt;
151: }
152: /* until first fractional digit, decrement exponent */
153: else if (fract) {
154: /* adjust expcnt for digit in front of decimal */
155: for (expcnt = -1;; --expcnt) {
156: fract = modf(fract * 10, &tmp);
157: if (tmp)
158: break;
159: }
160: *t++ = tochar((int)tmp);
161: if (prec || flags&ALT)
162: *t++ = '.';
163: }
164: else {
165: *t++ = '0';
166: if (prec || flags&ALT)
167: *t++ = '.';
168: }
169: /* if requires more precision and some fraction left */
170: if (fract) {
171: if (prec)
172: do {
173: fract = modf(fract * 10, &tmp);
174: *t++ = tochar((int)tmp);
175: } while (--prec && fract);
176: if (fract)
177: startp = round(fract, &expcnt, startp,
178: t - 1, (char)0, signp);
179: }
180: /* if requires more precision */
181: for (; prec--; *t++ = '0');
182:
183: /* unless alternate flag, trim any g/G format trailing 0's */
184: if (gformat && !(flags&ALT)) {
185: while (t > startp && *--t == '0');
186: if (*t == '.')
187: --t;
188: ++t;
189: }
190: t = exponent(t, expcnt, fmtch);
191: break;
192: case 'g':
193: case 'G':
194: /* a precision of 0 is treated as a precision of 1. */
195: if (!prec)
196: ++prec;
197: /*
198: * ``The style used depends on the value converted; style e
199: * will be used only if the exponent resulting from the
200: * conversion is less than -4 or greater than the precision.''
201: * -- ANSI X3J11
202: */
203: if (expcnt > prec || !expcnt && fract && fract < .0001) {
204: /*
205: * g/G format counts "significant digits, not digits of
206: * precision; for the e/E format, this just causes an
207: * off-by-one problem, i.e. g/G considers the digit
208: * before the decimal point significant and e/E doesn't
209: * count it as precision."
210: */
211: --prec;
212: fmtch -= 2; /* G->E, g->e */
213: gformat = 1;
214: goto eformat;
215: }
216: /*
217: * reverse integer into beginning of buffer,
218: * note, decrement precision
219: */
220: if (expcnt)
221: for (; ++p < endp; *t++ = *p, --prec);
222: else
223: *t++ = '0';
224: /*
225: * if precision required or alternate flag set, add in a
226: * decimal point. If no digits yet, add in leading 0.
227: */
228: if (prec || flags&ALT) {
229: dotrim = 1;
230: *t++ = '.';
231: }
232: else
233: dotrim = 0;
234: /* if requires more precision and some fraction left */
235: if (fract) {
236: if (prec) {
237: do {
238: fract = modf(fract * 10, &tmp);
239: *t++ = tochar((int)tmp);
240: } while(!tmp);
241: while (--prec && fract) {
242: fract = modf(fract * 10, &tmp);
243: *t++ = tochar((int)tmp);
244: }
245: }
246: if (fract)
247: startp = round(fract, (int *)NULL, startp,
248: t - 1, (char)0, signp);
249: }
250: /* alternate format, adds 0's for precision, else trim 0's */
251: if (flags&ALT)
252: for (; prec--; *t++ = '0');
253: else if (dotrim) {
254: while (t > startp && *--t == '0');
255: if (*t != '.')
256: ++t;
257: }
258: }
259: return(t - startp);
260: }
261:
262: static char * round(double fract,
263: int * exp,
264: char * start,
265: char * end,
266: char ch,
267: char * signp)
268: {
269: double tmp;
270:
271: if (fract)
272: (void)modf(fract * 10, &tmp);
273: else
274: tmp = todigit(ch);
275: if (tmp > 4)
276: for (;; --end) {
277: if (*end == '.')
278: --end;
279: if (++*end <= '9')
280: break;
281: *end = '0';
282: if (end == start) {
283: if (exp) { /* e/E; increment exponent */
284: *end = '1';
285: ++*exp;
286: }
287: else { /* f; add extra digit */
288: *--end = '1';
289: --start;
290: }
291: break;
292: }
293: }
294: /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
295: else if (*signp == '-')
296: for (;; --end) {
297: if (*end == '.')
298: --end;
299: if (*end != '0')
300: break;
301: if (end == start)
302: *signp = 0;
303: }
304: return(start);
305: }
306:
307: static char * exponent(char * p, int exp, u_char fmtch)
308: {
309: register char *t;
310: char expbuf[MAXEXP];
311:
312: *p++ = fmtch;
313: if (exp < 0) {
314: exp = -exp;
315: *p++ = '-';
316: }
317: else
318: *p++ = '+';
319: t = expbuf + MAXEXP;
320: if (exp > 9) {
321: do {
322: *--t = tochar(exp % 10);
323: } while ((exp /= 10) > 9);
324: *--t = tochar(exp);
325: for (; t < expbuf + MAXEXP; *p++ = *t++);
326: }
327: else {
328: *p++ = '0';
329: *p++ = tochar(exp);
330: }
331: return(p);
332: }
333:
334: int vostream_printf(const char *fmt0, va_list argp, ostream& fp)
335: {
336: register const u_char *fmt; /* format string */
337: register int ch; /* character from fmt */
338: register int cnt; /* return value accumulator */
339: register int n; /* random handy integer */
340: register char *t; /* buffer pointer */
341: double _double; /* double precision arguments %[eEfgG] */
342: u_long _ulong; /* integer arguments %[diouxX] */
343: int base; /* base for [diouxX] conversion */
344: int dprec; /* decimal precision in [diouxX] */
345: int fieldsz; /* field size expanded by sign, etc */
346: int flags; /* flags as above */
347: int fpprec; /* `extra' floating precision in [eEfgG] */
348: int prec; /* precision from format (%.3d), or -1 */
349: int realsz; /* field size expanded by decimal precision */
350: int size; /* size of converted field or string */
351: int width; /* width from format (%8d), or 0 */
352: char sign; /* sign prefix (' ', '+', '-', or \0) */
353: char softsign; /* temporary negative sign for floats */
354: char *digs; /* digits for [diouxX] conversion */
355: char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
356:
357: fmt = (const u_char *)fmt0;
358: digs = "0123456789abcdef";
359: for (cnt = 0;; ++fmt) {
360: for (;(ch = *fmt) && ch != '%'; ++cnt, ++fmt)
361: PUTC(ch);
362: if (!ch)return (cnt);
363:
364: flags = 0; dprec = 0; fpprec = 0; width = 0;
365: prec = -1;
366: sign = '\0';
367:
368: rflag: switch (*++fmt) {
369: case ' ':
370: /*
371: * ``If the space and + flags both appear, the space
372: * flag will be ignored.''
373: * -- ANSI X3J11
374: */
375: if (!sign)
376: sign = ' ';
377: goto rflag;
378: case '#':
379: flags |= ALT;
380: goto rflag;
381: case '*':
382: /*
383: * ``A negative field width argument is taken as a
384: * - flag followed by a positive field width.''
385: * -- ANSI X3J11
386: * They don't exclude field widths read from args.
387: */
388: if ((width = va_arg(argp, int)) >= 0)
389: goto rflag;
390: width = -width;
391: /* FALLTHROUGH */
392: case '-':
393: flags |= LADJUST;
394: goto rflag;
395: case '+':
396: sign = '+';
397: goto rflag;
398: case '.':
399: if (*++fmt == '*')
400: n = va_arg(argp, int);
401: else {
402: n = 0;
403: while (isascii(*fmt) && isdigit(*fmt))
404: n = 10 * n + todigit(*fmt++);
405: --fmt;
406: }
407: prec = n < 0 ? -1 : n;
408: goto rflag;
409: case '0':
410: /*
411: * ``Note that 0 is taken as a flag, not as the
412: * beginning of a field width.''
413: * -- ANSI X3J11
414: */
415: flags |= ZEROPAD;
416: goto rflag;
417: case '1': case '2': case '3': case '4':
418: case '5': case '6': case '7': case '8': case '9':
419: n = 0;
420: do {
421: n = 10 * n + todigit(*fmt);
422: } while (isascii(*++fmt) && isdigit(*fmt));
423: width = n;
424: --fmt;
425: goto rflag;
426: case 'L':
427: flags |= LONGDBL;
428: goto rflag;
429: case 'h':
430: flags |= SHORTINT;
431: goto rflag;
432: case 'l':
433: flags |= LONGINT;
434: goto rflag;
435: case 'c':
436: *(t = buf) = va_arg(argp, int);
437: size = 1;
438: sign = '\0';
439: goto pforw;
440: case 'D':
441: flags |= LONGINT;
442: /*FALLTHROUGH*/
443: case 'd':
444: case 'i':
445: ARG();
446: if ((long)_ulong < 0) {
447: _ulong = -_ulong;
448: sign = '-';
449: }
450: base = 10;
451: goto number;
452: case 'e':
453: case 'E':
454: case 'f':
455: case 'g':
456: case 'G':
457: _double = va_arg(argp, double);
458: /*
459: * don't do unrealistic precision; just pad it with
460: * zeroes later, so buffer size stays rational.
461: */
462: if (prec > MAXFRACT) {
463: if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
464: fpprec = prec - MAXFRACT;
465: prec = MAXFRACT;
466: }
467: else if (prec == -1)
468: prec = DEFPREC;
469: /*
470: * softsign avoids negative 0 if _double is < 0 and
471: * no significant digits will be shown
472: */
473: if (_double < 0) {
474: softsign = '-';
475: _double = -_double;
476: }
477: else
478: softsign = 0;
479: /*
480: * cvt may have to round up past the "start" of the
481: * buffer, i.e. ``intf("%.2f", (double)9.999);'';
482: * if the first char isn't NULL, it did.
483: */
484: *buf = NULL;
485: size = cvt(_double, prec, flags, &softsign, *fmt, buf,
486: buf + sizeof(buf));
487: if (softsign)
488: sign = '-';
489: t = *buf ? buf : buf + 1;
490: goto pforw;
491: case 'n':
492: if (flags & LONGINT)
493: *va_arg(argp, long *) = cnt;
494: else if (flags & SHORTINT)
495: *va_arg(argp, short *) = cnt;
496: else
497: *va_arg(argp, int *) = cnt;
498: break;
499: case 'O':
500: flags |= LONGINT;
501: /*FALLTHROUGH*/
502: case 'o':
503: ARG();
504: base = 8;
505: goto nosign;
506: case 'T': // Type
507: {
508: streampos cur_pos;
509: streampos new_pos;
510:
511: cur_pos = fp.tellp();
512:
513: _Print_self * obj = va_arg (argp, _Print_self *);
514: obj->print_self (fp);
515:
516: new_pos = fp.tellp();
517:
518: cnt += int (new_pos - cur_pos);
519: }
520: break;
521:
522: case 'p':
523: /*
524: * ``The argument shall be a pointer to void. The
525: * value of the pointer is converted to a sequence
526: * of printable characters, in an implementation-
527: * defined manner.''
528: * -- ANSI X3J11
529: */
530: /* NOSTRICT */
531: _ulong = (u_long)va_arg(argp, void *);
532: base = 16;
533: goto nosign;
534: case 's':
535: if (!(t = va_arg(argp, char *)))
536: t = "(null)";
537: if (prec >= 0) {
538: /*
539: * can't use strlen; can only look for the
540: * NUL in the first `prec' characters, and
541: * strlen() will go further.
542: */
543: char *p;
544:
545: if (p = (char *) memchr(t, 0, prec)) {
546: size = p - t;
547: if (size > prec)
548: size = prec;
549: } else
550: size = prec;
551: } else
552: size = strlen(t);
553: sign = '\0';
554: goto pforw;
555: case 'U':
556: flags |= LONGINT;
557: /*FALLTHROUGH*/
558: case 'u':
559: ARG();
560: base = 10;
561: goto nosign;
562: case 'X':
563: digs = "0123456789ABCDEF";
564: /* FALLTHROUGH */
565: case 'x':
566: ARG();
567: base = 16;
568: /* leading 0x/X only if non-zero */
569: if (flags & ALT && _ulong != 0)
570: flags |= HEXPREFIX;
571:
572: /* unsigned conversions */
573: nosign: sign = '\0';
574: /*
575: * ``... diouXx conversions ... if a precision is
576: * specified, the 0 flag will be ignored.''
577: * -- ANSI X3J11
578: */
579: number: if ((dprec = prec) >= 0)
580: flags &= ~ZEROPAD;
581:
582: /*
583: * ``The result of converting a zero value with an
584: * explicit precision of zero is no characters.''
585: * -- ANSI X3J11
586: */
587: t = buf + BUF;
588: if (_ulong != 0 || prec != 0) {
589: do {
590: *--t = digs[_ulong % base];
591: _ulong /= base;
592: } while (_ulong);
593: digs = "0123456789abcdef";
594: if (flags & ALT && base == 8 && *t != '0')
595: *--t = '0'; /* octal leading 0 */
596: }
597: size = buf + BUF - t;
598:
599: pforw:
600: /*
601: * All reasonable formats wind up here. At this point,
602: * `t' points to a string which (if not flags&LADJUST)
603: * should be padded out to `width' places. If
604: * flags&ZEROPAD, it should first be prefixed by any
605: * sign or other prefix; otherwise, it should be blank
606: * padded before the prefix is emitted. After any
607: * left-hand padding and prefixing, emit zeroes
608: * required by a decimal [diouxX] precision, then print
609: * the string proper, then emit zeroes required by any
610: * leftover floating precision; finally, if LADJUST,
611: * pad with blanks.
612: */
613:
614: /*
615: * compute actual size, so we know how much to pad
616: * fieldsz excludes decimal prec; realsz includes it
617: */
618: fieldsz = size + fpprec;
619: if (sign)
620: fieldsz++;
621: if (flags & HEXPREFIX)
622: fieldsz += 2;
623: realsz = dprec > fieldsz ? dprec : fieldsz;
624:
625: /* right-adjusting blank padding */
626: if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
627: for (n = realsz; n < width; n++)
628: PUTC(' ');
629: /* prefix */
630: if (sign)
631: PUTC(sign);
632: if (flags & HEXPREFIX) {
633: PUTC('0');
634: PUTC((char)*fmt);
635: }
636: /* right-adjusting zero padding */
637: if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
638: for (n = realsz; n < width; n++)
639: PUTC('0');
640: /* leading zeroes from decimal precision */
641: for (n = fieldsz; n < dprec; n++)
642: PUTC('0');
643:
644: /* the string or number proper */
645:
646: fp.write(t, size);
647:
648: /* trailing f.p. zeroes */
649: while (--fpprec >= 0)
650: PUTC('0');
651: /* left-adjusting padding (always blank) */
652: if (flags & LADJUST)
653: for (n = realsz; n < width; n++)
654: PUTC(' ');
655: /* finally, adjust cnt */
656: cnt += width > realsz ? width : realsz;
657: break;
658: case '\0': /* "%?" prints ?, unless ? is NULL */
659: return (cnt);
660: default:
661: PUTC(*fmt);
662: cnt++;
663: }
664: }
665: /* NOTREACHED */
666: }
667:
668: int ostream_printf(ostream& stream, const char * format ...)
669: {
670: int ret;
671:
672: va_list args;
673: va_start(args, format);
674: ret = vostream_printf(format, args, stream);
675: va_end(args);
676: return ret;
677: }
678:
679: // There are intended to be more convienient that stirring up
680: // an strstream by hand.
681:
682: int printf_to_string (char * string, int length, const char * format ...)
683: {
684: ostrstream& stream = ostrstream (string, length, (ios::open_mode)ios::app);
685:
686: va_list args;
687: va_start(args, format);
688:
689: vostream_printf (format, args, stream);
690:
691: va_end(args);
692: stream.str();
693: return stream.pcount();
694: }
695:
696: // caller of this must free() the string that comes back.
697:
698: char * printf_to_alloc_string (const char * format ...)
699: {
700: ostrstream& stream = ostrstream ();
701:
702: va_list args;
703: va_start(args, format);
704:
705: vostream_printf (format, args, stream);
706:
707: va_end(args);
708: return stream.str();
709: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.