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