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