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