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