xref: /illumos-gate/usr/src/cmd/csh/printf.c (revision 6c02b4a4)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980 Regents of the University of California.
11  * All rights reserved. The Berkeley Software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 /*
18  * Hacked "printf" which prints through putbyte and Putchar.
19  * putbyte() is used to send a pure byte, which might be a part
20  * of a mutlibyte character, mainly for %s.  A control character
21  * for putbyte() may be QUOTE'd meaning not to convert it to ^x
22  * sequence.  In all other cases Putchar() is used to send a character
23  * in tchar (== wchar_t + * optional QUOE.)
24  * DONT USE WITH STDIO!
25  * This printf has been hacked again so that it understands tchar string
26  * when the format specifier %t is used.  Also %c has been expanded
27  * to take a tchar character as well as normal int.
28  * %t is supported in its simplest form; no width or precision will
29  * be understood.
30  * Assumption here is that sizeof(tchar)<=sizeof(int) so that tchar is
31  * passed as int.  Otherwise, %T must be specified instead of %c to
32  * print a character in tchar.
33  */
34 
35 #include <stdarg.h>
36 #include <values.h>
37 #include "sh.h" /* For tchar. */
38 
39 #define	HIBITLL		(1ULL << 63)
40 
41 void _print(char *format, va_list *args);
42 
43 static char *p;
44 
45 int
printf(const char * format,...)46 printf(const char *format, ...)
47 {
48 	va_list stupid;
49 
50 	p = (char *)gettext(format);
51 	va_start(stupid, format);
52 	_print(p, &stupid);
53 	va_end(stupid);
54 
55 	return (0);
56 }
57 
58 /*
59  *	Floating-point code is included or not, depending
60  *	on whether the preprocessor variable FLOAT is 1 or 0.
61  */
62 
63 /* Maximum number of digits in any integer (long) representation */
64 #define	MAXDIGS	20
65 
66 /* Convert a digit character to the corresponding number */
67 #define	tonumber(x)	((x) - '0')
68 
69 /* Convert a number between 0 and 9 to the corresponding digit */
70 #define	todigit(x)	((x) + '0')
71 
72 /* Maximum total number of digits in E format */
73 #define	MAXECVT	17
74 
75 /* Maximum number of digits after decimal point in F format */
76 #define	MAXFCVT	60
77 
78 /* Maximum significant figures in a floating-point number */
79 #define	MAXFSIG	17
80 
81 /* Maximum number of characters in an exponent */
82 #define	MAXESIZ	4
83 
84 /* Maximum (positive) exponent or greater */
85 #define	MAXEXP	40
86 
87 
88 
89 #define	max(a, b) ((a) > (b) ? (a) : (b))
90 #define	min(a, b) ((a) < (b) ? (a) : (b))
91 
92 /* If this symbol is nonzero, allow '0' as a flag */
93 #define	FZERO 1
94 
95 #if FLOAT
96 /*
97  *	System-supplied routines for floating conversion
98  */
99 char *fcvt();
100 char *ecvt();
101 #endif
102 
103 void
_print(char * format,va_list * args)104 _print(char *format, va_list *args)
105 {
106 	/* Current position in format */
107 	char *cp;
108 
109 	/* Starting and ending points for value to be printed */
110 	char *bp, *p;
111 	tchar *tbp, *tep;	/* For "%t". */
112 	tchar tcbuf[2];		/* For "%c" or "%T". */
113 
114 	/* Field width and precision */
115 	int width, prec;
116 
117 	/* Format code */
118 	char fcode;
119 
120 	/* Number of padding zeroes required on the left */
121 	int lzero;
122 
123 	/* Flags - nonzero if corresponding character appears in format */
124 	bool length;		/* l */
125 	bool double_length;	/* ll */
126 	bool fplus;		/* + */
127 	bool fminus;		/* - */
128 	bool fblank;		/* blank */
129 	bool fsharp;		/* # */
130 #if FZERO
131 	bool fzero;		/* 0 */
132 #endif
133 
134 	/* Pointer to sign, "0x", "0X", or empty */
135 	char *prefix;
136 #if FLOAT
137 	/* Exponent or empty */
138 	char *suffix;
139 
140 	/* Buffer to create exponent */
141 	char expbuf[MAXESIZ + 1];
142 
143 	/* Number of padding zeroes required on the right */
144 	int rzero;
145 
146 	/* The value being converted, if real */
147 	double dval;
148 
149 	/* Output values from fcvt and ecvt */
150 	int decpt, sign;
151 
152 	/* Scratch */
153 	int k;
154 
155 	/* Values are developed in this buffer */
156 	char buf[max(MAXDIGS, max(MAXFCVT + DMAXEXP, MAXECVT) + 1)];
157 #else
158 	char buf[MAXDIGS];
159 #endif
160 	/* The value being converted, if integer */
161 	long long val;
162 
163 	/* Set to point to a translate table for digits of whatever radix */
164 	char *tab;
165 
166 	/* Work variables */
167 	int n, hradix, lowbit;
168 
169 	cp = format;
170 
171 	/*
172 	 *	The main loop -- this loop goes through one iteration
173 	 *	for each ordinary character or format specification.
174 	 */
175 	while (*cp)
176 		if (*cp != '%') {
177 			/* Ordinary (non-%) character */
178 			putbyte (*cp++);
179 		} else {
180 			/*
181 			 *	% has been found.
182 			 *	First, parse the format specification.
183 			 */
184 
185 			/* Scan the <flags> */
186 			fplus = fminus = fblank = fsharp = 0;
187 #if FZERO
188 			fzero = 0;
189 #endif
190 scan:
191 			switch (*++cp) {
192 			case '+':
193 				fplus = 1;
194 				goto scan;
195 			case '-':
196 				fminus = 1;
197 				goto scan;
198 			case ' ':
199 				fblank = 1;
200 				goto scan;
201 			case '#':
202 				fsharp = 1;
203 				goto scan;
204 #if FZERO
205 			case '0':
206 				fzero = 1;
207 				goto scan;
208 #endif
209 			}
210 
211 			/* Scan the field width */
212 			if (*cp == '*') {
213 				width = va_arg (*args, int);
214 				if (width < 0) {
215 					width = -width;
216 					fminus = 1;
217 				}
218 				cp++;
219 			} else {
220 				width = 0;
221 				while (isdigit(*cp)) {
222 					n = tonumber(*cp++);
223 					width = width * 10 + n;
224 				}
225 			}
226 
227 			/* Scan the precision */
228 			if (*cp == '.') {
229 
230 				/* '*' instead of digits? */
231 				if (*++cp == '*') {
232 					prec = va_arg(*args, int);
233 					cp++;
234 				} else {
235 					prec = 0;
236 					while (isdigit(*cp)) {
237 						n = tonumber(*cp++);
238 						prec = prec * 10 + n;
239 					}
240 				}
241 			} else {
242 				prec = -1;
243 			}
244 
245 			/* Scan the length modifier */
246 			double_length = length = 0;
247 			switch (*cp) {
248 			case 'l':
249 				if (*(cp + 1) == 'l') {
250 					cp++;
251 					double_length = 1;
252 				} else {
253 					length = 1;
254 				}
255 				/* No break */
256 			case 'h':
257 				cp++;
258 			}
259 
260 			/*
261 			 *	The character addressed by cp must be the
262 			 *	format letter -- there is nothing left for
263 			 *	it to be.
264 			 *
265 			 *	The status of the +, -, #, blank, and 0
266 			 *	flags are reflected in the variables
267 			 *	"fplus", "fminus", "fsharp", "fblank",
268 			 *	and "fzero", respectively.
269 			 *	"width" and "prec" contain numbers
270 			 *	corresponding to the digit strings
271 			 *	before and after the decimal point,
272 			 *	respectively. If there was no decimal
273 			 *	point, "prec" is -1.
274 			 *
275 			 *	The following switch sets things up
276 			 *	for printing.  What ultimately gets
277 			 *	printed will be padding blanks, a prefix,
278 			 *	left padding zeroes, a value, right padding
279 			 *	zeroes, a suffix, and more padding
280 			 *	blanks.  Padding blanks will not appear
281 			 *	simultaneously on both the left and the
282 			 *	right.  Each case in this switch will
283 			 *	compute the value, and leave in several
284 			 *	variables the information necessary to
285 			 *	construct what is to be printed.
286 			 *
287 			 *	The prefix is a sign, a blank, "0x", "0X",
288 			 *	or null, and is addressed by "prefix".
289 			 *
290 			 *	The suffix is either null or an exponent,
291 			 *	and is addressed by "suffix".
292 			 *
293 			 *	The value to be printed starts at "bp"
294 			 *	and continues up to and not including "p".
295 			 *
296 			 *	"lzero" and "rzero" will contain the number
297 			 *	of padding zeroes required on the left
298 			 *	and right, respectively.  If either of
299 			 *	these variables is negative, it will be
300 			 *	treated as if it were zero.
301 			 *
302 			 *	The number of padding blanks, and whether
303 			 *	they go on the left or the right, will be
304 			 *	computed on exit from the switch.
305 			 */
306 
307 			lzero = 0;
308 			prefix = "";
309 #if FLOAT
310 			rzero = lzero;
311 			suffix = prefix;
312 #endif
313 			switch (fcode = *cp++) {
314 
315 			/*
316 			 *	fixed point representations
317 			 *
318 			 *	"hradix" is half the radix for the conversion.
319 			 *	Conversion is unsigned unless fcode is 'd'.
320 			 *	HIBITLL is 1000...000 binary, and is equal to
321 			 *		the maximum negative number.
322 			 *	We assume a 2's complement machine
323 			 */
324 
325 			case 'D':
326 			case 'U':
327 				length = 1;
328 			case 'd':
329 			case 'u':
330 				hradix = 5;
331 				goto fixed;
332 
333 			case 'O':
334 				length = 1;
335 			case 'o':
336 				hradix = 4;
337 				goto fixed;
338 
339 			case 'X':
340 			case 'x':
341 				hradix = 8;
342 
343 fixed:
344 				/* Establish default precision */
345 				if (prec < 0) {
346 					prec = 1;
347 				}
348 
349 				/* Fetch the argument to be printed */
350 				if (double_length) {
351 					val = va_arg(*args, long long);
352 				} else if (length) {
353 					val = va_arg(*args, long);
354 				} else if (fcode == 'd') {
355 					val = va_arg(*args, int);
356 				} else {
357 					val = va_arg(*args, unsigned);
358 				}
359 
360 				/* If signed conversion, establish sign */
361 				if (fcode == 'd' || fcode == 'D') {
362 					if (val < 0) {
363 						prefix = "-";
364 						/*
365 						 *	Negate, checking in
366 						 *	advance for possible
367 						 *	overflow.
368 						 */
369 						if (val != HIBITLL) {
370 							val = -val;
371 						}
372 					} else if (fplus) {
373 						prefix = "+";
374 					} else if (fblank) {
375 						prefix = " ";
376 					}
377 				}
378 #if FZERO
379 				if (fzero) {
380 					int n = width - strlen(prefix);
381 					if (n > prec) {
382 						prec = n;
383 					}
384 				}
385 #endif
386 				/* Set translate table for digits */
387 				if (fcode == 'X') {
388 					tab = "0123456789ABCDEF";
389 				} else {
390 					tab = "0123456789abcdef";
391 				}
392 
393 				/* Develop the digits of the value */
394 				p = bp = buf + MAXDIGS;
395 				while (val) {
396 					lowbit = val & 1;
397 					val = (val >> 1) & ~HIBITLL;
398 					*--bp = tab[val % hradix * 2 + lowbit];
399 					val /= hradix;
400 				}
401 
402 				/* Calculate padding zero requirement */
403 				lzero = bp - p + prec;
404 
405 				/* Handle the # flag */
406 				if (fsharp && bp != p) {
407 					switch (fcode) {
408 					case 'o':
409 						if (lzero < 1)
410 							lzero = 1;
411 						break;
412 					case 'x':
413 						prefix = "0x";
414 						break;
415 					case 'X':
416 						prefix = "0X";
417 						break;
418 					}
419 				}
420 
421 				break;
422 #if FLOAT
423 			case 'E':
424 			case 'e':
425 				/*
426 				 *	E-format.  The general strategy
427 				 *	here is fairly easy: we take
428 				 *	what ecvt gives us and re-format it.
429 				 */
430 
431 				/* Establish default precision */
432 				if (prec < 0) {
433 					prec = 6;
434 				}
435 
436 				/* Fetch the value */
437 				dval = va_arg(*args, double);
438 
439 				/* Develop the mantissa */
440 				bp = ecvt(dval,
441 				    min(prec + 1, MAXECVT),
442 				    &decpt,
443 				    &sign);
444 
445 				/* Determine the prefix */
446 e_merge:
447 				if (sign) {
448 					prefix = "-";
449 				} else if (fplus) {
450 					prefix = "+";
451 				} else if (fblank) {
452 					prefix = " ";
453 				}
454 
455 				/* Place the first digit in the buffer */
456 				p = &buf[0];
457 				*p++ = *bp != '\0' ? *bp++ : '0';
458 
459 				/* Put in a decimal point if needed */
460 				if (prec != 0 || fsharp) {
461 					*p++ = '.';
462 				}
463 
464 				/* Create the rest of the mantissa */
465 				rzero = prec;
466 				while (rzero > 0 && *bp != '\0') {
467 					--rzero;
468 					*p++ = *bp++;
469 				}
470 
471 				bp = &buf[0];
472 
473 				/* Create the exponent */
474 				suffix = &expbuf[MAXESIZ];
475 				*suffix = '\0';
476 				if (dval != 0) {
477 					n = decpt - 1;
478 					if (n < 0) {
479 						n = -n;
480 					}
481 					while (n != 0) {
482 						*--suffix = todigit(n % 10);
483 						n /= 10;
484 					}
485 				}
486 
487 				/* Prepend leading zeroes to the exponent */
488 				while (suffix > &expbuf[MAXESIZ - 2]) {
489 					*--suffix = '0';
490 				}
491 
492 				/* Put in the exponent sign */
493 				*--suffix = (decpt > 0 || dval == 0) ?
494 				    '+' : '-';
495 
496 				/* Put in the e */
497 				*--suffix = isupper(fcode) ? 'E' : 'e';
498 
499 				break;
500 
501 			case 'f':
502 				/*
503 				 *	F-format floating point.  This is
504 				 *	a good deal less simple than E-format.
505 				 *	The overall strategy will be to call
506 				 *	fcvt, reformat its result into buf,
507 				 *	and calculate how many trailing
508 				 *	zeroes will be required.  There will
509 				 *	never be any leading zeroes needed.
510 				 */
511 
512 				/* Establish default precision */
513 				if (prec < 0) {
514 					prec = 6;
515 				}
516 
517 				/* Fetch the value */
518 				dval = va_arg(*args, double);
519 
520 				/* Do the conversion */
521 				bp = fcvt(dval,
522 				    min(prec, MAXFCVT),
523 				    &decpt,
524 				    &sign);
525 
526 				/* Determine the prefix */
527 f_merge:
528 				if (sign && decpt > -prec &&
529 				    *bp != '\0' && *bp != '0') {
530 					prefix = "-";
531 				} else if (fplus) {
532 					prefix = "+";
533 				} else if (fblank) {
534 					prefix = " ";
535 				}
536 
537 				/* Initialize buffer pointer */
538 				p = &buf[0];
539 
540 				/* Emit the digits before the decimal point */
541 				n = decpt;
542 				k = 0;
543 				if (n <= 0) {
544 					*p++ = '0';
545 				} else {
546 					do {
547 						if (*bp == '\0' ||
548 						    k >= MAXFSIG) {
549 							*p++ = '0';
550 						} else {
551 							*p++ = *bp++;
552 							++k;
553 						}
554 					} while (--n != 0);
555 				}
556 
557 				/* Decide whether we need a decimal point */
558 				if (fsharp || prec > 0) {
559 					*p++ = '.';
560 				}
561 
562 				/* Digits (if any) after the decimal point */
563 				n = min(prec, MAXFCVT);
564 				rzero = prec - n;
565 				while (--n >= 0) {
566 					if (++decpt <= 0 || *bp == '\0' ||
567 					    k >= MAXFSIG) {
568 						*p++ = '0';
569 					} else {
570 						*p++ = *bp++;
571 						++k;
572 					}
573 				}
574 
575 				bp = &buf[0];
576 
577 				break;
578 
579 			case 'G':
580 			case 'g':
581 				/*
582 				 *	g-format.  We play around a bit
583 				 *	and then jump into e or f, as needed.
584 				 */
585 
586 				/* Establish default precision */
587 				if (prec < 0) {
588 					prec = 6;
589 				}
590 
591 				/* Fetch the value */
592 				dval = va_arg(*args, double);
593 
594 				/* Do the conversion */
595 				bp = ecvt(dval,
596 				    min(prec, MAXECVT),
597 				    &decpt,
598 				    &sign);
599 				if (dval == 0) {
600 					decpt = 1;
601 				}
602 
603 				k = prec;
604 				if (!fsharp) {
605 					n = strlen(bp);
606 					if (n < k) {
607 						k = n;
608 					}
609 					while (k >= 1 && bp[k-1] == '0') {
610 						--k;
611 					}
612 				}
613 
614 				if (decpt < -3 || decpt > prec) {
615 					prec = k - 1;
616 					goto e_merge;
617 				} else {
618 					prec = k - decpt;
619 					goto f_merge;
620 				}
621 
622 #endif
623 			case 'c':
624 #ifdef MBCHAR_1 /* sizeof(int)>=sizeof(tchar) */
625 /*
626  * A tchar arg is passed as int so we used the normal %c to specify
627  * such an arugument.
628  */
629 				tcbuf[0] = va_arg(*args, int);
630 				tbp = &tcbuf[0];
631 				tep = tbp + 1;
632 				fcode = 't'; /* Fake the rest of code. */
633 				break;
634 #else
635 /*
636  * We would have to invent another new format speficier such as "%T" to
637  * take a tchar arg.  Let's worry about when that time comes.
638  */
639 				/*
640 				 * Following code take care of a char arg
641 				 * only.
642 				 */
643 				buf[0] = va_arg(*args, int);
644 				bp = &buf[0];
645 				p = bp + 1;
646 				break;
647 			case 'T': /* Corresponding arg is tchar. */
648 				tcbuf[0] = va_arg(*args, tchar);
649 				tbp = &tcbuf[0];
650 				tep = tbp + 1;
651 				fcode = 't'; /* Fake the rest of code. */
652 				break;
653 #endif
654 			case 's':
655 				bp = va_arg(*args, char *);
656 				if (bp == 0) {
657 nullstr:				bp = "(null)";
658 					p = bp + strlen("(null)");
659 					break;
660 				}
661 				if (prec < 0) {
662 					prec = MAXINT;
663 				}
664 				for (n = 0; *bp++ != '\0' && n < prec; n++)
665 					;
666 				p = --bp;
667 				bp -= n;
668 				break;
669 
670 			case 't':
671 				/*
672 				 * Special format specifier "%t" tells
673 				 * printf() to print char strings written
674 				 * as tchar string.
675 				 */
676 				tbp = va_arg(*args, tchar *);
677 				if (tbp == 0) {
678 					fcode = 's'; /* Act as if it were %s. */
679 					goto nullstr;
680 				}
681 				if (prec < 0) {
682 					prec = MAXINT;
683 				}
684 				for (n = 0; *tbp++ != 0 && n < prec; n++)
685 					;
686 				tep = --tbp;
687 				tbp -= n;
688 
689 				/*
690 				 * Just to make the following padding
691 				 * calculation not to go very crazy...
692 				 */
693 				bp = NULL;
694 				p = bp + n;
695 				break;
696 
697 			case '\0':
698 				cp--;
699 				break;
700 
701 			default:
702 				p = bp = &fcode;
703 				p++;
704 				break;
705 
706 			}
707 			if (fcode != '\0') {
708 				/* Calculate number of padding blanks */
709 				int nblank;
710 				nblank = width
711 #if FLOAT
712 					- (rzero < 0 ? 0:  rzero)
713 					- strlen(suffix)
714 #endif
715 					- (p - bp)
716 					- (lzero < 0 ? 0 : lzero)
717 					- strlen(prefix);
718 
719 				/* Blanks on left if required */
720 				if (!fminus) {
721 					while (--nblank >= 0) {
722 						Putchar(' ');
723 					}
724 				}
725 
726 				/* Prefix, if any */
727 				while (*prefix != '\0') {
728 					Putchar(*prefix++);
729 				}
730 
731 				/* Zeroes on the left */
732 				while (--lzero >= 0) {
733 					Putchar('0');
734 				}
735 
736 				/* The value itself */
737 				if (fcode == 't') {	/* %t is special. */
738 					while (tbp < tep) {
739 					    Putchar(*tbp++);
740 					}
741 				} else {	/* For rest of the cases. */
742 					while (bp < p) {
743 					    putbyte(*bp++);
744 					}
745 				}
746 #if FLOAT
747 				/* Zeroes on the right */
748 				while (--rzero >= 0)
749 					Putchar('0');
750 
751 				/* The suffix */
752 				while (*suffix != '\0') {
753 					Putchar(*suffix++);
754 				}
755 #endif
756 				/* Blanks on the right if required */
757 				if (fminus) {
758 					while (--nblank >= 0) {
759 						Putchar(' ');
760 					}
761 				}
762 			}
763 		}
764 }
765