For NOT43 systems, get correct definition for dosynch().
[unix-history] / usr / src / lib / libc / stdio / vfprintf.c
CommitLineData
ad0e16d0 1/*
5f6de66c
KB
2 * Copyright (c) 1988 Regents of the University of California.
3 * All rights reserved.
ad0e16d0 4 *
5f6de66c
KB
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.
ad0e16d0
KB
11 */
12
5f6de66c 13#if defined(LIBC_SCCS) && !defined(lint)
f853d467 14static char sccsid[] = "@(#)vfprintf.c 5.10 (Berkeley) %G%";
5f6de66c 15#endif /* LIBC_SCCS and not lint */
ad0e16d0 16
0d15d742 17#include <sys/types.h>
5f6de66c
KB
18#include <varargs.h>
19#include <stdio.h>
20#include <ctype.h>
ad0e16d0 21
0d15d742
KB
22#define MAXBUF 120
23#define DEFPREC 6
6c1ffc8c 24
f853d467 25#define PUTC(ch) {++cnt; putc(ch, fp);}
66d7f790
KB
26
27#define LONGINT 0x01
28#define LONGDBL 0x02
29#define SHORTINT 0x04
a5676d90
KB
30#define GETARG(r) \
31 r = argsize&LONGINT ? va_arg(argp, long) : \
32 argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
ad0e16d0 33
862bbfbe
KB
34static int alternate;
35static char printsign;
36
66d7f790 37x_doprnt(fmt, argp, fp)
5f6de66c
KB
38 register char *fmt;
39 va_list argp;
66d7f790 40 register FILE *fp;
ad0e16d0 41{
f853d467
KB
42 register int base, cnt;
43 register char *bp, *t;
5f6de66c
KB
44 register u_long reg_ulong;
45 register long reg_long;
66d7f790 46 double _double;
f853d467
KB
47 char argsize, padc, *_cvt(), *digs, buf[MAXBUF];
48 int n, ladjust, width, prec, size;
5f6de66c 49
5592524e 50 digs = "0123456789abcdef";
5f6de66c
KB
51 for (cnt = 0; *fmt; ++fmt) {
52 if (*fmt != '%') {
f853d467 53 PUTC(*fmt);
5f6de66c 54 continue;
ad0e16d0
KB
55 }
56
66d7f790 57 alternate = ladjust = width = 0;
5f6de66c
KB
58 prec = -1;
59 padc = ' ';
66d7f790 60 argsize = printsign = '\0';
5f6de66c
KB
61
62flags: switch (*++fmt) {
63 case '#':
64 alternate = 1;
65 goto flags;
5f6de66c 66 case '*':
66d7f790
KB
67 /*
68 * ``A negative field width argument is taken as a
69 * - flag followed by a positive field width.''
70 * -- ANSI X3J11
71 * They don't exclude field widths read from args.
72 */
73 if ((width = va_arg(argp, int)) >= 0)
74 goto flags;
75 width = -width;
76 /*FALLTHROUGH*/
77 case '-':
78 ladjust = 1;
5f6de66c
KB
79 goto flags;
80 case '+':
81 printsign = '+';
82 goto flags;
5f6de66c 83 case '.':
66d7f790
KB
84 if (*++fmt == '*')
85 prec = va_arg(argp, int);
86 else if (isdigit(*fmt)) {
87 prec = 0;
5f6de66c 88 do {
6c1ffc8c 89 prec = 10 * prec + *fmt - '0';
5f6de66c
KB
90 } while isdigit(*++fmt);
91 --fmt;
ad0e16d0 92 }
66d7f790
KB
93 else {
94 prec = 0;
95 --fmt;
96 goto flags;
97 }
98 if (prec < 0)
99 prec = -1;
5f6de66c
KB
100 goto flags;
101 case '0':
102 padc = '0';
66d7f790 103 /*FALLTHROUGH*/
5f6de66c
KB
104 case '1': case '2': case '3': case '4':
105 case '5': case '6': case '7': case '8': case '9':
106 do {
6c1ffc8c 107 width = 10 * width + *fmt - '0';
5f6de66c
KB
108 } while isdigit(*++fmt);
109 --fmt;
66d7f790
KB
110 case 'L':
111 argsize |= LONGDBL;
112 goto flags;
113 case 'h':
114 argsize |= SHORTINT;
115 goto flags;
5f6de66c 116 case 'l':
66d7f790 117 argsize |= LONGINT;
5f6de66c 118 goto flags;
5592524e
KB
119 case 'c': {
120 char ch;
ad0e16d0 121
5592524e 122 ch = va_arg(argp, int);
f853d467 123 PUTC(ch);
66d7f790 124 break;
5592524e 125 }
a5676d90
KB
126 case 'd':
127 case 'i':
128 GETARG(reg_long);
129 if (reg_long < 0) {
130 reg_ulong = -reg_long;
131 printsign = '-';
132 }
133 else {
134 reg_ulong = reg_long;
135 }
136 if (printsign)
f853d467 137 PUTC(printsign);
a5676d90
KB
138 base = 10;
139 goto num1;
6c1ffc8c 140 case 'e':
0d15d742 141 case 'E':
66d7f790 142 case 'f':
5592524e 143 case 'g':
0d15d742 144 case 'G':
5592524e 145 _double = va_arg(argp, double);
0d15d742 146 bp = _cvt(_double, prec, buf, buf + sizeof(buf), *fmt);
6c1ffc8c 147pbuf: size = bp - buf;
66d7f790
KB
148 if (size < width && !ladjust)
149 do {
f853d467 150 PUTC(padc);
66d7f790
KB
151 } while (--width > size);
152 for (t = buf; t < bp; ++t)
f853d467 153 PUTC(*t);
66d7f790 154 for (; width > size; --width)
f853d467 155 PUTC(padc);
ad0e16d0 156 break;
66d7f790
KB
157 case 'n':
158 *(va_arg(argp, int *)) = cnt;
159 break;
ad0e16d0 160 case 'o':
66d7f790 161 GETARG(reg_ulong);
ad0e16d0 162 base = 8;
66d7f790
KB
163 if (!reg_ulong || !alternate)
164 goto num1;
165 bp = buf + sizeof(buf) - 1;
166 do {
167 *bp-- = digs[reg_ulong % base];
168 reg_ulong /= base;
169 } while(reg_ulong);
170 size = &buf[sizeof(buf) - 1] - bp;
171 if (size < --width && !ladjust)
172 do {
f853d467 173 PUTC(padc);
66d7f790 174 } while (--width > size);
f853d467 175 PUTC('0');
6c1ffc8c 176 goto num2;
66d7f790 177 case 'p':
ad0e16d0 178 case 's':
66d7f790
KB
179 if (!(bp = va_arg(argp, char *)))
180 bp = "(null)";
181 if (width > 0 && !ladjust) {
5f6de66c
KB
182 char *savep;
183
66d7f790
KB
184 savep = bp;
185 for (n = 0; *bp && (prec < 0 || n < prec);
186 n++, bp++);
187 bp = savep;
188 while (n++ < width)
f853d467 189 PUTC(' ');
5f6de66c 190 }
66d7f790
KB
191 for (n = 0; *bp; ++bp) {
192 if (++n > prec && prec >= 0)
ad0e16d0 193 break;
f853d467 194 PUTC(*bp);
5f6de66c 195 }
66d7f790 196 if (n < width && ladjust)
5f6de66c 197 do {
f853d467 198 PUTC(' ');
66d7f790 199 } while (++n < width);
ad0e16d0 200 break;
ad0e16d0 201 case 'u':
66d7f790 202 GETARG(reg_ulong);
ad0e16d0 203 base = 10;
66d7f790 204 goto num1;
ad0e16d0
KB
205 case 'X':
206 digs = "0123456789ABCDEF";
5f6de66c 207 /*FALLTHROUGH*/
ad0e16d0 208 case 'x':
66d7f790 209 GETARG(reg_ulong);
5f6de66c 210 if (alternate && reg_ulong) {
f853d467
KB
211 PUTC('0');
212 PUTC(*fmt);
5f6de66c 213 }
ad0e16d0 214 base = 16;
66d7f790 215num1: bp = buf + sizeof(buf) - 1;
5f6de66c 216 do {
66d7f790 217 *bp-- = digs[reg_ulong % base];
5f6de66c
KB
218 reg_ulong /= base;
219 } while(reg_ulong);
66d7f790
KB
220 size = &buf[sizeof(buf) - 1] - bp;
221 for (; size < prec; *bp-- = '0', ++size);
222 if (size < width && !ladjust)
223 do {
f853d467 224 PUTC(padc);
66d7f790 225 } while (--width > size);
6c1ffc8c 226num2: while (++bp != &buf[MAXBUF])
f853d467 227 PUTC(*bp);
66d7f790 228 for (; width > size; --width)
f853d467 229 PUTC(padc);
5592524e 230 digs = "0123456789abcdef";
ad0e16d0 231 break;
5f6de66c 232 case '\0': /* "%?" prints ?, unless ? is NULL */
66d7f790 233 return(ferror(fp) ? -1 : cnt);
ad0e16d0 234 default:
f853d467 235 PUTC(*fmt);
ad0e16d0 236 }
ad0e16d0 237 }
66d7f790 238 return(ferror(fp) ? -1 : cnt);
ad0e16d0 239}
cd0a25f4 240
0d15d742
KB
241#define EFORMAT 0x01
242#define FFORMAT 0x02
243#define GFORMAT 0x04
244
245static char *
246_cvt(number, prec, startp, endp, fmtch)
cd0a25f4 247 double number;
0d15d742 248 register int prec;
862bbfbe 249 char *startp, *endp, fmtch;
cd0a25f4 250{
862bbfbe 251 register char *p;
0d15d742 252 register int expcnt, format;
862bbfbe 253 double fract, integer, tmp, modf();
0d15d742 254 int decpt;
862bbfbe 255 char *savep;
cd0a25f4 256
862bbfbe 257 if (prec == -1) /* set default precision */
cd0a25f4 258 prec = DEFPREC;
cd0a25f4 259
862bbfbe
KB
260 p = endp - 1;
261 if (number < 0) { /* set sign */
262 *startp++ = '-';
263 number = -number;
264 }
cd0a25f4 265 else if (printsign)
862bbfbe
KB
266 *startp++ = '+';
267
0d15d742
KB
268 switch(fmtch) {
269 case 'e':
270 case 'E':
271 format = EFORMAT;
272 break;
273 case 'f':
274 format = FFORMAT;
275 break;
276 case 'g':
277 case 'G':
278 format = GFORMAT;
279 fmtch -= 2;
280 }
281
862bbfbe
KB
282 /*
283 * if the alternate flag is set, or, at least one digit of precision
284 * was requested, add a decimal point, unless it's the g/G format
285 * in which case we require two digits of precision, since it counts
286 * precision differently.
287 */
288 decpt = alternate || prec > 1 || !(format&GFORMAT) && prec;
cd0a25f4 289
862bbfbe
KB
290 expcnt = 0;
291 fract = modf(number, &integer);
292 if (integer) {
293 register char *p2;
294
295 /* get integer part of number; count decimal places */
296 for (; integer; ++expcnt) {
297 tmp = modf(integer / 10, &integer);
298 *p-- = (int)((tmp + .03) * 10) + '0';
cd0a25f4 299 }
862bbfbe
KB
300
301 /* copy, in reverse order, to start of buffer */
302 p2 = startp;
303 *p2++ = *++p;
304
305 /*
306 * if the format is g/G, and the resulting exponent will be
307 * greater than the precision, use e/E format. If e/E format,
308 * put in a decimal point as needed, and decrement precision
309 * count for each digit after the decimal point.
310 */
311 if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
312 if (format&GFORMAT) {
313 format |= EFORMAT;
314
315 /* first digit is precision for g/G format */
316 if (prec)
317 --prec;
318 }
319 if (decpt)
320 *p2++ = '.';
321 for (; ++p < endp && prec; --prec, *p2++ = *p);
322
323 /* precision ran out; round number */
324 if (p < endp) {
325 if (*p > '4') {
326 for (savep = p2--;; *p2-- = '0') {
327 if (*p2 == '.')
328 --p2;
329 if (++*p2 <= '9')
330 break;
331 }
332 p2 = savep;
333 }
334 fract = 0;
335 }
336 }
337 /*
338 * g/G in f format; if run out of precision, replace digits
339 * with zeroes, note, have to round first, otherwise lose
340 * rounding point.
341 */
342 else if (format&GFORMAT) {
343 for (; ++p < endp && prec; --prec, *p2++ = *p);
344 /* precision ran out; round and then add zeroes */
345 if (p < endp) {
346 if (*p > '4') {
347 for (savep = p2--; ++*p2 > '9';
348 *p2-- = '0');
349 p2 = savep;
350 }
351 do {
352 *p2++ = '0';
353 } while (++p < endp);
354 fract = 0;
355 }
356 if (decpt)
357 *p2++ = '.';
cd0a25f4 358 }
862bbfbe
KB
359 /* f format */
360 else {
361 for (; ++p < endp; *p2++ = *p);
362 if (decpt)
363 *p2++ = '.';
cd0a25f4 364 }
862bbfbe
KB
365 p = p2;
366 }
367 /*
368 * it's unclear from the ANSI X3J11 spec if the g/G format should
369 * just result in an empty string, because it's supposed to remove
370 * trailing zeroes. That seems counter-intuitive, so here it does
371 * what f and e/E do; if no fraction, the number was zero, and if
372 * no precision can't show anything after the decimal point.
373 */
374 else if (!fract || !prec) {
375 *startp++ = '0';
376 if (decpt)
377 *startp++ = '.';
378 *startp++ = '\0';
379 return(startp);
380 }
381 /*
382 * if the format is g/G, and the resulting exponent will be less than
383 * -4 use e/E format. If e/E format, compute exponent value.
384 */
385 else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
386 format |= EFORMAT;
387 if (fract)
388 for (p = startp; fract;) {
389 fract = modf(fract * 10, &tmp);
390 if (!tmp) {
391 --expcnt;
392 continue;
393 }
394 *p++ = (int)tmp + '0';
395 break;
396 }
cd0a25f4 397 else
862bbfbe
KB
398 *p++ = '0';
399
400 /* g/G format, decrement precision for first digit */
401 if (format&GFORMAT && prec)
402 --prec;
403
404 /* add decimal after first non-zero digit */
405 if (decpt)
406 *p++ = '.';
cd0a25f4 407 }
862bbfbe
KB
408 /*
409 * f format or g/G printed as f format; don't worry about decimal
410 * point, if g/G format doesn't need it, will get stripped later.
411 */
cd0a25f4 412 else {
862bbfbe
KB
413 p = startp;
414 *p++ = '0';
415 *p++ = '.';
416 }
417
418 /* finish out requested precision from fractional value */
419 while (prec--)
420 if (fract) {
421 fract = modf(fract * 10, &tmp);
422 *p++ = (int)tmp + '0';
cd0a25f4 423 }
862bbfbe
KB
424 else
425 *p++ = '0';
426
427 /*
428 * if any fractional value left, "round" it back up to the beginning
429 * of the number, fixing the exponent as necessary, and avoiding the
430 * decimal point.
431 */
432 if (fract) {
433 (void)modf(fract * 10, &tmp);
434 if (tmp > 4) {
435 for (savep = p--;; *p-- = '0') {
436 if (*p == '.')
437 --p;
438 if (p == startp) {
439 *p = '1';
440 ++expcnt;
441 break;
442 }
443 if (++*p <= '9')
444 break;
445 }
446 p = savep;
cd0a25f4 447 }
862bbfbe
KB
448 }
449
450 /*
451 * if a g/G format and not alternate flag, lose trailing zeroes,
452 * if e/E or g/G format, and last char is decimal point, lose it.
453 */
454 if (!alternate) {
455 if (format&GFORMAT)
456 for (; p[-1] == '0'; --p);
457 if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
458 --p;
459 }
460
461 /* if an e/E format, add exponent */
462 if (format&EFORMAT) {
463 *p++ = fmtch;
464 if (--expcnt < 0) {
465 expcnt = -expcnt;
466 *p++ = '-';
cd0a25f4 467 }
862bbfbe
KB
468 else
469 *p++ = '+';
470 *p++ = expcnt / 10 + '0';
471 *p++ = expcnt % 10 + '0';
cd0a25f4 472 }
862bbfbe 473 return(p);
cd0a25f4 474}