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