added sccs, Bill put in more buffers
[unix-history] / usr / src / usr.bin / ex / printf.c
CommitLineData
5f4b8e7a
MH
1/* char printf_id[] = "@(#) printf.c:2.2 6/5/79";*/
2#include "varargs.h"
3/*
4 * This version of printf is compatible with the Version 7 C
5 * printf. The differences are only minor except that this
6 * printf assumes it is to print through putchar. Version 7
7 * printf is more general (and is much larger) and includes
8 * provisions for floating point.
9 */
10
11
12#define MAXOCT 11 /* Maximum octal digits in a long */
13#define MAXINT 32767 /* largest normal length positive integer */
14#define BIG 1000000000 /* largest power of 10 less than an unsigned long */
15#define MAXDIGS 10 /* number of digits in BIG */
16
17static int width, sign, fill;
18
19char *_p_dconv();
20
21printf(va_alist)
22 va_dcl
23{
24 va_list ap;
25 register char *fmt;
26 char fcode;
27 int prec;
28 int length,mask1,nbits,n;
29 long int mask2, num;
30 register char *bptr;
31 char *ptr;
32 char buf[134];
33
34 va_start(ap);
35 fmt = va_arg(ap,char *);
36 for (;;) {
37 /* process format string first */
38 while ((fcode = *fmt++)!='%') {
39 /* ordinary (non-%) character */
40 if (fcode=='\0')
41 return;
42 putchar(fcode);
43 }
44 /* length modifier: -1 for h, 1 for l, 0 for none */
45 length = 0;
46 /* check for a leading - sign */
47 sign = 0;
48 if (*fmt == '-') {
49 sign++;
50 fmt++;
51 }
52 /* a '0' may follow the - sign */
53 /* this is the requested fill character */
54 fill = 1;
55 if (*fmt == '0') {
56 fill--;
57 fmt++;
58 }
59
60 /* Now comes a digit string which may be a '*' */
61 if (*fmt == '*') {
62 width = va_arg(ap, int);
63 if (width < 0) {
64 width = -width;
65 sign = !sign;
66 }
67 fmt++;
68 }
69 else {
70 width = 0;
71 while (*fmt>='0' && *fmt<='9')
72 width = width * 10 + (*fmt++ - '0');
73 }
74
75 /* maybe a decimal point followed by more digits (or '*') */
76 if (*fmt=='.') {
77 if (*++fmt == '*') {
78 prec = va_arg(ap, int);
79 fmt++;
80 }
81 else {
82 prec = 0;
83 while (*fmt>='0' && *fmt<='9')
84 prec = prec * 10 + (*fmt++ - '0');
85 }
86 }
87 else
88 prec = -1;
89
90 /*
91 * At this point, "sign" is nonzero if there was
92 * a sign, "fill" is 0 if there was a leading
93 * zero and 1 otherwise, "width" and "prec"
94 * contain numbers corresponding to the digit
95 * strings before and after the decimal point,
96 * respectively, and "fmt" addresses the next
97 * character after the whole mess. If there was
98 * no decimal point, "prec" will be -1.
99 */
100 switch (*fmt) {
101 case 'L':
102 case 'l':
103 length = 2;
104 /* no break!! */
105 case 'h':
106 case 'H':
107 length--;
108 fmt++;
109 break;
110 }
111
112 /*
113 * At exit from the following switch, we will
114 * emit the characters starting at "bptr" and
115 * ending at "ptr"-1, unless fcode is '\0'.
116 */
117 switch (fcode = *fmt++) {
118 /* process characters and strings first */
119 case 'c':
120 buf[0] = va_arg(ap, int);
121 ptr = bptr = &buf[0];
122 if (buf[0] != '\0')
123 ptr++;
124 break;
125 case 's':
126 bptr = va_arg(ap,char *);
127 if (bptr==0)
128 bptr = "(null pointer)";
129 if (prec < 0)
130 prec = MAXINT;
131 for (n=0; *bptr++ && n < prec; n++) ;
132 ptr = --bptr;
133 bptr -= n;
134 break;
135 case 'O':
136 length = 1;
137 fcode = 'o';
138 /* no break */
139 case 'o':
140 case 'X':
141 case 'x':
142 if (length > 0)
143 num = va_arg(ap,long);
144 else
145 num = (unsigned)va_arg(ap,int);
146 if (fcode=='o') {
147 mask1 = 0x7;
148 mask2 = 0x1fffffffL;
149 nbits = 3;
150 }
151 else {
152 mask1 = 0xf;
153 mask2 = 0x0fffffffL;
154 nbits = 4;
155 }
156 n = (num!=0);
157 bptr = buf + MAXOCT + 3;
158 /* shift and mask for speed */
159 do
160 if (((int) num & mask1) < 10)
161 *--bptr = ((int) num & mask1) + 060;
162 else
163 *--bptr = ((int) num & mask1) + 0127;
164 while (num = (num >> nbits) & mask2);
165
166 if (fcode=='o') {
167 if (n)
168 *--bptr = '0';
169 }
170 else
171 if (!sign && fill <= 0) {
172 putchar('0');
173 putchar(fcode);
174 width -= 2;
175 }
176 else {
177 *--bptr = fcode;
178 *--bptr = '0';
179 }
180 ptr = buf + MAXOCT + 3;
181 break;
182 case 'D':
183 case 'U':
184 case 'I':
185 length = 1;
186 fcode = fcode + 'a' - 'A';
187 /* no break */
188 case 'd':
189 case 'i':
190 case 'u':
191 if (length > 0)
192 num = va_arg(ap,long);
193 else {
194 n = va_arg(ap,int);
195 if (fcode=='u')
196 num = (unsigned) n;
197 else
198 num = (long) n;
199 }
200 if (n = (fcode != 'u' && num < 0))
201 num = -num;
202 /* now convert to digits */
203 bptr = _p_dconv(num, buf);
204 if (n)
205 *--bptr = '-';
206 if (fill == 0)
207 fill = -1;
208 ptr = buf + MAXDIGS + 1;
209 break;
210 default:
211 /* not a control character,
212 * print it.
213 */
214 ptr = bptr = &fcode;
215 ptr++;
216 break;
217 }
218 if (fcode != '\0')
219 _p_emit(bptr,ptr);
220 }
221 va_end(ap);
222}
223
224/* _p_dconv converts the unsigned long integer "value" to
225 * printable decimal and places it in "buffer", right-justified.
226 * The value returned is the address of the first non-zero character,
227 * or the address of the last character if all are zero.
228 * The result is NOT null terminated, and is MAXDIGS characters long,
229 * starting at buffer[1] (to allow for insertion of a sign).
230 *
231 * This program assumes it is running on 2's complement machine
232 * with reasonable overflow treatment.
233 */
234char *
235_p_dconv(value, buffer)
236 long value;
237 char *buffer;
238{
239 register char *bp;
240 register int svalue;
241 int n;
242 long lval;
243
244 bp = buffer;
245
246 /* zero is a special case */
247 if (value == 0) {
248 bp += MAXDIGS;
249 *bp = '0';
250 return(bp);
251 }
252
253 /* develop the leading digit of the value in "n" */
254 n = 0;
255 while (value < 0) {
256 value -= BIG; /* will eventually underflow */
257 n++;
258 }
259 while ((lval = value - BIG) >= 0) {
260 value = lval;
261 n++;
262 }
263
264 /* stash it in buffer[1] to allow for a sign */
265 bp[1] = n + '0';
266 /*
267 * Now develop the rest of the digits. Since speed counts here,
268 * we do it in two loops. The first gets "value" down until it
269 * is no larger than MAXINT. The second one uses integer divides
270 * rather than long divides to speed it up.
271 */
272 bp += MAXDIGS + 1;
273 while (value > MAXINT) {
274 *--bp = (int)(value % 10) + '0';
275 value /= 10;
276 }
277
278 /* cannot lose precision */
279 svalue = value;
280 while (svalue > 0) {
281 *--bp = (svalue % 10) + '0';
282 svalue /= 10;
283 }
284
285 /* fill in intermediate zeroes if needed */
286 if (buffer[1] != '0') {
287 while (bp > buffer + 2)
288 *--bp = '0';
289 --bp;
290 }
291 return(bp);
292}
293
294/*
295 * This program sends string "s" to putchar. The character after
296 * the end of "s" is given by "send". This allows the size of the
297 * field to be computed; it is stored in "alen". "width" contains the
298 * user specified length. If width<alen, the width will be taken to
299 * be alen. "sign" is zero if the string is to be right-justified
300 * in the field, nonzero if it is to be left-justified. "fill" is
301 * 0 if the string is to be padded with '0', positive if it is to be
302 * padded with ' ', and negative if an initial '-' should appear before
303 * any padding in right-justification (to avoid printing "-3" as
304 * "000-3" where "-0003" was intended).
305 */
306_p_emit(s, send)
307 register char *s;
308 char *send;
309{
310 char cfill;
311 register int alen;
312 int npad;
313
314 alen = send - s;
315 if (alen > width)
316 width = alen;
317 cfill = fill>0? ' ': '0';
318
319 /* we may want to print a leading '-' before anything */
320 if (*s == '-' && fill < 0) {
321 putchar(*s++);
322 alen--;
323 width--;
324 }
325 npad = width - alen;
326
327 /* emit any leading pad characters */
328 if (!sign)
329 while (--npad >= 0)
330 putchar(cfill);
331
332 /* emit the string itself */
333 while (--alen >= 0)
334 putchar(*s++);
335
336 /* emit trailing pad characters */
337 if (sign)
338 while (--npad >= 0)
339 putchar(cfill);
340}