/*
* BASIC by Phil Cockcroft
*/
#include "bas.h"
/*
* This file contains subroutines used by many commands
*/
/* stringcompare will compare two strings and return a valid
* logical value
*/
#ifdef __STDC__
static STR lgcvt(void);
static void _scale(double *, int *, int *, int);
static CHAR *d_expand(double, int, int *);
static int pat_exp(CHAR *, int, int, int, int, int, int, int, int, int);
#else
static STR lgcvt();
static void _scale();
static CHAR *d_expand();
static int pat_exp();
#endif
void
stringcompare()
{
register ival i;
register CHAR *p,*q;
ival cursiz;
int reslt=0;
int c;
STR st1, st2;
st1 = stringeval();
cursiz = st1->strlen;
if( (c=getch()) == 0)
error(SYNTAX);
st2 = stringeval();
if( (i = ((cursiz > st2->strlen) ? st2->strlen : cursiz)) != 0){
/*
* make i the minimum of gcursiz and cursiz
*/
st2->strlen -= i; st1->strlen -= i;
p = st1->strval; q = st2->strval; /* set pointers */
do{
if(*p != *q){ /* do the compare */
if( UC(*p) > UC(*q) )
reslt++;
else
reslt--;
FREE_STR(st2);
FREE_STR(st1);
compare(c,reslt);
return;
}
p++;
q++;
}while(--i);
}
if(st1->strlen)
reslt++;
else if(st2->strlen)
reslt--;
FREE_STR(st2);
FREE_STR(st1);
compare(c,reslt);
}
/* given the comparison operator 'c' then returns a value
* given that 'reslt' has a value of:-
* 0: equal
* 1: greater than
* -1: less than
*/
void
compare(c,reslt)
register int c;
register int reslt;
{
vartype= IVAL;
if(c==EQL){
if(!reslt)
goto true;
}
else if(c==LTEQ){
if( reslt<=0)
goto true;
}
else if(c==NEQE){
if( reslt)
goto true;
}
else if(c==LTTH){
if( reslt<0)
goto true;
}
else if(c==GTEQ){
if( reslt>=0)
goto true;
}
else if(c==GRTH){
if( reslt>0)
goto true;
}
else
error(SYNTAX);
res.i=0; /* false */
return;
true:
res.i = -1;
}
/* converts a number in 'res' to a string in gblock
* the string will have a space at the start if it is positive
*/
STR
mgcvt()
{
int sign, decpt;
int ndigit=9;
register CHAR *p1, *p2;
register int i;
CHAR *necvt();
STR st;
if(vartype== IVAL) /* integer deal with them separately */
return(lgcvt());
#ifndef SOFTFP
p1 = necvt(res.f, ndigit+2, &decpt, &sign);
#else
p1 = necvt(&res, ndigit+2, &decpt, &sign);
#endif
st = ALLOC_STR( (ival) (ndigit + 10)); /* needed for extra chars */
*st->strval = sign ? '-' : ' ';
/*
* work out number of significant digits
*/
if(ndigit > 1){
p2 = p1 + ndigit-1;
do {
if(*p2 != '0')
break;
ndigit--;
}while(--p2 > p1);
}
p2 = &st->strval[1];
/*
* if need 1e2 representation then do it here
*/
if(decpt < 0 || decpt > 9){
decpt--;
/*
* print out first digit
*/
*p2++ = *p1++;
/*
* if more than one digit print out
*/
if(ndigit != 1){
*p2++ = '.';
for (i=1; i<ndigit; i++)
*p2++ = *p1++;
}
/*
* now do the exponent
*/
*p2++ = 'e';
if (decpt<0) {
decpt = -decpt;
*p2++ = '-';
}
for(i = 1000 ; i ; i /= 10)
if(decpt >= i)
break;
for( ; i ; i /= 10){
*p2++ = decpt/i + '0';
decpt %= i;
}
}
else {
/*
* print out in normal notation
* add a zero if decimal point is at start of line
*/
if(!decpt){
*p2++ = '0';
*p2++ = '.';
}
for(i=1; i<=ndigit; i++){
*p2++ = *p1++;
if(i==decpt && i != ndigit)
*p2++ = '.';
}
while(ndigit++<decpt)
*p2++ = '0';
}
*p2 = 0;
st->strlen = p2 - st->strval;
return(st);
}
/* integer version of above - a very simple algorithm */
static STR
lgcvt()
{
CHAR s[16];
register CHAR *p,*q;
STR st;
ival fl=0;
#ifdef BIG_INTS
register unsigned long l;
#else
register unsigned l;
#endif
l= res.i;
q = p = &s[14];
*p = 0;
if(res.i <0){
fl++;
l= -l;
}
do{
*p-- = l%10 +'0';
}while(l/=10 );
if(fl)
*p ='-';
else
*p =' ';
fl = q - p + 1;
st = ALLOC_STR(fl);
VOID strmov(st->strval, p, fl);
return(st);
}
/* get a linenumber or if no linenumber return a -1
* used by all routines with optional linenumbers
*/
lnumb
getlin()
{
register lnumb l=0;
register int c;
c=getch();
if(!isnumber(c)){
point--;
return(NOLNUMB);
}
do{
if(l>=6553 )
error(7);
l= l*10 + (c-'0');
c= UC(*point++);
}while(isnumber(c));
point--;
return(l);
}
/* getline() gets a line number and returns a valid pointer
* to it, if there is no linenumber or the line is not there
* then there is an error. Used by 'goto' etc.
*/
lpoint
getline()
{
register lnumb l=0;
register lpoint p;
register int c;
c=getch();
if(!isnumber(c))
error(5);
do{
if(l>=6553)
error(7);
l= l*10+(c-'0');
c= UC(*point++);
}while(isnumber(c));
point--;
/* speed it up a bit no need to search the whole lot */
if((p = stocurlin) == 0 || l < p->linnumb)
p = program;
for(; p ;p = p->next)
if(p->linnumb == l)
return(p);
error(6);
return(0);
}
lpoint
getsline(l)
register lnumb l;
{
register lpoint p;
if(l == NOLNUMB)
return(program);
if((p = stocurlin) == 0 || l < p->linnumb)
p = program;
for(; p ;p = p->next)
if(p->linnumb == l)
return(p);
error(6); /* undefined line */
return(0);
}
lnumb
getrline(p)
register lpoint p;
{
register lpoint op, savp;
if(p->linnumb != CONTLNUMB)
return(p->linnumb);
savp = 0;
for(op = program ; op != p ; op = op->next)
if(op->linnumb != CONTLNUMB)
savp = op;
if(savp)
return(savp->linnumb);
return(0);
}
void
prsline(str, p)
register lpoint p;
const char *str;
{
register lpoint op, savp;
lnumb xline;
if(str)
prints( (char *)str);
if(p->linnumb != CONTLNUMB){
printd(p->linnumb);
return;
}
savp = 0;
for(op = program ; op != p ; op = op->next)
if(op->linnumb != CONTLNUMB)
savp = op;
if(savp == 0)
op = program;
else
op = savp;
for(xline = 0; op != p ; op = op->next)
xline++;
printd(savp ? savp->linnumb : 0);
prints(".");
printd(xline);
}
/* printlin() returns a pointer to a string representing the
* the numeric value of the linenumber. linenumbers are unsigned
* quantities.
*/
char *
printlin(l)
register lnumb l;
{
static char ln[7];
register char *p;
p = &ln[5];
do{
*p-- = l %10 + '0';
}while(l/=10);
p++;
return(p);
}
void
printd(l)
lnumb l;
{
prints(printlin(l));
}
/* routine used to check the type of expression being evaluated
* used by print and eval.
* A string expression returns a value of '1'
* A numeric expression returns a value of '0'
*/
int
checktype()
{
register CHAR *tpoint;
register int c;
if( (c= UC(*point)) & SPECIAL){
if(c != FN){
if(c == IFUNCA || c == IFUNCN || c == NOTT)
return(0);
if(c == OFUNC)
error(SYNTAX);
return(1);
}
tpoint = point + 1;
c = UC(*tpoint++);
}
else {
if(isnumber(c) || c=='.' || c== '-' || c=='(' || c == '&')
return(0);
if(c=='"' || c=='`')
return(1);
tpoint= point + 1;
}
if(!isletter(c))
error(SYNTAX);
while(ispnchar(tpoint))
tpoint++;
if(*tpoint == D_STR)
return(1);
else if(*tpoint == D_INT || *tpoint == D_FLT)
return(0);
return(tcharmap[c - 'A'] == SVAL);
}
int
slen(s)
char *s;
{
register char *p = s;
while(*p)
p++;
return(p - s);
}
/* print out a message , used for all types of 'basic' messages
*/
void
prints(s)
char *s;
{
VOID write(1, s,(unsigned)slen(s));
}
/* copy a string from a to b returning the last address used in b
*/
CHAR *
str_cpy(a,b)
register CHAR *a,*b;
{
#ifdef mips
while( (*b = *a) != 0)
b++, a++;
return(b);
#else
while(*b++ = *a++);
return(--b);
#endif
}
void
set_mem(b, len, c)
register CHAR *b;
register ival len;
register int c;
{
if(!len)
return;
do {
*b++ = (CHAR)c;
} while(--len);
}
void
clr_mem(b, len)
register CHAR *b;
register ival len;
{
if(len >= 20 && ((long)b & WORD_MASK) == 0){
register ival xlen = len & WORD_MASK;
len >>= WORD_SHIFT;
do {
/*LINTED*/
*(int *)b = 0;
b += sizeof(int);
} while(--len);
len = xlen;
}
if(!len)
return;
do {
*b++ = 0;
} while(--len);
}
#ifndef lint
#define DUFF
#endif
CHAR *
strmov(dest, src, len)
register CHAR *dest, *src;
register ival len;
{
#if defined(i386) || defined(mips)
#ifdef mips
if( (((long)dest | (long) src) & WORD_MASK) == 0)
#endif
{
int xlen = len & WORD_MASK;
if( (len >>= WORD_SHIFT) != 0){
#ifdef DUFF
register int dufslen = (len + 7) >> 3;
switch(len &= 7){
case 0:
do {
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
case 7:
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
case 6:
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
case 5:
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
case 4:
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
case 3:
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
case 2:
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
case 1:
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
}while(--dufslen);
}
#else
do {
/*LINTED*/
*(int *)dest = *(int *)src;
dest += sizeof(int);
src += sizeof(int);
}while(--len);
#endif
}
len = xlen;
}
#endif
if(len)
do {
#ifdef mips
*dest = *src;
dest++;
src++;
#else
*dest++ = *src++;
#endif
}while(--len);
return(dest);
}
#ifdef SOFTFP
int getop(void);
int
getnumb(buf, bufp)
CHAR *buf, **bufp;
{
CHAR *tmp;
int ret;
tmp = point;
point = buf;
ret = getop();
if(bufp)
*bufp = point;
point = tmp;
return(ret);
}
#else
/* convert an ascii string into a number. If it is possibly an integer
* return an integer.
* Otherwise return a double ( in res )
* should never overflow. One day I may fix the non floating point one.
*/
static const struct cvttab {
double cval;
int expn;
double maxval;
} cvttab[] = {
#ifdef IEEEMATHS
#ifdef NOT_NEEDED
/*
* these values are not needed since they just slow the lower
* values down, and they don't improve the performance much for
* large values.
*/
1e256, 256, BIGval/1e256, /* bad value */
1e128, 128, BIGval/1e128, /* bad value also in i386 */
1e64, 64, BIGval/1e64,
#endif
#endif
1e32, 32, BIGval/1e32, /* bad value also in i386 */
1e16, 16, BIGval/1e16,
1e8, 8, BIGval/1e8,
10000, 4, BIGval/10000,
100, 2, BIGval/100,
10, 1, BIGval/10,
0, 0, 0,
};
static const double TEN = 10.0;
#ifdef BIG_INTS
#define MAX_L_INT (MAX_INT / 10)
#else
#define MAX_L_INT 214748364
#endif
int
getnumb(ptr, ptrp)
CHAR *ptr, **ptrp;
{
register double x;
register int c;
register long lx = 0;
long ly;
long lym;
int exp;
int ndigits = 0;
int exponent = 0;
char decp = 0;
char lzeros = 0;
int minus;
itype xx;
const struct cvttab *cp = cvttab;
int seenz = 0;
if(*ptr == '&'){
c = UC(*++ptr);
if(!ishex(c))
return(0);
do {
if((unsigned long)lx &
((unsigned long)0x0FL << (4 * (sizeof(itype) * 2 - 1))))
return(0);
if(isnumber(c))
c -= '0';
else
c = (c & 0x7) + 9;
lx = (lx << 4) | c;
c = UC(*++ptr);
} while(ishex(c));
vartype= IVAL;
res.i = (itype)lx;
if(ptrp)
*ptrp = ptr;
return(1);
}
/*
* first accumulate number in a long since this is
* quicker than doing it in an fp number. When we have a
* large number (>8 digits) then we go back to original algorithm
*/
ly = 0;
for(c = UC(*ptr) ; isnumber(c); c = UC(*++ptr)){
if(lx >= MAX_L_INT){
if(ly)
break;
ly = lx;
lym = 1;
lx = 0;
}
else if(!lzeros){
seenz = 1;
if(c == '0') /* ignore leading zeros */
continue;
lzeros++;
}
if(ly){
if(lym >= MAX_L_INT)
break;
lym *= 10;
}
ndigits++;
lx = lx * 10 + c - '0';
}
if(ly){
x = (double)ly * (double)lym;
if(lx)
x += (double)lx;
}
else if(lx)
x = (double)lx;
else
x = ZERO;
dot: for(; isnumber(c) ; c = UC(*++ptr)){
if(!lzeros){
if(c == '0'){ /* ignore leading zeros */
seenz = 1;
if(decp)
exponent--;
continue;
}
lzeros++;
}
if(ndigits > 17){ /* ignore insignificant digits */
if(!decp)
exponent++;
continue;
}
if(decp)
exponent--;
ndigits++;
/*
* there is a bug in the i386 C compiler which means
* that if I use c in:
* x = x * TEN + c - '0';
* then the bug bites and I get incorrect assembler code
*/
xx = c - '0';
x = x * TEN + xx;
}
if(c == '.'){
if(decp)
return(0);
c = UC(*++ptr);
decp++;
goto dot;
}
if(!ndigits && !seenz)
return(0);
if(c == 'e' || c == 'E'){
minus = 0;
exp = 0;
if( (c = UC(*++ptr)) == '+' || c == '-'){
if(c == '-')
minus++;
c = UC(*++ptr);
}
if(!isnumber(c))
return(0);
do {
if(exp < BIGEXP)
exp = exp * 10 + c - '0';
c = UC(*++ptr);
} while(isnumber(c));
if(minus)
exponent -= exp;
else
exponent += exp;
}
if(exponent < 0){
exp = -exponent;
do {
while(exp >= cp->expn){
x /= cp->cval;
exp -= cp->expn;
}
} while((++cp)->expn);
}
else if(exponent > 0){
/*
* Evaluate maximum values that can be multiplied
* by a given multiple of ten
*/
do {
while(exponent >= cp->expn){
if(x >= cp->maxval)
return(0);
x *= cp->cval;
exponent -= cp->expn;
}
} while((++cp)->expn);
}
res.f = x;
vartype = RVAL;
switch(*ptr){
case D_FLT:
ptr++;
break;
case D_INT:
ptr++;
if(conv(&res)){
if(ptrp)
*ptrp = ptr;
return(0);
}
vartype = IVAL;
break;
default:
#ifndef UNPORTABLE
if(x > MAXint || x < MINint)
break;
#endif
xx = (itype)x; /* see if x is == an integer */
/*
* shouldn't need a cast below but there is a bug in the 68000
* compiler which does the comparison wrong without it.
*/
if( (double) xx == x){
vartype = IVAL;
res.i = xx;
}
break;
}
if(ptrp)
*ptrp = ptr;
return(1);
}
/*
* perform the opposite function to getop. Return a string
* from a double. with at most ndigits.
* rounding is performed on the last digit.
*/
CHAR *
necvt(x, ndigits, decpt, sign)
int ndigits, *decpt, *sign;
double x;
{
register CHAR *p;
int nd;
_scale(&x, decpt, sign, 1);
p = d_expand(x, ndigits, &nd);
if(nd)
(*decpt)++;
return(p);
}
static CHAR *
d_expand(x, ndigits, nd)
double x;
int ndigits;
int *nd;
{
register CHAR *p;
register int ndig = ndigits + 1;
register int c;
static CHAR tbuf[30];
for(p = tbuf, *p++ = '0'; ndig ; ndig--){
*p = (CHAR)x;
x = (x - (double)*p) * TEN;
*p++ += '0';
}
if(nd == 0){
*p = 0;
return(tbuf + 1);
}
*nd = 0;
c = (int)*--p;
*p = 0;
if(c >= '5'){
for(;;){
++(*--p);
if(*p <= '9')
break;
*p = '0';
}
if(p == tbuf){
*nd = 1;
tbuf[ndigits] = 0;
return(tbuf);
}
}
return(tbuf + 1);
}
#endif
static void
_scale(xp, decpt, sign, zflag)
int *sign;
double *xp;
int *decpt;
int zflag;
{
register const struct cvttab *cp = cvttab;
register int exp;
double x = *xp;
double y;
#ifdef NaN
if(NaN(x))
if(IsPosNAN(x))
x = BIG;
else
x = BIGminus;
#endif
*sign = 0;
if(x <= ZERO){
if(x == ZERO){
*decpt = zflag;
return;
}
x = -x;
*sign = 1;
}
exp = 0;
if(x < ONE){
do {
while( (y = x * cp->cval) < TEN){
x = y;
exp -= cp->expn;
}
}while( (++cp)->expn);
}
else if(x >= TEN){
do {
while(x >= cp->cval){
x /= cp->cval;
exp += cp->expn;
}
}while( (++cp)->expn);
}
*decpt = exp + 1;
*xp = x;
}
STR
mathpat(spat)
STR spat;
{
register STR outstr;
CHAR *pat, *epat;
ival olen;
ival flen;
int done_spec = 0;
int dot = 0;
int lchars = 0;
int comma = 0;
int rchars = 0;
int exp = 0;
int last_char = 0;
int first_char = 0;
int dolars = 0;
int stars = 0;
CHAR *p;
CHAR num_buf[64];
olen = spat->strlen;
if(olen < LOC_BUF_SIZ)
olen = LOC_BUF_SIZ;
outstr = ALLOC_STR(olen);
outstr->strlen = 0;
for(pat = spat->strval, epat = pat + spat->strlen ; pat < epat ; pat++){
if(last_char){
for(p = (CHAR *)",.#^+-*$"; *p && *p != *pat ; p++);
if(*p){
bad:;
error(BADFORMAT);
}
}
switch(*pat){
default:
if(done_spec){
if(dolars == 1 && !stars)
goto bad;
flen = pat_exp(num_buf, lchars, rchars, dot,
exp, stars, first_char,
last_char, dolars, comma);
if(outstr->strlen + flen > olen){
RESERVE_SPACE(outstr, olen + flen);
olen += flen;
}
VOID strmov(outstr->strval + outstr->strlen,
num_buf, flen);
outstr->strlen += flen;
done_spec = 0;
dot = 0;
lchars = 0;
rchars = 0;
comma = 0;
exp = 0;
last_char = 0;
first_char = 0;
dolars = 0;
stars = 0;
}
if(*pat == '_' && pat + 1 < epat)
pat++;
if(outstr->strlen >= olen){
RESERVE_SPACE(outstr, (ival)(olen + 32));
olen += 32;
}
outstr->strval[outstr->strlen++] = *pat;
continue;
case ',':
if(comma || dot)
goto bad;
comma = 1;
break;
case '.':
if(dot)
goto bad;
dot = 1;
break;
case '#':
if(dot)
rchars++;
else
lchars++;
break;
case '^':
if(!done_spec || (!rchars && !lchars) || exp)
goto bad;
exp = 0;
while(pat < epat && *pat == '^'){
exp++;
pat++;
}
pat--;
if(exp < 3 || exp > 5)
goto bad;
break;
case '+':
if(!done_spec)
first_char = '+';
else
last_char = '+';
break;
case '-':
if(!done_spec)
goto bad;
else
last_char = '-';
break;
case '*':
if(dolars || stars || lchars || rchars || dot)
goto bad;
while(pat < epat && *pat == '*'){
pat++;
stars++;
}
pat--;
if(stars != 2)
goto bad;
break;
case '$':
if(lchars || rchars || dot)
goto bad;
dolars++;
if(dolars > 2 || (stars && dolars > 1))
goto bad;
break;
}
done_spec = 1;
}
if(done_spec){
flen = pat_exp(num_buf, lchars, rchars, dot, exp, stars,
first_char, last_char, dolars, comma);
if(outstr->strlen + flen > olen)
RESERVE_SPACE(outstr, (ival)(outstr->strlen + flen));
VOID strmov(outstr->strval + outstr->strlen, num_buf, flen);
outstr->strlen += flen;
}
return(outstr);
}
static int
pat_exp(num_buf, lchars, rchars, dot, exp, stars,
first_char, last_char, dolars, comma)
CHAR *num_buf;
int lchars, rchars, dot, exp, stars,
first_char, last_char, dolars, comma;
{
int nd;
int decpt;
int sign;
CHAR *p;
CHAR *pf;
int ndigits;
int sdigs;
int scnt;
int fsign;
pf = num_buf;
if(!lchars && !rchars){
bad:;
error(BADFORMAT);
}
/*
* now we have to do the conversion
*/
if(vartype != RVAL)
cvt(&res);
_scale(&res.f, &decpt, &sign, (exp && lchars));
/*
* now check to see if this thing will fit in
* the given space. if we aren't in scientific
* notation, then we limit ourselves
* to a max of 24
*/
if(decpt > 24 && !exp)
goto bad;
if(lchars + rchars > 24)
goto bad;
fsign = first_char || (!last_char && sign);
if(exp){
ndigits = rchars;
if(lchars){
decpt--;
ndigits++;
}
p = d_expand(res.f, ndigits, &nd);
if(nd)
decpt++;
if(decpt >= 100 && exp < 5)
*pf++ = '%';
if(stars){
if(!fsign)
*pf++ = '*';
*pf++ = '*';
}
while(lchars > 1){
*pf++ = stars ? '*' : ' ';
lchars--;
}
if(fsign)
*pf++ = sign ? '-' : '+';
if(dolars)
*pf++ = '$';
if(lchars > 0)
*pf++ = *p++;
else if(last_char)
*pf++ = '0';
if(dot)
*pf++ = '.';
while(rchars > 0){
*pf++ = *p++;
rchars--;
}
*pf++ = 'E';
if(exp > 3 || decpt < 0)
*pf++ = (decpt < 0) ? '-' : '+';
if(decpt < 0)
decpt = -decpt;
if(decpt >= 100 || exp > 4){
*pf++ = (decpt / 100) + '0';
decpt %= 100;
}
*pf++ = (decpt / 10) + '0';
*pf++ = (decpt % 10) + '0';
}
else {
sdigs = stars + lchars;
if(dolars > 1)
sdigs++;
ndigits = decpt + rchars;
if(ndigits > 24)
goto bad;
if(ndigits < 0)
ndigits = 0;
p = d_expand(res.f, ndigits, &nd);
if(nd){
decpt++;
p[ndigits] = '0';
}
if(decpt > sdigs){
*pf++ = '%';
sdigs = decpt;
}
if(decpt > 0)
scnt = sdigs - decpt;
else
scnt = sdigs - 1;
if(!last_char && sign)
scnt--;
while(scnt > 0){
scnt--;
*pf++ = stars ? '*' : ' ';
}
if(fsign)
*pf++ = sign ? '-' : '+';
if(dolars)
*pf++ = '$';
if(lchars && decpt <= 0)
*pf++ = '0';
sdigs = decpt;
while(sdigs > 0){
*pf++ = *p++;
sdigs--;
if(comma && sdigs && (sdigs % 3) == 0)
*pf++ = ',';
}
if(dot)
*pf++ = '.';
while(rchars > 0){
if(decpt < 0){
*pf++ = '0';
decpt++;
}
else
*pf++ = *p++;
rchars--;
}
}
if(last_char == '+' || (last_char && sign))
*pf++ = sign ? '-' : '+';
return(pf - num_buf);
}
syntax highlighted by Code2HTML, v. 0.9.1