/* * 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 */