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