2 * zio - scanf and printf routines for arbitrary precision integers
4 * Copyright (C) 1999-2007 David I. Bell
6 * Calc is open software; you can redistribute it and/or modify it under
7 * the terms of the version 2.1 of the GNU Lesser General Public License
8 * as published by the Free Software Foundation.
10 * Calc is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
13 * Public License for more details.
15 * A copy of version 2.1 of the GNU Lesser General Public License is
16 * distributed with calc under the filename COPYING-LGPL. You should have
17 * received a copy with calc; if not, write to Free Software Foundation, Inc.
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * @(#) $Revision: 30.2 $
21 * @(#) $Id: zio.c,v 30.2 2013/08/11 08:41:38 chongo Exp $
22 * @(#) $Source: /usr/local/src/bin/calc/RCS/zio.c,v $
24 * Under source code control: 1993/07/30 19:42:48
25 * File existed as early as: 1993
27 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
37 #define OUTBUFSIZE 200 /* realloc size for output buffers */
39 #define PUTCHAR(ch) math_chr(ch)
40 #define PUTSTR(str) math_str(str)
41 #define PRINTF1(fmt, a1) math_fmt(fmt, a1)
42 #define PRINTF2(fmt, a1, a2) math_fmt(fmt, a1, a2)
43 #define PRINTF3(fmt, a1, a2, a3) math_fmt(fmt, a1, a2, a3)
44 #define PRINTF4(fmt, a1, a2, a3, a4) math_fmt(fmt, a1, a2, a3, a4)
48 * Output state that has been saved when diversions are done.
50 typedef struct iostate IOSTATE
;
52 IOSTATE
*oldiostates
; /* previous saved state */
53 long outdigits
; /* digits for output */
54 int outmode
; /* output mode */
55 int outmode2
; /* secondary output mode */
56 FILE *outfp
; /* file unit for output (if any) */
57 char *outbuf
; /* output string buffer (if any) */
58 size_t outbufsize
; /* current size of string buffer */
59 size_t outbufused
; /* space used in string buffer */
60 BOOL outputisstring
; /* TRUE if output is to string buffer */
64 STATIC IOSTATE
*oldiostates
= NULL
; /* list of saved output states */
65 STATIC
FILE *outfp
= NULL
; /* file unit for output */
66 STATIC
char *outbuf
= NULL
; /* current diverted buffer */
67 STATIC BOOL outputisstring
= FALSE
;
68 STATIC
size_t outbufsize
;
69 STATIC
size_t outbufused
;
73 * zio_init - perform needed initilization work
75 * On some systems, one cannot initialize a pointer to a FILE *.
76 * This routine, called once at startup is a work-a-round for
77 * systems with such bogons.
82 STATIC
int done
= 0; /* 1 => routine already called */
92 * Routine to output a character either to a FILE
93 * handle or into a string.
100 if (!outputisstring
) {
104 if (outbufused
>= outbufsize
) {
105 cp
= (char *)realloc(outbuf
, outbufsize
+ OUTBUFSIZE
+ 1);
107 math_error("Cannot realloc output string");
111 outbufsize
+= OUTBUFSIZE
;
113 outbuf
[outbufused
++] = (char)ch
;
118 * Routine to output a null-terminated string either
119 * to a FILE handle or into a string.
127 if (!outputisstring
) {
132 if ((outbufused
+ len
) > outbufsize
) {
133 cp
= (char *)realloc(outbuf
, outbufsize
+ len
+ OUTBUFSIZE
+ 1);
135 math_error("Cannot realloc output string");
139 outbufsize
+= (len
+ OUTBUFSIZE
);
141 memcpy(&outbuf
[outbufused
], str
, len
);
147 * Output a null-terminated string either to a FILE handle or into a string,
148 * padded with spaces as needed so as to fit within the specified width.
149 * If width is positive, the spaces are added at the front of the string.
150 * If width is negative, the spaces are added at the end of the string.
151 * The complete string is always output, even if this overflows the width.
152 * No characters within the string are handled specially.
155 math_fill(char *str
, long width
)
158 width
-= (long)strlen(str
);
163 width
+= (long)strlen(str
);
172 * Routine to output a printf-style formatted string either
173 * to a FILE handle or into a string.
176 math_fmt(char *fmt
, ...)
182 vsnprintf(buf
, BUFSIZ
, fmt
, ap
);
190 * Flush the current output stream.
201 * Divert further output so that it is saved into a string that will be
202 * returned later when the diversion is completed. The current state of
203 * output is remembered for later restoration. Diversions can be nested.
204 * Output diversion is only intended for saving output to "stdout".
209 register IOSTATE
*sp
;
211 sp
= (IOSTATE
*) malloc(sizeof(IOSTATE
));
213 math_error("No memory for diverting output");
216 sp
->oldiostates
= oldiostates
;
217 sp
->outdigits
= conf
->outdigits
;
218 sp
->outmode
= conf
->outmode
;
219 sp
->outmode2
= conf
->outmode2
;
222 sp
->outbufsize
= outbufsize
;
223 sp
->outbufused
= outbufused
;
224 sp
->outputisstring
= outputisstring
;
228 outbuf
= (char *) malloc(OUTBUFSIZE
+ 1);
229 if (outbuf
== NULL
) {
230 math_error("Cannot allocate divert string");
233 outbufsize
= OUTBUFSIZE
;
234 outputisstring
= TRUE
;
240 * Undivert output and return the saved output as a string. This also
241 * restores the output state to what it was before the diversion began.
242 * The string needs freeing by the caller when it is no longer needed.
245 math_getdivertedio(void)
247 register IOSTATE
*sp
;
252 math_error("No diverted state to restore");
256 cp
[outbufused
] = '\0';
257 oldiostates
= sp
->oldiostates
;
258 conf
->outdigits
= sp
->outdigits
;
259 conf
->outmode
= sp
->outmode
;
260 conf
->outmode2
= sp
->outmode2
;
263 outbufsize
= sp
->outbufsize
;
264 outbufused
= sp
->outbufused
;
266 outputisstring
= sp
->outputisstring
;
273 * Clear all diversions and set output back to the original destination.
274 * This is called when resetting the global state of the program.
277 math_cleardiversions(void)
280 free(math_getdivertedio());
285 * Set the output routines to output to the specified FILE stream.
286 * This interacts with output diversion in the following manner.
287 * STDOUT diversion action
288 * ---- --------- ------
289 * yes yes set output to diversion string again.
290 * yes no set output to stdout.
291 * no yes set output to specified file.
292 * no no set output to specified file.
295 math_setfp(FILE *newfp
)
298 outputisstring
= (oldiostates
&& (newfp
== stdout
));
303 * Set the output mode for numeric output.
304 * This also returns the previous mode.
307 math_setmode(int newmode
)
311 if ((newmode
<= MODE_DEFAULT
) || (newmode
> MODE_MAX
)) {
312 math_error("Setting illegal output mode");
315 oldmode
= conf
->outmode
;
316 conf
->outmode
= newmode
;
322 * Set the secondary output mode for numeric output.
323 * This also returns the previous mode.
326 math_setmode2(int newmode
)
330 if (newmode
!= MODE2_OFF
&& ((newmode
<= MODE_DEFAULT
) ||
331 (newmode
> MODE_MAX
))) {
332 math_error("Setting illegal secondary output mode");
335 oldmode
= conf
->outmode2
;
336 conf
->outmode2
= newmode
;
342 * Set the number of digits for float or exponential output.
343 * This also returns the previous number of digits.
346 math_setdigits(LEN newdigits
)
351 math_error("Setting illegal number of digits");
354 olddigits
= conf
->outdigits
;
355 conf
->outdigits
= newdigits
;
361 * Print an integer value as a hex number.
362 * Width is the number of columns to print the number in, including the
363 * sign if required. If zero, no extra output is done. If positive,
364 * leading spaces are typed if necessary. If negative, trailing spaces are
365 * typed if necessary. The special characters 0x appear to indicate the
370 zprintx(ZVALUE z
, long width
)
372 register HALF
*hp
; /* current word to print */
373 int len
; /* number of halfwords to type */
379 str
= math_getdivertedio();
380 math_fill(str
, width
);
387 if ((len
== 0) && (*z
.v
<= (HALF
) 9)) {
388 len
= '0' + (int)(*z
.v
);
394 PRINTF1("0x%lx", (PRINT
) *hp
--);
396 PRINTF1("%08lx", (PRINT
) *hp
--);
398 #else /* BASEB == 32 */
399 PRINTF1("0x%lx", (FULL
) *hp
--);
401 PRINTF1("%04lx", (FULL
) *hp
--);
403 #endif /* BASEB == 32 */
408 * Print an integer value as a binary number.
409 * The special characters 0b appear to indicate the number is binary.
413 zprintb(ZVALUE z
, long width
)
415 register HALF
*hp
; /* current word to print */
416 int len
; /* number of halfwords to type */
417 HALF val
; /* current value */
418 HALF mask
; /* current mask */
419 int didprint
; /* nonzero if printed some digits */
420 int ch
; /* current char */
426 str
= math_getdivertedio();
427 math_fill(str
, width
);
434 if ((len
== 0) && (*z
.v
<= (FULL
) 1)) {
435 len
= '0' + (int)(*z
.v
);
443 val
= ((len
>= 0) ? *hp
-- : *hp
);
444 mask
= ((HALF
)1 << (BASEB
- 1));
446 ch
= '0' + ((mask
& val
) != 0);
447 if (didprint
|| (ch
!= '0')) {
458 * Print an integer value as an octal number.
459 * The number begins with a leading 0 to indicate that it is octal.
463 zprinto(ZVALUE z
, long width
)
465 register HALF
*hp
; /* current word to print */
466 int len
; /* number of halfwords to type */
467 #if BASEB == 32 /* Yes, the larger base needs a smaller type! */
468 HALF num1
='0'; /* numbers to type */
469 HALF num2
=(HALF
)0; /* numbers to type */
470 HALF num3
; /* numbers to type */
471 HALF num4
; /* numbers to type */
473 FULL num1
='0'; /* numbers to type */
474 FULL num2
=(FULL
)0; /* numbers to type */
476 int rem
; /* remainder number of halfwords */
482 str
= math_getdivertedio();
483 math_fill(str
, width
);
490 if ((len
== 1) && (*z
.v
<= (FULL
) 7)) {
491 num1
= '0' + (int)(*z
.v
);
492 PUTCHAR((int)(num1
& 0xff));
498 switch (rem
) { /* handle odd amounts first */
500 num1
= ((hp
[0]) >> 8);
501 num2
= (((hp
[0] & 0xff) << 16) + (hp
[-1] >> 16));
502 num3
= (((hp
[-1] & 0xffff) << 8) + (hp
[-2] >> 24));
503 num4
= (hp
[-2] & 0xffffff);
505 PRINTF4("0%lo%08lo%08lo%08lo",
506 (PRINT
) num1
, (PRINT
) num2
,
507 (PRINT
) num3
, (PRINT
) num4
);
509 PRINTF3("0%lo%08lo%08lo",
510 (PRINT
) num2
, (PRINT
) num3
, (PRINT
) num4
);
515 PRINTF1("0%lo", (PRINT
) hp
[0]);
518 num1
= ((hp
[0]) >> 16);
519 num2
= (((hp
[0] & 0xffff) << 8) + (hp
[-1] >> 24));
520 num3
= (hp
[-1] & 0xffffff);
522 PRINTF3("0%lo%08lo%08lo",
523 (PRINT
) num1
, (PRINT
) num2
, (PRINT
) num3
);
525 PRINTF2("0%lo%08lo", (PRINT
) num2
, (PRINT
) num3
);
532 while (len
> 0) { /* finish in groups of 3 words */
533 PRINTF4("%08lo%08lo%08lo%08lo",
534 (PRINT
) ((hp
[0]) >> 8),
535 (PRINT
) (((hp
[0] & 0xff) << 16) + (hp
[-1] >> 16)),
536 (PRINT
) (((hp
[-1] & 0xffff) << 8) + (hp
[-2] >> 24)),
537 (PRINT
) (hp
[-2] & 0xffffff));
543 switch (rem
) { /* handle odd amounts first */
545 num1
= ((((FULL
) hp
[0]) << 8) + (((FULL
) hp
[-1]) >> 8));
546 num2
= ((((FULL
) (hp
[-1] & 0xff)) << 16) + ((FULL
) hp
[-2]));
554 num1
= (((FULL
) hp
[0]) >> 8);
555 num2
= ((((FULL
) (hp
[0] & 0xff)) << 16) + ((FULL
) hp
[-1]));
559 PRINTF2("0%lo%08lo", num1
, num2
);
561 PRINTF1("0%lo", num2
);
566 while (len
> 0) { /* finish in groups of 3 halfwords */
567 PRINTF2("%08lo%08lo",
568 ((((FULL
) hp
[0]) << 8) + (((FULL
) hp
[-1]) >> 8)),
569 ((((FULL
) (hp
[-1] & 0xff))<<16) + ((FULL
) hp
[-2])));
579 * Print a decimal integer to the terminal.
580 * This works by dividing the number by 10^2^N for some N, and
581 * then doing this recursively on the quotient and remainder.
582 * Decimals supplies number of decimal places to print, with a decimal
583 * point at the right location, with zero meaning no decimal point.
584 * Width is the number of columns to print the number in, including the
585 * decimal point and sign if required. If zero, no extra output is done.
586 * If positive, leading spaces are typed if necessary. If negative, trailing
587 * spaces are typed if necessary. As examples of the effects of these values,
588 * (345,0,0) = "345", (345,2,0) = "3.45", (345,5,8) = " .00345".
591 * z number to be printed
592 * decimals number of decimal places
593 * width number of columns to print in
596 zprintval(ZVALUE z
, long decimals
, long width
)
598 int depth
; /* maximum depth */
599 int n
; /* current index into array */
600 long i
; /* number to print */
601 long leadspaces
; /* number of leading spaces to print */
602 long putpoint
; /* digits until print decimal point */
603 long digits
; /* number of digits of raw number */
604 BOOL output
; /* TRUE if have output something */
605 BOOL neg
; /* TRUE if negative */
606 ZVALUE quo
, rem
; /* quotient and remainder */
607 ZVALUE leftnums
[32]; /* left parts of the number */
608 ZVALUE rightnums
[32]; /* right parts of the number */
616 leadspaces
= width
- neg
- (decimals
> 0);
619 * Find the 2^N power of ten which is greater than or equal
620 * to the number, calculating it the first time if necessary.
622 _tenpowers_
[0] = _ten_
;
624 while ((_tenpowers_
[depth
].len
< z
.len
) ||
625 (zrel(_tenpowers_
[depth
], z
) <= 0)) {
627 if (_tenpowers_
[depth
].len
== 0) {
628 if (depth
<= TEN_MAX
) {
629 zsquare(_tenpowers_
[depth
-1],
630 &_tenpowers_
[depth
]);
632 math_error("cannot compute 10^2^(TEN_MAX+1)");
638 * Divide by smaller 2^N powers of ten until the parts are small
639 * enough to output. This algorithm walks through a binary tree
640 * where each node is a piece of the number to print, and such that
641 * we visit left nodes first. We do the needed recursion in line.
647 rightnums
[0].len
= 0;
652 zdiv(leftnums
[n
], _tenpowers_
[i
], &quo
, &rem
, 0);
659 i
= (long)(leftnums
[n
].v
[0]);
660 if (output
|| i
|| (n
== 0)) {
663 if (decimals
< digits
)
664 leadspaces
-= digits
;
666 leadspaces
-= decimals
+conf
->leadzero
;
667 while (--leadspaces
>= 0)
672 putpoint
= (digits
- decimals
);
677 while (++putpoint
<= 0)
684 PUTCHAR((int)(i
& 0xff));
688 while (rightnums
[n
].len
== 0) {
696 leftnums
[n
] = rightnums
[n
];
697 rightnums
[n
].len
= 0;
703 * Read an integer value in decimal, hex, octal, or binary.
704 * Hex numbers are indicated by a leading "0x", binary with a leading "0b",
705 * and octal by a leading "0". Periods are skipped over, but any other
706 * extraneous character stops the scan.
709 str2z(char *s
, ZVALUE
*res
)
711 ZVALUE z
, ztmp
, digit
;
720 else if (*s
== '-') {
724 if (*s
== '0') { /* possibly hex, octal, or binary */
726 if ((*s
>= '0') && (*s
<= '7')) {
728 } else if ((*s
== 'x') || (*s
== 'X')) {
731 } else if ((*s
== 'b') || (*s
== 'B')) {
742 if ((digval
>= '0') && (digval
<= '9'))
744 else if ((digval
>= 'a') && (digval
<= 'f') && shift
)
745 digval
-= ('a' - 10);
746 else if ((digval
>= 'A') && (digval
<= 'F') && shift
)
747 digval
-= ('A' - 10);
748 else if (digval
== '.')
753 zshift(z
, shift
, &ztmp
);
755 zmuli(z
, 10L, &ztmp
);
757 zadd(ztmp
, digit
, &z
);
761 if (minus
&& !ziszero(z
))
768 fitzprint(ZVALUE z
, long digits
, long show
)
773 if (digits
<= show
) {
778 ztenpow(digits
- show
, &ztmp1
);
779 (void) zquo(z
, ztmp1
, &ztmp2
, 1);
780 zprintval(ztmp2
, 0, 0);
784 ztenpow(show
, &ztmp1
);
785 (void) zmod(z
, ztmp1
, &ztmp2
, 0);
789 zprintval(ztmp2
, 0, 0);