xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34233)
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  * Originally derived from code posted by Steve Summit to USENET,
13  * dated 3/25/87
14  */
15 
16 #if defined(LIBC_SCCS) && !defined(lint)
17 static char sccsid[] = "@(#)vfprintf.c	5.2 (Berkeley) 05/07/88";
18 #endif /* LIBC_SCCS and not lint */
19 
20 #include <sys/param.h>
21 #include <varargs.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 
25 #define	MAXBUF		(sizeof(long) * 8)	 /* enough for binary */
26 #define	PUTC(ch, fd)	{ ++cnt; putc(ch, fd); }
27 #define	todigit(ch)	((ch) - '0')
28 
29 doprnt(fmt, argp, fd)
30 	register char *fmt;
31 	va_list argp;
32 	register FILE *fd;
33 {
34 	register u_long reg_ulong;
35 	register long reg_long;
36 	register int base;
37 	register char *digs, *p, padc;
38 	char printsign, buf[MAXBUF];
39 	int alternate, cnt, n, ladjust, length, setlong, prec, size;
40 
41 	for (cnt = 0; *fmt; ++fmt) {
42 		if (*fmt != '%') {
43 			PUTC(*fmt, fd);
44 			continue;
45 		}
46 
47 		alternate = ladjust = length = setlong = 0;
48 		prec = -1;
49 		padc = ' ';
50 		printsign = '\0';
51 
52 flags:		switch (*++fmt) {
53 		case '#':
54 			alternate = 1;
55 			goto flags;
56 		case '%':			/* "%#%" prints as "%" */
57 			PUTC('%', fd);
58 			continue;
59 		case '*':
60 			if ((length = va_arg(argp, int)) < 0) {
61 				ladjust = !ladjust;
62 				length = -length;
63 			}
64 			goto flags;
65 		case '+':
66 			printsign = '+';
67 			goto flags;
68 		case '-':
69 			ladjust = 1;
70 			goto flags;
71 		case '.':
72 			if (isdigit(*++fmt)) {
73 				do {
74 					prec = 10 * prec + todigit(*fmt);
75 				} while isdigit(*++fmt);
76 				--fmt;
77 			}
78 			else if (*fmt == '*')
79 				prec = va_arg(argp, int);
80 			goto flags;
81 		case '0':
82 			padc = '0';
83 			goto flags;
84 		case '1': case '2': case '3': case '4':
85 		case '5': case '6': case '7': case '8': case '9':
86 			do {
87 				length = 10 * length + todigit(*fmt);
88 			} while isdigit(*++fmt);
89 			--fmt;
90 		case 'l':
91 			setlong = 1;
92 			goto flags;
93 		}
94 
95 		digs = "0123456789abcdef";
96 
97 		switch (*fmt) {
98 		case 'c':
99 			PUTC(va_arg(argp, int), fd);
100 			break;
101 		case 'd':
102 			if (setlong)
103 				reg_long = va_arg(argp, long);
104 			else
105 				reg_long = va_arg(argp, int);
106 			if (reg_long < 0) {
107 				reg_ulong = -reg_long;
108 				printsign = '-';
109 			}
110 			else {
111 				reg_ulong = reg_long;
112 			}
113 			if (printsign)
114 				PUTC(printsign, fd);
115 			base = 10;
116 			goto donum;
117 		case 'o':
118 			if (setlong)
119 				reg_ulong = va_arg(argp, long);
120 			else
121 				reg_ulong = va_arg(argp, int);
122 			base = 8;
123 			goto donum;
124 		case 's':
125 			if (!(p = va_arg(argp, char *)))
126 				p = "(null)";
127 			if (length > 0 && !ladjust) {
128 				char *savep;
129 
130 				savep = p;
131 				for (n = 0; *p && (prec == -1 || n < prec);
132 				    n++, p++);
133 				p = savep;
134 				while (n++ < length)
135 					PUTC(' ', fd);
136 			}
137 			for (n = 0; *p; ++p) {
138 				if (++n > prec && prec != -1)
139 					break;
140 				PUTC(*p, fd);
141 			}
142 			if (n < length && ladjust)
143 				do {
144 					PUTC(' ', fd);
145 				} while (++n < length);
146 			break;
147 		case 'u':
148 			if (setlong)
149 				reg_ulong = va_arg(argp, long);
150 			else
151 				reg_ulong = va_arg(argp, int);
152 			base = 10;
153 			goto donum;
154 		case 'X':
155 			digs = "0123456789ABCDEF";
156 			/*FALLTHROUGH*/
157 		case 'x':
158 			if (setlong)
159 				reg_ulong = va_arg(argp, long);
160 			else
161 				reg_ulong = va_arg(argp, int);
162 			if (alternate && reg_ulong) {
163 				PUTC('0', fd);
164 				PUTC(*fmt, fd);
165 			}
166 			base = 16;
167 donum:			p = &buf[sizeof(buf) - 1];
168 			do {
169 				*p-- = digs[reg_ulong % base];
170 				reg_ulong /= base;
171 			} while(reg_ulong);
172 			size = &buf[sizeof(buf) - 1] - p;
173 			if (reg_ulong && alternate && *fmt == 'o') {
174 				if (size < --length && !ladjust)
175 					do {
176 						PUTC(padc, fd);
177 					} while (--length > size);
178 				PUTC('0', fd);
179 				while (++p != &buf[MAXBUF])
180 					PUTC(*p, fd);
181 				if (size < length)	/* must be ladjust */
182 					for (; length > size; --length)
183 						PUTC(padc, fd);
184 			}
185 			else {
186 				if (size < length && !ladjust)
187 					do {
188 						PUTC(padc, fd);
189 					} while (--length > size);
190 				while (++p != &buf[MAXBUF])
191 					PUTC(*p, fd);
192 				if (size < length)	/* must be ladjust */
193 					for (; length > size; --length)
194 						PUTC(padc, fd);
195 			}
196 			break;
197 		case '\0':		/* "%?" prints ?, unless ? is NULL */
198 			return(cnt);
199 		default:
200 			PUTC(*fmt, fd);
201 		}
202 	}
203 	return(cnt);
204 }
205