xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34331)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 
13 #if defined(LIBC_SCCS) && !defined(lint)
14 static char sccsid[] = "@(#)vfprintf.c	5.27 (Berkeley) 05/19/88";
15 #endif /* LIBC_SCCS and not lint */
16 
17 #include <sys/types.h>
18 #include <varargs.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 
22 /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
23 #define	MAXEXP		308
24 /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
25 #define	MAXFRACT	39
26 
27 #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
28 
29 #define	PUTC(ch)	{++cnt; putc((char)ch, fp);}
30 
31 #define	ARG() \
32 	_ulong = flags&LONGINT ? va_arg(argp, long) : \
33 	    flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
34 
35 #define	todigit(c)	((c) - '0')
36 #define	tochar(n)	((n) + '0')
37 
38 /* have to deal with the negative buffer count kludge */
39 #define	NEGATIVE_COUNT_KLUDGE
40 
41 #define	LONGINT		0x01		/* long integer */
42 #define	LONGDBL		0x02		/* long double; unimplemented */
43 #define	SHORTINT	0x04		/* short integer */
44 #define	ALT		0x08		/* alternate form */
45 #define	LADJUST		0x10		/* left adjustment */
46 
47 _doprnt(fmt0, argp, fp)
48 	u_char *fmt0;
49 	va_list argp;
50 	register FILE *fp;
51 {
52 	register u_char *fmt;
53 	register int ch, cnt, n;
54 	register char *t;
55 	double _double;
56 	u_long _ulong;
57 	int base, flags, fpprec, prec, size, width;
58 	char padc, sign, *digs, buf[BUF], *_cvt();
59 
60 	fmt = fmt0;
61 	digs = "0123456789abcdef";
62 	for (cnt = 0;; ++fmt) {
63 		n = fp->_cnt;
64 		for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
65 			if (--n < 0
66 #ifdef NEGATIVE_COUNT_KLUDGE
67 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
68 #endif
69 			    || ch == '\n' && fp->_flag&_IOLBF) {
70 				fp->_cnt = n;
71 				fp->_ptr = t;
72 				(void)_flsbuf(ch, fp);
73 				n = fp->_cnt;
74 				t = fp->_ptr;
75 			}
76 			else
77 				*t++ = ch;
78 		fp->_cnt = n;
79 		fp->_ptr = t;
80 		if (!ch)
81 			return(cnt);
82 
83 		flags = fpprec = width = 0;
84 		prec = -1;
85 		padc = ' ';
86 		sign = '\0';
87 
88 rflag:		switch (*++fmt) {
89 		case ' ':
90 			sign = ' ';
91 			goto rflag;
92 		case '#':
93 			flags |= ALT;
94 			goto rflag;
95 		case '*':
96 			/*
97 			 * ``A negative field width argument is taken as a
98 			 * - flag followed by a  positive field width.''
99 			 *	-- ANSI X3J11
100 			 * They don't exclude field widths read from args.
101 			 */
102 			if ((width = va_arg(argp, int)) >= 0)
103 				goto rflag;
104 			width = -width;
105 			/*FALLTHROUGH*/
106 		case '-':
107 			flags |= LADJUST;
108 			goto rflag;
109 		case '+':
110 			sign = '+';
111 			goto rflag;
112 		case '.':
113 			if (*++fmt == '*')
114 				n = va_arg(argp, int);
115 			else if (isascii(*fmt) && isdigit(*fmt)) {
116 				n = 0;
117 				do {
118 					n = 10 * n + todigit(*fmt);
119 				} while (isascii(*++fmt) && isdigit(*fmt));
120 				--fmt;
121 			}
122 			else {
123 				--fmt;
124 				prec = 0;
125 				goto rflag;
126 			}
127 			prec = n < 0 ? -1 : n;
128 			goto rflag;
129 		case '0':
130 			padc = '0';
131 			/*FALLTHROUGH*/
132 		case '1': case '2': case '3': case '4':
133 		case '5': case '6': case '7': case '8': case '9':
134 			n = 0;
135 			do {
136 				n = 10 * n + todigit(*fmt);
137 			} while (isascii(*++fmt) && isdigit(*fmt));
138 			width = n;
139 			--fmt;
140 			goto rflag;
141 		case 'L':
142 			flags |= LONGDBL;
143 			goto rflag;
144 		case 'h':
145 			flags |= SHORTINT;
146 			goto rflag;
147 		case 'l':
148 			flags |= LONGINT;
149 			goto rflag;
150 		case 'c':
151 			buf[0] = va_arg(argp, int);
152 			size = 1;
153 			t = buf;
154 			goto pforw;
155 		case 'd':
156 		case 'i':
157 			ARG();
158 			if ((long)_ulong < 0) {
159 				_ulong = -_ulong;
160 				sign = '-';
161 			}
162 			base = 10;
163 			goto number;
164 		case 'e':
165 		case 'E':
166 		case 'f':
167 		case 'g':
168 		case 'G':
169 			_double = va_arg(argp, double);
170 			/*
171 			 * don't bother to do unrealistic precision; just
172 			 * pad it with zeroes later.  This keeps buffer size
173 			 * rational.
174 			 */
175 			if (prec > MAXFRACT) {
176 				fpprec = prec - MAXFRACT;
177 				prec = MAXFRACT;
178 			}
179 			size = _cvt(_double, prec, flags, *fmt, padc, &sign,
180 			    buf, buf + sizeof(buf)) - buf;
181 			t = buf;
182 			/*
183 			 * zero-padded sign put out here; blank padded sign
184 			 * placed in number in _cvt().
185 			 */
186 			if (sign && padc == '0') {
187 				PUTC(sign);
188 				--width;
189 			}
190 			goto pforw;
191 		case 'n':
192 			if (flags&LONGINT)
193 				*va_arg(argp, long *) = cnt;
194 			else if (flags&SHORTINT)
195 				*va_arg(argp, short *) = cnt;
196 			else
197 				*va_arg(argp, int *) = cnt;
198 			break;
199 		case 'o':
200 			ARG();
201 			base = 8;
202 			goto nosign;
203 		case 'p':
204 			/*
205 			 * ``The argument shall be a pointer to void.  The
206 			 * value of the pointer is converted to a sequence
207 			 * of printable characters, in an implementation-
208 			 * defined manner.''
209 			 *	-- ANSI X3J11
210 			 */
211 			/*NOSTRICT*/
212 			_ulong = (u_long)va_arg(argp, void *);
213 			base = 16;
214 			goto nosign;
215 		case 's':
216 			if (!(t = va_arg(argp, char *)))
217 				t = "(null)";
218 			if (prec >= 0) {
219 				/*
220 				 * can't use strlen; can only look for the
221 				 * NUL in the first `prec' characters, and
222 				 * strlen() will go further.
223 				 */
224 				char *p, *memchr();
225 
226 				if (p = memchr(t, 0, prec)) {
227 					size = p - t;
228 					if (size > prec)
229 						size = prec;
230 				}
231 				else
232 					size = prec;
233 			}
234 			else
235 				size = strlen(t);
236 			goto pforw;
237 		case 'u':
238 			ARG();
239 			base = 10;
240 			goto nosign;
241 		case 'X':
242 			digs = "0123456789ABCDEF";
243 			/*FALLTHROUGH*/
244 		case 'x':
245 			ARG();
246 			base = 16;
247 			/* leading 0x/X only if non-zero */
248 			if (!_ulong)
249 				flags &= ~ALT;
250 
251 			/* unsigned conversions */
252 nosign:			sign = NULL;
253 			/*
254 			 * ``... diouXx conversions ... if a precision is
255 			 * specified, the 0 flag will be ignored.''
256 			 *	-- ANSI X3J11
257 			 */
258 number:			if (prec >= 0)
259 				padc = ' ';
260 			/*
261 			 * ``The result of converting a zero value with an
262 			 * explicit precision of zero is no characters.''
263 			 *	-- ANSI X3J11
264 			 */
265 			if (!_ulong && !prec) {
266 				size = 0;
267 				goto pforw;
268 			}
269 
270 			t = buf + BUF - 1;
271 			do {
272 				*t-- = digs[_ulong % base];
273 				_ulong /= base;
274 			} while(_ulong);
275 			for (size = buf + BUF - 1 - t; size < prec; ++size)
276 				*t-- = '0';
277 			digs = "0123456789abcdef";
278 
279 			/* alternate mode for hex and octal numbers */
280 			if (flags&ALT)
281 				switch (base) {
282 				case 16:
283 					/* avoid "00000x35" */
284 					if (padc == ' ') {
285 						*t-- = *fmt;
286 						*t-- = '0';
287 						size += 2;
288 					}
289 					else {
290 						PUTC('0');
291 						PUTC(*fmt);
292 						width -= 2;
293 					}
294 					break;
295 				case 8:
296 					if (t[1] != '0') {
297 						*t-- = '0';
298 						++size;
299 					}
300 					break;
301 				}
302 
303 			if (sign) {
304 				/* avoid "0000-3" */
305 				if (padc == ' ') {
306 					*t-- = sign;
307 					++size;
308 				}
309 				else {
310 					PUTC(sign);
311 					--width;
312 				}
313 			}
314 			++t;
315 
316 pforw:			if (!(flags&LADJUST) && width)
317 				for (n = size + fpprec; n++ < width;)
318 					PUTC(padc);
319 			if (fp->_cnt - (n = size) >= 0) {
320 				cnt += n;
321 				fp->_cnt -= n;
322 				bcopy(t, fp->_ptr, n);
323 				fp->_ptr += n;
324 			}
325 			else for (; n--; ++t)
326 				PUTC(*t);
327 			while (fpprec--)
328 				PUTC('0');
329 			if (flags&LADJUST)
330 				for (n = size + fpprec; ++n < width;)
331 					PUTC(' ');
332 			break;
333 		case '\0':		/* "%?" prints ?, unless ? is NULL */
334 			return(cnt);
335 		default:
336 			PUTC(*fmt);
337 		}
338 	}
339 	/*NOTREACHED*/
340 }
341 
342 #define	EFORMAT	0x01
343 #define	FFORMAT	0x02
344 #define	GFORMAT	0x04
345 #define	DEFPREC	6
346 
347 static char *
348 _cvt(number, prec, flags, fmtch, padc, sign, startp, endp)
349 	double number;
350 	register int prec;
351 	int flags;
352 	u_char fmtch;
353 	char padc, *sign, *startp, *endp;
354 {
355 	register char *p, *t;
356 	register int expcnt, format;
357 	double fract, integer, tmp, modf();
358 	int decpt;
359 	char *savep, exponent[MAXEXP];
360 
361 	if (prec == -1)
362 		prec = DEFPREC;
363 
364 	if (number < 0) {
365 		*sign = '-';
366 		number = -number;
367 	}
368 
369 	/* if blank padded, add sign in as part of the number */
370 	if (*sign && padc == ' ')
371 		*startp++ = *sign;
372 
373 	switch(fmtch) {
374 	case 'e':
375 	case 'E':
376 		format = EFORMAT;
377 		break;
378 	case 'f':
379 		format = FFORMAT;
380 		break;
381 	case 'g':
382 	case 'G':
383 		format = GFORMAT;
384 		fmtch -= 2;
385 	}
386 
387 	/*
388 	 * if the alternate flag is set, or, at least one digit of precision
389 	 * was requested, add a decimal point, unless it's the g/G format
390 	 * in which case we require two digits of precision, as it counts
391 	 * precision differently.
392 	 */
393 	decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
394 
395 	expcnt = 0;
396 	p = endp - 1;
397 	fract = modf(number, &integer);
398 	if (integer) {
399 		/* get integer part of number; count decimal places */
400 		for (; integer; ++expcnt) {
401 			tmp = modf(integer / 10, &integer);
402 			*p-- = tochar((int)((tmp + .03) * 10));
403 		}
404 
405 		/* copy, in reverse order, to start of buffer */
406 		t = startp;
407 		*t++ = *++p;
408 
409 		/*
410 		 * if the format is g/G, and the resulting exponent will be
411 		 * greater than the precision, use e/E format.  If e/E format,
412 		 * put in a decimal point as needed, and decrement precision
413 		 * count for each digit after the decimal point.
414 		 */
415 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
416 			if (format&GFORMAT) {
417 				format |= EFORMAT;
418 
419 				/* first digit is precision for g/G format */
420 				if (prec)
421 					--prec;
422 			}
423 			if (decpt)
424 				*t++ = '.';
425 			for (; ++p < endp && prec; --prec, *t++ = *p);
426 
427 			/* precision ran out, round */
428 			if (p < endp) {
429 				if (*p > '4') {
430 					for (savep = t--;; *t-- = '0') {
431 						if (*t == '.')
432 							--t;
433 						if (++*t <= '9')
434 							break;
435 					}
436 					t = savep;
437 				}
438 				fract = 0;
439 			}
440 		}
441 		/*
442 		 * g/G in f format; if out of precision, replace digits with
443 		 * zeroes, note, have to round first.
444 		 */
445 		else if (format&GFORMAT) {
446 			for (; ++p < endp && prec; --prec, *t++ = *p);
447 			/* precision ran out; round and then add zeroes */
448 			if (p < endp) {
449 				if (*p > '4') {
450 					for (savep = t--; ++*t > '9';
451 					    *t-- = '0');
452 					t = savep;
453 				}
454 				do {
455 					*t++ = '0';
456 				} while (++p < endp);
457 				fract = 0;
458 			}
459 			if (decpt)
460 				*t++ = '.';
461 		}
462 		/* f format */
463 		else {
464 			for (; ++p < endp; *t++ = *p);
465 			if (decpt)
466 				*t++ = '.';
467 		}
468 		p = t;
469 	}
470 	/*
471 	 * if no fraction, the number was zero, and if no precision, can't
472 	 * show anything after the decimal point.
473 	 */
474 	else if (!fract || !prec) {
475 		*startp++ = '0';
476 		if (decpt && !(format&GFORMAT))
477 			*startp++ = '.';
478 		*startp = '\0';
479 		return(startp);
480 	}
481 	/*
482 	 * if the format is g/G, and the resulting exponent will be less than
483 	 * -4 use e/E format.  If e/E format, compute exponent value.
484 	 */
485 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
486 		format |= EFORMAT;
487 		if (fract)
488 			for (p = startp; fract;) {
489 				fract = modf(fract * 10, &tmp);
490 				if (!tmp) {
491 					--expcnt;
492 					continue;
493 				}
494 				*p++ = tochar((int)tmp);
495 				break;
496 			}
497 		else
498 			*p++ = '0';
499 
500 		/* g/G format, decrement precision for first digit */
501 		if (format&GFORMAT && prec)
502 			--prec;
503 
504 		/* add decimal after first non-zero digit */
505 		if (decpt)
506 			*p++ = '.';
507 	}
508 	/*
509 	 * f format or g/G printed as f format; don't worry about decimal
510 	 * point, if g/G format doesn't need it, will get stripped later.
511 	 */
512 	else {
513 		p = startp;
514 		*p++ = '0';
515 		*p++ = '.';
516 	}
517 
518 	/* finish out requested precision */
519 	while (fract && prec-- > 0) {
520 		fract = modf(fract * 10, &tmp);
521 		*p++ = tochar((int)tmp);
522 	}
523 	while (prec-- > 0)
524 		*p++ = '0';
525 
526 	/*
527 	 * if any fractional value left, "round" it back up to the beginning
528 	 * of the number, fixing the exponent as necessary, and avoiding the
529 	 * decimal point.
530 	 */
531 	if (fract) {
532 		(void)modf(fract * 10, &tmp);
533 		if (tmp > 4) {
534 			for (savep = p--;; *p-- = '0') {
535 				if (*p == '.')
536 					--p;
537 				if (p == startp) {
538 					*p = '1';
539 					++expcnt;
540 					break;
541 				}
542 				if (++*p <= '9')
543 					break;
544 			}
545 			p = savep;
546 		}
547 	}
548 
549 	/*
550 	 * if a g/G format and not alternate flag, lose trailing zeroes,
551 	 * if e/E or g/G format, and last char is decimal point, lose it.
552 	 */
553 	if (!(flags&ALT)) {
554 		if (format&GFORMAT)
555 			for (; p[-1] == '0'; --p);
556 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
557 			--p;
558 	}
559 
560 	/* if an e/E format, add exponent */
561 	if (format&EFORMAT) {
562 		*p++ = fmtch;
563 		if (--expcnt < 0) {
564 			expcnt = -expcnt;
565 			*p++ = '-';
566 		}
567 		else
568 			*p++ = '+';
569 		t = exponent + MAXEXP;
570 		if (expcnt > 9) {
571 			do {
572 				*--t = tochar(expcnt % 10);
573 			} while ((expcnt /= 10) > 9);
574 			*--t = tochar(expcnt);
575 			for (; t < exponent + MAXEXP; *p++ = *t++);
576 		}
577 		else {
578 			*p++ = '0';
579 			*p++ = tochar(expcnt);
580 		}
581 	}
582 	return(p);
583 }
584