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