xref: /illumos-gate/usr/src/cmd/vi/port/printf.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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
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 *
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
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