* Copyright (c) 1994 David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
* Scanf and printf routines for arbitrary precision integers.
#define OUTBUFSIZE 200 /* realloc size for output buffers */
#define PUTCHAR(ch) math_chr(ch)
#define PUTSTR(str) math_str(str)
#define PRINTF1(fmt, a1) math_fmt(fmt, a1)
#define PRINTF2(fmt, a1, a2) math_fmt(fmt, a1, a2)
long _outdigits_
= 20; /* default digits for output */
int _outmode_
= MODE_INITIAL
; /* default output mode */
* Output state that has been saved when diversions are done.
typedef struct iostate IOSTATE
;
IOSTATE
*oldiostates
; /* previous saved state */
long outdigits
; /* digits for output */
int outmode
; /* output mode */
FILE *outfp
; /* file unit for output (if any) */
char *outbuf
; /* output string buffer (if any) */
long outbufsize
; /* current size of string buffer */
long outbufused
; /* space used in string buffer */
BOOL outputisstring
; /* TRUE if output is to string buffer */
static IOSTATE
*oldiostates
= NULL
; /* list of saved output states */
static FILE *outfp
= NULL
; /* file unit for output */
static char *outbuf
= NULL
; /* current diverted buffer */
static BOOL outputisstring
= FALSE
;
* zio_init - perform needed initilization work
* On some systems, one cannot initialize a pointer to a FILE *.
* This routine, called once at startup is a work-a-round for
* systems with such bogons.
static int done
= 0; /* 1 => routine already called */
* Routine to output a character either to a FILE
* handle or into a string.
if (outbufused
>= outbufsize
) {
cp
= (char *)realloc(outbuf
, outbufsize
+ OUTBUFSIZE
+ 1);
math_error("Cannot realloc output string");
outbufsize
+= OUTBUFSIZE
;
outbuf
[outbufused
++] = (char)ch
;
* Routine to output a null-terminated string either
* to a FILE handle or into a string.
if ((outbufused
+ len
) > outbufsize
) {
cp
= (char *)realloc(outbuf
, outbufsize
+ len
+ OUTBUFSIZE
+ 1);
math_error("Cannot realloc output string");
outbufsize
+= (len
+ OUTBUFSIZE
);
memcpy(&outbuf
[outbufused
], str
, len
);
* Output a null-terminated string either to a FILE handle or into a string,
* padded with spaces as needed so as to fit within the specified width.
* If width is positive, the spaces are added at the front of the string.
* If width is negative, the spaces are added at the end of the string.
* The complete string is always output, even if this overflows the width.
* No characters within the string are handled specially.
* Routine to output a printf-style formatted string either
* to a FILE handle or into a string.
# define VA_ALIST fmt, va_alist
# define VA_DCL char *fmt; va_dcl
# if defined(__STDC__) && __STDC__ == 1
# define VA_ALIST char *fmt, ...
# define VA_DCL char *fmt;
* Flush the current output stream.
* Divert further output so that it is saved into a string that will be
* returned later when the diversion is completed. The current state of
* output is remembered for later restoration. Diversions can be nested.
* Output diversion is only intended for saving output to "stdout".
sp
= (IOSTATE
*) malloc(sizeof(IOSTATE
));
math_error("No memory for diverting output");
sp
->oldiostates
= oldiostates
;
sp
->outdigits
= _outdigits_
;
sp
->outbufsize
= outbufsize
;
sp
->outbufused
= outbufused
;
sp
->outputisstring
= outputisstring
;
outbuf
= (char *) malloc(OUTBUFSIZE
+ 1);
math_error("Cannot allocate divert string");
* Undivert output and return the saved output as a string. This also
* restores the output state to what it was before the diversion began.
* The string needs freeing by the caller when it is no longer needed.
math_error("No diverted state to restore");
oldiostates
= sp
->oldiostates
;
_outdigits_
= sp
->outdigits
;
outbufsize
= sp
->outbufsize
;
outbufused
= sp
->outbufused
;
outputisstring
= sp
->outputisstring
;
* Clear all diversions and set output back to the original destination.
* This is called when resetting the global state of the program.
free(math_getdivertedio());
* Set the output routines to output to the specified FILE stream.
* This interacts with output diversion in the following manner.
* STDOUT diversion action
* yes yes set output to diversion string again.
* yes no set output to stdout.
* no yes set output to specified file.
* no no set output to specified file.
outputisstring
= (oldiostates
&& (newfp
== stdout
));
* Set the output mode for numeric output.
* This also returns the previous mode.
if ((newmode
<= MODE_DEFAULT
) || (newmode
> MODE_MAX
))
math_error("Setting illegal output mode");
* Set the number of digits for float or exponential output.
* This also returns the previous number of digits.
math_setdigits(newdigits
)
math_error("Setting illegal number of digits");
* Print an integer value as a hex number.
* Width is the number of columns to print the number in, including the
* sign if required. If zero, no extra output is done. If positive,
* leading spaces are typed if necessary. If negative, trailing spaces are
* typed if necessary. The special characters 0x appear to indicate the
register HALF
*hp
; /* current word to print */
int len
; /* number of halfwords to type */
str
= math_getdivertedio();
if ((len
== 0) && (*z
.v
<= (FULL
) 9)) {
PRINTF1("0x%x", (FULL
) *hp
--);
PRINTF1("%04x", (FULL
) *hp
--);
* Print an integer value as a binary number.
* The special characters 0b appear to indicate the number is binary.
register HALF
*hp
; /* current word to print */
int len
; /* number of halfwords to type */
HALF val
; /* current value */
HALF mask
; /* current mask */
int didprint
; /* nonzero if printed some digits */
int ch
; /* current char */
str
= math_getdivertedio();
if ((len
== 0) && (*z
.v
<= (FULL
) 1)) {
mask
= (1 << (BASEB
- 1));
ch
= '0' + ((mask
& val
) != 0);
if (didprint
|| (ch
!= '0')) {
* Print an integer value as an octal number.
* The number begins with a leading 0 to indicate that it is octal.
register HALF
*hp
; /* current word to print */
int len
; /* number of halfwords to type */
int num1
='0', num2
='0'; /* numbers to type */
int rem
; /* remainder number of halfwords */
str
= math_getdivertedio();
if ((len
== 1) && (*z
.v
<= (FULL
) 7)) {
switch (rem
) { /* handle odd amounts first */
num1
= (((FULL
) hp
[0]) << 8) + (((FULL
) hp
[-1]) >> 8);
num2
= (((FULL
) (hp
[-1] & 0xff)) << 16) + ((FULL
) hp
[-2]);
num1
= (((FULL
) hp
[0]) >> 8);
num2
= (((FULL
) (hp
[0] & 0xff)) << 16) + ((FULL
) hp
[-1]);
PRINTF2("0%o%08o", num1
, num2
);
while (len
> 0) { /* finish in groups of 3 halfwords */
num1
= (((FULL
) hp
[0]) << 8) + (((FULL
) hp
[-1]) >> 8);
num2
= (((FULL
) (hp
[-1] & 0xff)) << 16) + ((FULL
) hp
[-2]);
PRINTF2("%08o%08o", num1
, num2
);
* Print a decimal integer to the terminal.
* This works by dividing the number by 10^2^N for some N, and
* then doing this recursively on the quotient and remainder.
* Decimals supplies number of decimal places to print, with a decimal
* point at the right location, with zero meaning no decimal point.
* Width is the number of columns to print the number in, including the
* decimal point and sign if required. If zero, no extra output is done.
* If positive, leading spaces are typed if necessary. If negative, trailing
* spaces are typed if necessary. As examples of the effects of these values,
* (345,0,0) = "345", (345,2,0) = "3.45", (345,5,8) = " .00345".
zprintval(z
, decimals
, width
)
ZVALUE z
; /* number to be printed */
long decimals
; /* number of decimal places */
long width
; /* number of columns to print in */
int depth
; /* maximum depth */
int n
; /* current index into array */
int i
; /* number to print */
long leadspaces
; /* number of leading spaces to print */
long putpoint
; /* digits until print decimal point */
long digits
; /* number of digits of raw number */
BOOL output
; /* TRUE if have output something */
BOOL neg
; /* TRUE if negative */
ZVALUE quo
, rem
; /* quotient and remainder */
ZVALUE leftnums
[32]; /* left parts of the number */
ZVALUE rightnums
[32]; /* right parts of the number */
leadspaces
= width
- neg
- (decimals
> 0);
* Find the 2^N power of ten which is greater than or equal
* to the number, calculating it the first time if necessary.
while ((_tenpowers_
[depth
].len
< z
.len
) || (zrel(_tenpowers_
[depth
], z
) <= 0)) {
if (_tenpowers_
[depth
].len
== 0)
zsquare(_tenpowers_
[depth
-1], &_tenpowers_
[depth
]);
* Divide by smaller 2^N powers of ten until the parts are small
* enough to output. This algorithm walks through a binary tree
* where each node is a piece of the number to print, and such that
* we visit left nodes first. We do the needed recursion in line.
zdiv(leftnums
[n
], _tenpowers_
[i
], &quo
, &rem
);
if (output
|| i
|| (n
== 0)) {
while (--leadspaces
>= 0)
putpoint
= (digits
- decimals
);
while (rightnums
[n
].len
== 0) {
leftnums
[n
] = rightnums
[n
];
* Read an integer value in decimal, hex, octal, or binary.
* Hex numbers are indicated by a leading "0x", binary with a leading "0b",
* and octal by a leading "0". Periods are skipped over, but any other
* extraneous character stops the scan.
if (*s
== '0') { /* possibly hex, octal, or binary */
if ((*s
>= '0') && (*s
<= '7')) {
} else if ((*s
== 'x') || (*s
== 'X')) {
} else if ((*s
== 'b') || (*s
== 'B')) {
if ((digval
>= '0') && (digval
<= '9'))
else if ((digval
>= 'a') && (digval
<= 'f') && shift
)
else if ((digval
>= 'A') && (digval
<= 'F') && shift
)
if (minus
&& !ziszero(z
))