4.4BSD/usr/src/contrib/calc-1.26.4/io.c
/*
* Copyright (c) 1993 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 extended precision numbers
*/
#include <stdio.h>
#include "stdarg.h"
#include "math.h"
#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;
struct 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 = stdout; /* file unit for output */
static char *outbuf = NULL; /* current diverted buffer */
static long scalefactor;
static long outbufsize;
static long outbufused;
static BOOL outputisstring;
static ZVALUE scalenumber = { 0, 0, 0 };
#if 0
static long etoalen;
static char *etoabuf = NULL;
#endif
static void zprintx proto((ZVALUE z, long width));
static void zprintb proto((ZVALUE z, long width));
static void zprinto proto((ZVALUE z, long width));
static void zprintval proto((ZVALUE z, long decimals, long width));
static void atoz proto((char * s, ZVALUE * res));
static void qprintff();
static void qprintfd();
static void qprintfe();
static void qprintfr();
static void qprintfo();
static void qprintfb();
#ifdef CODE
extern void qprintfx();
#else
static void qprintfx();
#endif
/*
* Routine to output a character either to a FILE
* handle or into a string.
*/
void
math_chr(ch)
{
char *cp;
if (!outputisstring) {
fputc(ch, outfp);
return;
}
if (outbufused >= outbufsize) {
cp = (char *)realloc(outbuf, outbufsize + OUTBUFSIZE + 1);
if (cp == NULL)
error("Cannot realloc output string");
outbuf = cp;
outbufsize += OUTBUFSIZE;
}
outbuf[outbufused++] = (char)ch;
}
/*
* Routine to output a null-terminated string either
* to a FILE handle or into a string.
*/
void
math_str(str)
char *str;
{
char *cp;
int len;
if (!outputisstring) {
fputs(str, outfp);
return;
}
len = strlen(str);
if ((outbufused + len) > outbufsize) {
cp = (char *)realloc(outbuf, outbufsize + len + OUTBUFSIZE + 1);
if (cp == NULL)
error("Cannot realloc output string");
outbuf = cp;
outbufsize += (len + OUTBUFSIZE);
}
memcpy(&outbuf[outbufused], str, len);
outbufused += len;
}
/*
* Routine to output a printf-style formatted string either
* to a FILE handle or into a string.
*/
#ifdef VARARGS
# define VA_ALIST fmt, va_alist
# define VA_DCL char *fmt; va_dcl
#else
# ifdef __STDC__
# define VA_ALIST char *fmt, ...
# define VA_DCL
# else
# define VA_ALIST fmt
# define VA_DCL char *fmt;
# endif
#endif
/*VARARGS*/
void
math_fmt(VA_ALIST)
VA_DCL
{
va_list ap;
char buf[200];
#ifdef VARARGS
va_start(ap);
#else
va_start(ap, fmt);
#endif
vsprintf(buf, fmt, ap);
va_end(ap);
math_str(buf);
}
/*
* Flush the current output stream.
*/
void
math_flush()
{
if (!outputisstring)
fflush(outfp);
}
/*
* 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".
*/
void
divertio()
{
register IOSTATE *sp;
sp = (IOSTATE *) malloc(sizeof(IOSTATE));
if (sp == NULL)
error("No memory for diverting output");
sp->oldiostates = oldiostates;
sp->outdigits = _outdigits_;
sp->outmode = _outmode_;
sp->outfp = outfp;
sp->outbuf = outbuf;
sp->outbufsize = outbufsize;
sp->outbufused = outbufused;
sp->outputisstring = outputisstring;
outbufused = 0;
outbufsize = 0;
outbuf = (char *) malloc(OUTBUFSIZE + 1);
if (outbuf == NULL)
error("Cannot allocate divert string");
outbufsize = OUTBUFSIZE;
outputisstring = TRUE;
oldiostates = sp;
}
/*
* 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.
*/
char *
getdivertedio()
{
register IOSTATE *sp;
char *cp;
sp = oldiostates;
if (sp == NULL)
error("No diverted state to restore");
cp = outbuf;
cp[outbufused] = '\0';
oldiostates = sp->oldiostates;
_outdigits_ = sp->outdigits;
_outmode_ = sp->outmode;
outfp = sp->outfp;
outbuf = sp->outbuf;
outbufsize = sp->outbufsize;
outbufused = sp->outbufused;
outbuf = sp->outbuf;
outputisstring = sp->outputisstring;
return cp;
}
/*
* Clear all diversions and set output back to the original destination.
* This is called when resetting the global state of the program.
*/
void
cleardiversions()
{
while (oldiostates)
free(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.
*/
void
setfp(newfp)
FILE *newfp;
{
outfp = newfp;
outputisstring = (oldiostates && (newfp == stdout));
}
/*
* Set the output mode for numeric output.
*/
void
set_mode(newmode)
{
if ((newmode <= MODE_DEFAULT) || (newmode > MODE_MAX))
error("Setting illegal output mode");
_outmode_ = newmode;
}
/*
* Set the number of digits for float or exponential output.
*/
void
setdigits(newdigits)
long newdigits;
{
if (newdigits < 0)
error("Setting illegal number of digits");
_outdigits_ = newdigits;
}
/*
* Print a formatted string containing arbitrary numbers, similar to printf.
* ALL numeric arguments to this routine are rational NUMBERs.
* Various forms of printing such numbers are supplied, in addition
* to strings and characters. Output can actually be to any FILE
* stream or a string.
*/
#ifdef VARARGS
# define VA_ALIST1 fmt, va_alist
# define VA_DCL1 char *fmt; va_dcl
#else
# ifdef __STDC__
# define VA_ALIST1 char *fmt, ...
# define VA_DCL1
# else
# define VA_ALIST1 fmt
# define VA_DCL1 char *fmt;
# endif
#endif
/*VARARGS*/
void
qprintf(VA_ALIST1)
VA_DCL1
{
va_list ap;
NUMBER *q;
int ch, sign;
long width, precision;
#ifdef VARARGS
va_start(ap);
#else
va_start(ap, fmt);
#endif
while ((ch = *fmt++) != '\0') {
if (ch == '\\') {
ch = *fmt++;
switch (ch) {
case 'n': ch = '\n'; break;
case 'r': ch = '\r'; break;
case 't': ch = '\t'; break;
case 'f': ch = '\f'; break;
case 'v': ch = '\v'; break;
case 'b': ch = '\b'; break;
case 0:
va_end(ap);
return;
}
PUTCHAR(ch);
continue;
}
if (ch != '%') {
PUTCHAR(ch);
continue;
}
ch = *fmt++;
width = 0; precision = 8; sign = 1;
percent: ;
switch (ch) {
case 'd':
q = va_arg(ap, NUMBER *);
qprintfd(q, width);
break;
case 'f':
q = va_arg(ap, NUMBER *);
qprintff(q, width, precision);
break;
case 'e':
q = va_arg(ap, NUMBER *);
qprintfe(q, width, precision);
break;
case 'r':
case 'R':
q = va_arg(ap, NUMBER *);
qprintfr(q, width, (BOOL) (ch == 'R'));
break;
case 'N':
q = va_arg(ap, NUMBER *);
zprintval(q->num, 0L, width);
break;
case 'D':
q = va_arg(ap, NUMBER *);
zprintval(q->den, 0L, width);
break;
case 'o':
q = va_arg(ap, NUMBER *);
qprintfo(q, width);
break;
case 'x':
q = va_arg(ap, NUMBER *);
qprintfx(q, width);
break;
case 'b':
q = va_arg(ap, NUMBER *);
qprintfb(q, width);
break;
case 's':
PUTSTR(va_arg(ap, char *));
break;
case 'c':
PUTCHAR(va_arg(ap, int));
break;
case 0:
va_end(ap);
return;
case '-':
sign = -1;
ch = *fmt++;
default:
if (('0' <= ch && ch <= '9') || ch == '.' || ch == '*') {
if (ch == '*') {
q = va_arg(ap, NUMBER *);
width = sign * qtoi(q);
ch = *fmt++;
} else if (ch != '.') {
width = ch - '0';
while ('0' <= (ch = *fmt++) && ch <= '9')
width = width * 10 + ch - '0';
width *= sign;
}
if (ch == '.') {
if ((ch = *fmt++) == '*') {
q = va_arg(ap, NUMBER *);
precision = qtoi(q);
ch = *fmt++;
} else {
precision = 0;
while ('0' <= (ch = *fmt++) && ch <= '9')
precision = precision * 10 + ch - '0';
}
}
goto percent;
}
}
}
va_end(ap);
}
#if 0
/*
* Read a number from the specified FILE stream (NULL means stdin).
* The number can be an integer, a fraction, a real number, an
* exponential number, or a hex, octal or binary number. Leading blanks
* are skipped. Illegal numbers return NULL. Unrecognized characters
* remain to be read on the line.
* q = qreadval(fp);
*/
NUMBER *
qreadval(fp)
FILE *fp; /* file stream to read from (or NULL) */
{
NUMBER *r; /* returned number */
char *cp; /* current buffer location */
long savecc; /* characters saved in buffer */
long scancc; /* characters parsed correctly */
int ch; /* current character */
if (fp == NULL)
fp = stdin;
if (etoabuf == NULL) {
etoabuf = (char *)malloc(OUTBUFSIZE + 2);
if (etoabuf == NULL)
return NULL;
etoalen = OUTBUFSIZE;
}
cp = etoabuf;
ch = fgetc(fp);
while ((ch == ' ') || (ch == '\t'))
ch = fgetc(fp);
savecc = 0;
for (;;) {
if (ch == EOF)
return NULL;
if (savecc >= etoalen)
{
cp = (char *)realloc(etoabuf, etoalen + OUTBUFSIZE + 2);
if (cp == NULL)
return NULL;
etoabuf = cp;
etoalen += OUTBUFSIZE;
cp += savecc;
}
*cp++ = (char)ch;
*cp = '\0';
scancc = qparse(etoabuf, QPF_SLASH);
if (scancc != ++savecc)
break;
ch = fgetc(fp);
}
ungetc(ch, fp);
if (scancc < 0)
return NULL;
r = atoq(etoabuf);
if (iszero(r->den)) {
qfree(r);
r = NULL;
}
return r;
}
#endif
/*
* Print a complex number in rational representation.
* Example: 2/3-4i/5
*/
void
cprintfr(c)
COMPLEX *c;
{
NUMBER *r;
NUMBER *i;
r = c->real;
i = c->imag;
if (!qiszero(r) || qiszero(i))
qprintfr(r, 0L, FALSE);
if (qiszero(i))
return;
if (!qiszero(r) && !qisneg(i))
PUTCHAR('+');
zprintval(i->num, 0L, 0L);
PUTCHAR('i');
if (qisfrac(i)) {
PUTCHAR('/');
zprintval(i->den, 0L, 0L);
}
}
/*
* Print a number in the specified output mode.
* If MODE_DEFAULT is given, then the default output mode is used.
* Any approximate output is flagged with a leading tilde.
* Integers are always printed as themselves.
*/
void
qprintnum(q, outmode)
NUMBER *q;
{
NUMBER tmpval;
long prec, exp;
if (outmode == MODE_DEFAULT)
outmode = _outmode_;
if ((outmode == MODE_FRAC) || ((outmode == MODE_REAL) && qisint(q))) {
qprintfr(q, 0L, FALSE);
return;
}
switch (outmode) {
case MODE_INT:
if (qisfrac(q))
PUTCHAR('~');
qprintfd(q, 0L);
break;
case MODE_REAL:
prec = qplaces(q);
if ((prec < 0) || (prec > _outdigits_)) {
prec = _outdigits_;
PUTCHAR('~');
}
qprintff(q, 0L, prec);
break;
case MODE_EXP:
if (qiszero(q)) {
PUTCHAR('0');
return;
}
tmpval = *q;
tmpval.num.sign = 0;
exp = qilog10(&tmpval);
if (exp == 0) { /* in range to output as real */
qprintnum(q, MODE_REAL);
return;
}
tmpval.num = _one_;
tmpval.den = _one_;
if (exp > 0)
ztenpow(exp, &tmpval.den);
else
ztenpow(-exp, &tmpval.num);
q = qmul(q, &tmpval);
freeh(tmpval.num.v);
freeh(tmpval.den.v);
qprintnum(q, MODE_REAL);
qfree(q);
PRINTF1("e%ld", exp);
break;
case MODE_HEX:
qprintfx(q, 0L);
break;
case MODE_OCTAL:
qprintfo(q, 0L);
break;
case MODE_BINARY:
qprintfb(q, 0L);
break;
default:
error("Bad mode for print");
}
}
/*
* Print a number in floating point representation.
* Example: 193.784
*/
static void
qprintff(q, width, precision)
NUMBER *q;
long width;
long precision;
{
ZVALUE z, z1;
if (precision != scalefactor) {
if (scalenumber.v)
freeh(scalenumber.v);
ztenpow(precision, &scalenumber);
scalefactor = precision;
}
if (scalenumber.v)
zmul(q->num, scalenumber, &z);
else
z = q->num;
if (qisfrac(q)) {
zquo(z, q->den, &z1);
if (z.v != q->num.v)
freeh(z.v);
z = z1;
}
if (qisneg(q) && iszero(z))
PUTCHAR('-');
zprintval(z, precision, width);
if (z.v != q->num.v)
freeh(z.v);
}
/*
* Print a number in exponential notation.
* Example: 4.1856e34
*/
/*ARGSUSED*/
static void
qprintfe(q, width, precision)
register NUMBER *q;
long width;
long precision;
{
long exponent;
NUMBER q2;
ZVALUE num, den, tenpow, tmp;
if (qiszero(q)) {
PUTSTR("0.0");
return;
}
num = q->num;
den = q->den;
num.sign = 0;
exponent = zdigits(num) - zdigits(den);
if (exponent > 0) {
ztenpow(exponent, &tenpow);
zmul(den, tenpow, &tmp);
freeh(tenpow.v);
den = tmp;
}
if (exponent < 0) {
ztenpow(-exponent, &tenpow);
zmul(num, tenpow, &tmp);
freeh(tenpow.v);
num = tmp;
}
if (zrel(num, den) < 0) {
zmuli(num, 10L, &tmp);
if (num.v != q->num.v)
freeh(num.v);
num = tmp;
exponent--;
}
q2.num = num;
q2.den = den;
q2.num.sign = q->num.sign;
qprintff(&q2, 0L, precision);
if (exponent)
PRINTF1("e%ld", exponent);
if (num.v != q->num.v)
freeh(num.v);
if (den.v != q->den.v)
freeh(den.v);
}
/*
* Print a number in rational representation.
* Example: 397/37
*/
static void
qprintfr(q, width, force)
NUMBER *q;
long width;
BOOL force;
{
zprintval(q->num, 0L, width);
if (force || qisfrac(q)) {
PUTCHAR('/');
zprintval(q->den, 0L, width);
}
}
/*
* Print a number as an integer (truncating fractional part).
* Example: 958421
*/
static void
qprintfd(q, width)
NUMBER *q;
long width;
{
ZVALUE z;
if (qisfrac(q)) {
zquo(q->num, q->den, &z);
zprintval(z, 0L, width);
freeh(z.v);
} else
zprintval(q->num, 0L, width);
}
/*
* Print a number in hex.
* This prints the numerator and denominator in hex.
*/
#ifndef CODE
static
#endif
void
qprintfx(q, width)
NUMBER *q;
long width;
{
zprintx(q->num, width);
if (qisfrac(q)) {
PUTCHAR('/');
zprintx(q->den, 0L);
}
}
/*
* Print a number in binary.
* This prints the numerator and denominator in binary.
*/
static void
qprintfb(q, width)
NUMBER *q;
long width;
{
zprintb(q->num, width);
if (qisfrac(q)) {
PUTCHAR('/');
zprintb(q->den, 0L);
}
}
/*
* Print a number in octal.
* This prints the numerator and denominator in octal.
*/
static void
qprintfo(q, width)
NUMBER *q;
long width;
{
zprinto(q->num, width);
if (qisfrac(q)) {
PUTCHAR('/');
zprinto(q->den, 0L);
}
}
/*
* Print an integer value as a hex number.
* The special characters 0x appear to indicate the number is hex.
*/
/*ARGSUSED*/
static void
zprintx(z, width)
ZVALUE z;
long width;
{
register HALF *hp; /* current word to print */
int len; /* number of halfwords to type */
len = z.len - 1;
if (isneg(z))
PUTCHAR('-');
if ((len == 0) && (*z.v <= (FULL) 9)) {
len = '0' + *z.v;
PUTCHAR(len);
return;
}
hp = z.v + len;
PRINTF1("0x%x", (FULL) *hp--);
while (--len >= 0)
PRINTF1("%04x", (FULL) *hp--);
}
/*
* Print an integer value as a binary number.
* The special characters 0b appear to indicate the number is binary.
*/
/*ARGSUSED*/
static void
zprintb(z, width)
ZVALUE z;
long width;
{
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 */
len = z.len - 1;
if (isneg(z))
PUTCHAR('-');
if ((len == 0) && (*z.v <= (FULL) 1)) {
len = '0' + *z.v;
PUTCHAR(len);
return;
}
hp = z.v + len;
didprint = 0;
PUTSTR("0b");
while (len-- >= 0) {
val = *hp--;
mask = (1 << (BASEB - 1));
while (mask) {
ch = '0' + ((mask & val) != 0);
if (didprint || (ch != '0')) {
PUTCHAR(ch);
didprint = 1;
}
mask >>= 1;
}
}
}
/*
* Print an integer value as an octal number.
* The number begins with a leading 0 to indicate that it is octal.
*/
/*ARGSUSED*/
static void
zprinto(z, width)
ZVALUE z;
long width;
{
register HALF *hp; /* current word to print */
int len; /* number of halfwords to type */
int num1, num2; /* numbers to type */
int rem; /* remainder number of halfwords */
if (isneg(z))
PUTCHAR('-');
len = z.len;
if ((len == 1) && (*z.v <= (FULL) 7)) {
num1 = '0' + *z.v;
PUTCHAR(num1);
return;
}
hp = z.v + len - 1;
rem = len % 3;
switch (rem) { /* handle odd amounts first */
case 0:
num1 = (((FULL) hp[0]) << 8) + (((FULL) hp[-1]) >> 8);
num2 = (((FULL) (hp[-1] & 0xff)) << 16) + ((FULL) hp[-2]);
rem = 3;
break;
case 1:
num1 = 0;
num2 = (FULL) hp[0];
break;
case 2:
num1 = (((FULL) hp[0]) >> 8);
num2 = (((FULL) (hp[0] & 0xff)) << 16) + ((FULL) hp[-1]);
break;
}
if (num1)
PRINTF2("0%o%08o", num1, num2);
else
PRINTF1("0%o", num2);
len -= rem;
hp -= rem;
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);
hp -= 3;
len -= 3;
}
}
/*
* 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".
*/
static void
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 */
if (decimals < 0)
decimals = 0;
if (width < 0)
width = 0;
neg = (z.sign != 0);
leadspaces = width - neg - (decimals > 0);
z.sign = 0;
/*
* Find the 2^N power of ten which is greater than or equal
* to the number, calculating it the first time if necessary.
*/
_tenpowers_[0] = _ten_;
depth = 0;
while ((_tenpowers_[depth].len < z.len) || (zrel(_tenpowers_[depth], z) <= 0)) {
depth++;
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.
*/
digits = 1;
output = FALSE;
n = 0;
putpoint = 0;
rightnums[0].len = 0;
leftnums[0] = z;
for (;;) {
while (n < depth) {
i = depth - n - 1;
zdiv(leftnums[n], _tenpowers_[i], &quo, &rem);
if (!iszero(quo))
digits += (1L << i);
n++;
leftnums[n] = quo;
rightnums[n] = rem;
}
i = leftnums[n].v[0];
if (output || i || (n == 0)) {
if (!output) {
output = TRUE;
if (decimals > digits)
leadspaces -= decimals;
else
leadspaces -= digits;
while (--leadspaces >= 0)
PUTCHAR(' ');
if (neg)
PUTCHAR('-');
if (decimals) {
putpoint = (digits - decimals);
if (putpoint <= 0) {
PUTCHAR('.');
while (++putpoint <= 0)
PUTCHAR('0');
putpoint = 0;
}
}
}
i += '0';
PUTCHAR(i);
if (--putpoint == 0)
PUTCHAR('.');
}
while (rightnums[n].len == 0) {
if (n <= 0)
return;
if (leftnums[n].len)
freeh(leftnums[n].v);
n--;
}
freeh(leftnums[n].v);
leftnums[n] = rightnums[n];
rightnums[n].len = 0;
}
}
/*
* Convert a string to a number in rational, floating point,
* exponential notation, hex, or octal.
* q = atoq(string);
*/
NUMBER *
atoq(s)
register char *s;
{
register NUMBER *q;
register char *t;
ZVALUE div, newnum, newden, tmp;
long decimals, exp;
BOOL hex, negexp;
q = qalloc();
decimals = 0;
exp = 0;
negexp = FALSE;
hex = FALSE;
t = s;
if ((*t == '+') || (*t == '-'))
t++;
if ((*t == '0') && ((t[1] == 'x') || (t[1] == 'X'))) {
hex = TRUE;
t += 2;
}
while (((*t >= '0') && (*t <= '9')) || (hex &&
(((*t >= 'a') && (*t <= 'f')) || ((*t >= 'A') && (*t <= 'F')))))
t++;
if (*t == '/') {
t++;
atoz(t, &q->den);
} else if ((*t == '.') || (*t == 'e') || (*t == 'E')) {
if (*t == '.') {
t++;
while ((*t >= '0') && (*t <= '9')) {
t++;
decimals++;
}
}
/*
* Parse exponent if any
*/
if ((*t == 'e') || (*t == 'E')) {
t++;
if (*t == '+')
t++;
else if (*t == '-') {
negexp = TRUE;
t++;
}
while ((*t >= '0') && (*t <= '9')) {
exp = (exp * 10) + *t++ - '0';
if (exp > 1000000)
error("Exponent too large");
}
}
ztenpow(decimals, &q->den);
}
atoz(s, &q->num);
if (qiszero(q)) {
qfree(q);
return qlink(&_qzero_);
}
/*
* Apply the exponential if any
*/
if (exp) {
ztenpow(exp, &tmp);
if (negexp) {
zmul(q->den, tmp, &newden);
freeh(q->den.v);
q->den = newden;
} else {
zmul(q->num, tmp, &newnum);
freeh(q->num.v);
q->num = newnum;
}
freeh(tmp.v);
}
/*
* Reduce the fraction to lowest terms
*/
if (isunit(q->num) || isunit(q->den))
return q;
zgcd(q->num, q->den, &div);
if (isunit(div))
return q;
zquo(q->num, div, &newnum);
freeh(q->num.v);
zquo(q->den, div, &newden);
freeh(q->den.v);
q->num = newnum;
q->den = newden;
return q;
}
/*
* 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.
*/
static void
atoz(s, res)
register char *s;
ZVALUE *res;
{
ZVALUE z, ztmp, digit;
HALF digval;
BOOL minus;
long shift;
minus = FALSE;
shift = 0;
if (*s == '+')
s++;
else if (*s == '-') {
minus = TRUE;
s++;
}
if (*s == '0') { /* possibly hex, octal, or binary */
s++;
if ((*s >= '0') && (*s <= '7')) {
shift = 3;
} else if ((*s == 'x') || (*s == 'X')) {
shift = 4;
s++;
} else if ((*s == 'b') || (*s == 'B')) {
shift = 1;
s++;
}
}
digit.v = &digval;
digit.len = 1;
digit.sign = 0;
z = _zero_;
while (*s) {
digval = *s++;
if ((digval >= '0') && (digval <= '9'))
digval -= '0';
else if ((digval >= 'a') && (digval <= 'f') && shift)
digval -= ('a' - 10);
else if ((digval >= 'A') && (digval <= 'F') && shift)
digval -= ('A' - 10);
else if (digval == '.')
continue;
else
break;
if (shift)
zshift(z, shift, &ztmp);
else
zmuli(z, 10L, &ztmp);
freeh(z.v);
zadd(ztmp, digit, &z);
freeh(ztmp.v);
}
trim(&z);
if (minus && !iszero(z))
z.sign = 1;
*res = z;
}
/*
* Parse a number in any of the various legal forms, and return the count
* of characters that are part of a legal number. Numbers can be either a
* decimal integer, possibly two decimal integers separated with a slash, a
* floating point or exponential number, a hex number beginning with "0x",
* a binary number beginning with "0b", or an octal number beginning with "0".
* The flags argument modifies the end of number testing for ease in handling
* fractions or complex numbers. Minus one is returned if the number format
* is definitely illegal.
*/
long
qparse(cp, flags)
register char *cp;
{
char *oldcp;
oldcp = cp;
if ((*cp == '+') || (*cp == '-'))
cp++;
if ((*cp == '+') || (*cp == '-'))
return -1;
if ((*cp == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) { /* hex */
cp += 2;
while (((*cp >= '0') && (*cp <= '9')) ||
((*cp >= 'a') && (*cp <= 'f')) ||
((*cp >= 'A') && (*cp <= 'F')))
cp++;
goto done;
}
if ((*cp == '0') && ((cp[1] == 'b') || (cp[1] == 'B'))) { /* binary */
cp += 2;
while ((*cp == '0') || (*cp == '1'))
cp++;
goto done;
}
if ((*cp == '0') && (cp[1] >= '0') && (cp[1] <= '9')) { /* octal */
while ((*cp >= '0') && (*cp <= '7'))
cp++;
goto done;
}
/*
* Number is decimal, but can still be a fraction or real or exponential.
*/
while ((*cp >= '0') && (*cp <= '9'))
cp++;
if (*cp == '/' && flags & QPF_SLASH) { /* fraction */
cp++;
while ((*cp >= '0') && (*cp <= '9'))
cp++;
goto done;
}
if (*cp == '.') { /* floating point */
cp++;
while ((*cp >= '0') && (*cp <= '9'))
cp++;
}
if ((*cp == 'e') || (*cp == 'E')) { /* exponential */
cp++;
if ((*cp == '+') || (*cp == '-'))
cp++;
if ((*cp == '+') || (*cp == '-'))
return -1;
while ((*cp >= '0') && (*cp <= '9'))
cp++;
}
done:
if (((*cp == 'i') || (*cp == 'I')) && (flags & QPF_IMAG))
cp++;
if ((*cp == '.') || ((*cp == '/') && (flags & QPF_SLASH)) ||
((*cp >= '0') && (*cp <= '9')) ||
((*cp >= 'a') && (*cp <= 'z')) ||
((*cp >= 'A') && (*cp <= 'Z')))
return -1;
return (cp - oldcp);
}
/* END CODE */