1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40 /* The pwb version this is based on */
41 static char *printf_id = "@(#) printf.c:2.2 6/5/79";
42 /* The local sccs version within ex */
43
44 #pragma ident "%Z%%M% %I% %E% SMI"
45
46 #include <stdarg.h>
47 #include <stdlib.h>
48 #include <limits.h>
49 extern short putoctal;
50 /*
51 * This version of printf is compatible with the Version 7 C
52 * printf. The differences are only minor except that
53 * viprintf assumes it is to print through putchar. Version 7
54 * printf is more general (and is much larger) and includes
55 * provisions for floating point.
56 */
57
58
59 #define MAXOCT 11 /* Maximum octal digits in a long */
60 #define MAXINT 32767 /* largest normal length positive integer */
61 #define BIG 1000000000 /* largest power of 10 less than an unsigned long */
62 #define MAXDIGS 10 /* number of digits in BIG */
63
64 static int width, sign, fill;
65
66 unsigned char *_p_dconv();
67 void _p_emit(unsigned char *, unsigned char *);
68
69 /*VARARGS*/
70 void
viprintf(unsigned char * fmt,...)71 viprintf(unsigned char *fmt, ...)
72 {
73 va_list ap;
74 unsigned char fcode;
75 wchar_t wfcode;
76 int prec;
77 int length,mask1,nbits,n;
78 int length2;
79 long int mask2, num;
80 unsigned char *bptr;
81 unsigned char *ptr;
82 unsigned char buf[134];
83
84 va_start(ap, fmt);
85 for (;;) {
86 /* process format string first */
87 for (;;) {
88 length2 = mbtowc(&wfcode, (char *)fmt, MB_LEN_MAX);
89 if(length2 <= 0) {
90 if (*fmt == '\0') {
91 fmt++;
92 return;
93 }
94 putchar(*fmt++);
95 } else {
96 if (wfcode == '%') {
97 fmt++;
98 break;
99 }
100 /* ordinary (non-%) character */
101 putchar(wfcode);
102 fmt += length2;
103 }
104 }
105 /* length modifier: -1 for h, 1 for l, 0 for none */
106 length = 0;
107 /* check for a leading - sign */
108 sign = 0;
109 if (*fmt == '-') {
110 sign++;
111 fmt++;
112 }
113 /* a '0' may follow the - sign */
114 /* this is the requested fill character */
115 fill = 1;
116 if (*fmt == '0') {
117 fill--;
118 fmt++;
119 }
120
121 /* Now comes a digit string which may be a '*' */
122 if (*fmt == '*') {
123 width = va_arg(ap, int);
124 if (width < 0) {
125 width = -width;
126 sign = !sign;
127 }
128 fmt++;
129 }
130 else {
131 width = 0;
132 while (*fmt>='0' && *fmt<='9')
133 width = width * 10 + (*fmt++ - '0');
134 }
135
136 /* maybe a decimal point followed by more digits (or '*') */
137 if (*fmt=='.') {
138 if (*++fmt == '*') {
139 prec = va_arg(ap, int);
140 fmt++;
141 }
142 else {
143 prec = 0;
144 while (*fmt>='0' && *fmt<='9')
145 prec = prec * 10 + (*fmt++ - '0');
146 }
147 }
148 else
149 prec = -1;
150
151 /*
152 * At this point, "sign" is nonzero if there was
153 * a sign, "fill" is 0 if there was a leading
154 * zero and 1 otherwise, "width" and "prec"
155 * contain numbers corresponding to the digit
156 * strings before and after the decimal point,
157 * respectively, and "fmt" addresses the next
158 * character after the whole mess. If there was
159 * no decimal point, "prec" will be -1.
160 */
161 switch (*fmt) {
162 case 'L':
163 case 'l':
164 length = 2;
165 /* no break!! */
166 case 'h':
167 case 'H':
168 length--;
169 fmt++;
170 break;
171 }
172
173 /*
174 * At exit from the following switch, we will
175 * emit the characters starting at "bptr" and
176 * ending at "ptr"-1, unless fcode is '\0'.
177 */
178 switch (fcode = *fmt++) {
179 /* process characters and strings first */
180 case 'c':
181 buf[0] = va_arg(ap, int);
182 ptr = bptr = &buf[0];
183 if (buf[0] != '\0')
184 ptr++;
185 break;
186 case 's':
187 bptr = va_arg(ap,unsigned char *);
188 if (bptr==0)
189 bptr = (unsigned char *)"(null pointer)";
190 if (prec < 0)
191 prec = MAXINT;
192 for (n=0; *bptr++ && n < prec; n++) ;
193 ptr = --bptr;
194 bptr -= n;
195 break;
196 case 'O':
197 length = 1;
198 fcode = 'o';
199 /* no break */
200 case 'o':
201 case 'X':
202 case 'x':
203 if (length > 0)
204 num = va_arg(ap,long);
205 else
206 num = (unsigned)va_arg(ap,int);
207 if (fcode=='o') {
208 mask1 = 0x7;
209 mask2 = 0x1fffffffL;
210 nbits = 3;
211 }
212 else {
213 mask1 = 0xf;
214 mask2 = 0x0fffffffL;
215 nbits = 4;
216 }
217 n = (num!=0);
218 bptr = buf + MAXOCT + 3;
219 /* shift and mask for speed */
220 do
221 if (((int) num & mask1) < 10)
222 *--bptr = ((int) num & mask1) + 060;
223 else
224 *--bptr = ((int) num & mask1) + 0127;
225 while (num = (num >> nbits) & mask2);
226
227 if (fcode=='o') {
228 if (n)
229 *--bptr = '0';
230 }
231 else
232 if (!sign && fill <= 0) {
233 putchar('0');
234 putchar(fcode);
235 width -= 2;
236 }
237 else {
238 *--bptr = fcode;
239 *--bptr = '0';
240 }
241 ptr = buf + MAXOCT + 3;
242 break;
243 case 'D':
244 case 'U':
245 case 'I':
246 length = 1;
247 fcode = fcode + 'a' - 'A';
248 /* no break */
249 case 'd':
250 case 'i':
251 case 'u':
252 if (length > 0)
253 num = va_arg(ap,long);
254 else {
255 n = va_arg(ap,int);
256 if (fcode=='u')
257 num = (unsigned) n;
258 else
259 num = (long) n;
260 }
261 if (n = (fcode != 'u' && num < 0))
262 num = -num;
263 /* now convert to digits */
264 bptr = _p_dconv(num, buf);
265 if (n)
266 *--bptr = '-';
267 if (fill == 0)
268 fill = -1;
269 ptr = buf + MAXDIGS + 1;
270 break;
271 default:
272 /* not a control character,
273 * print it.
274 */
275 ptr = bptr = &fcode;
276 ptr++;
277 break;
278 }
279 if (fcode != '\0')
280 _p_emit(bptr,ptr);
281 }
282 va_end(ap);
283 }
284
285 /* _p_dconv converts the unsigned long integer "value" to
286 * printable decimal and places it in "buffer", right-justified.
287 * The value returned is the address of the first non-zero character,
288 * or the address of the last character if all are zero.
289 * The result is NOT null terminated, and is MAXDIGS characters long,
290 * starting at buffer[1] (to allow for insertion of a sign).
291 *
292 * This program assumes it is running on 2's complement machine
293 * with reasonable overflow treatment.
294 */
295 unsigned char *
_p_dconv(value,buffer)296 _p_dconv(value, buffer)
297 long value;
298 unsigned char *buffer;
299 {
300 unsigned char *bp;
301 int svalue;
302 int n;
303 long lval;
304
305 bp = buffer;
306
307 /* zero is a special case */
308 if (value == 0) {
309 bp += MAXDIGS;
310 *bp = '0';
311 return(bp);
312 }
313
314 /* develop the leading digit of the value in "n" */
315 n = 0;
316 while (value < 0) {
317 value -= BIG; /* will eventually underflow */
318 n++;
319 }
320 while ((lval = value - BIG) >= 0) {
321 value = lval;
322 n++;
323 }
324
325 /* stash it in buffer[1] to allow for a sign */
326 bp[1] = n + '0';
327 /*
328 * Now develop the rest of the digits. Since speed counts here,
329 * we do it in two loops. The first gets "value" down until it
330 * is no larger than MAXINT. The second one uses integer divides
331 * rather than long divides to speed it up.
332 */
333 bp += MAXDIGS + 1;
334 while (value > MAXINT) {
335 *--bp = (int)(value % 10) + '0';
336 value /= 10;
337 }
338
339 /* cannot lose precision */
340 svalue = value;
341 while (svalue > 0) {
342 *--bp = (svalue % 10) + '0';
343 svalue /= 10;
344 }
345
346 /* fill in intermediate zeroes if needed */
347 if (buffer[1] != '0') {
348 while (bp > buffer + 2)
349 *--bp = '0';
350 --bp;
351 }
352 return(bp);
353 }
354
355 /*
356 * This program sends string "s" to putchar. The character after
357 * the end of "s" is given by "send". This allows the size of the
358 * field to be computed; it is stored in "alen". "width" contains the
359 * user specified length. If width<alen, the width will be taken to
360 * be alen. "sign" is zero if the string is to be right-justified
361 * in the field, nonzero if it is to be left-justified. "fill" is
362 * 0 if the string is to be padded with '0', positive if it is to be
363 * padded with ' ', and negative if an initial '-' should appear before
364 * any padding in right-justification (to avoid printing "-3" as
365 * "000-3" where "-0003" was intended).
366 */
367 void
_p_emit(unsigned char * s,unsigned char * send)368 _p_emit(unsigned char *s, unsigned char *send)
369 {
370 unsigned char cfill;
371 int alen;
372 int npad, length;
373 wchar_t wchar;
374
375 alen = send - s;
376 if (alen > width)
377 width = alen;
378 cfill = fill>0? ' ': '0';
379
380 /* we may want to print a leading '-' before anything */
381 if (*s == '-' && fill < 0) {
382 putchar(*s++);
383 alen--;
384 width--;
385 }
386 npad = width - alen;
387
388 /* emit any leading pad characters */
389 if (!sign)
390 while (--npad >= 0)
391 putchar(cfill);
392
393 /* emit the string itself */
394 while (--alen >= 0) {
395 length = mbtowc(&wchar, (char *)s, MB_LEN_MAX);
396 if(length <= 0) {
397 putoctal = 1;
398 putchar((unsigned char)*s++);
399 putoctal = 0;
400 } else {
401 putchar(wchar);
402 s += length;
403 alen = alen - length + 1;
404 }
405 }
406 /* emit trailing pad characters */
407 if (sign)
408 while (--npad >= 0)
409 putchar(cfill);
410 }
411