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
printf(const char * format,...)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
_print(char * format,va_list * args)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