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