/* funmath.c - math and logic functions */
/* $Id: funmath.c,v 1.27 2004/02/23 04:35:14 rmg Exp $ */
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include <math.h>
#include "alloc.h" /* required by mudconf */
#include "flags.h" /* required by mudconf */
#include "htab.h" /* required by mudconf */
#include "mudconf.h" /* required by code */
#include "db.h" /* required by externs */
#include "externs.h" /* required by code */
#include "functions.h" /* required by code */
/* ---------------------------------------------------------------------------
* fval: copy the floating point value into a buffer and make it presentable
*/
#ifdef FLOATING_POINTS
#define FP_SIZE ((sizeof(double) + sizeof(unsigned int) - 1) / sizeof(unsigned int))
#define FP_EXP_WEIRD 0x1
#define FP_EXP_ZERO 0x2
typedef union {
double d;
unsigned int u[FP_SIZE];
} fp_union_uint;
static unsigned int fp_check_weird(buff, bufc, result)
char *buff, **bufc;
double result;
{
static fp_union_uint fp_sign_mask, fp_exp_mask, fp_mant_mask, fp_val;
static const double d_zero = 0.0;
static int fp_initted = 0;
unsigned int fp_sign, fp_exp, fp_mant;
int i;
if (!fp_initted) {
memset(fp_sign_mask.u, 0, sizeof(fp_sign_mask));
memset(fp_exp_mask.u, 0, sizeof(fp_exp_mask));
memset(fp_val.u, 0, sizeof(fp_val));
fp_exp_mask.d = 1.0 / d_zero;
fp_sign_mask.d = -1.0 / fp_exp_mask.d;
for (i = 0; i < FP_SIZE; i++) {
fp_mant_mask.u[i] = ~(fp_exp_mask.u[i] | fp_sign_mask.u[i]);
}
fp_initted = 1;
}
fp_val.d = result;
fp_sign = fp_mant = 0;
fp_exp = FP_EXP_ZERO | FP_EXP_WEIRD;
for (i = 0; (i < FP_SIZE) && fp_exp; i++) {
if (fp_exp_mask.u[i]) {
unsigned int x = (fp_exp_mask.u[i] & fp_val.u[i]);
if (x == fp_exp_mask.u[i]) {
/* these bits are all set. can't be zero
* exponent, but could still be max (weird)
* exponent.
*/
fp_exp &= ~FP_EXP_ZERO;
} else if (x == 0) {
/* none of these bits are set. can't be max
* exponent, but could still be zero exponent.
*/
fp_exp &= ~FP_EXP_WEIRD;
} else {
/* some bits were set but not others. can't
* be either zero exponent or max exponent.
*/
fp_exp = 0;
}
}
fp_sign |= (fp_sign_mask.u[i] & fp_val.u[i]);
fp_mant |= (fp_mant_mask.u[i] & fp_val.u[i]);
}
if (fp_exp == FP_EXP_WEIRD) {
if (fp_sign) {
safe_chr('-', buff, bufc);
}
if (fp_mant) {
safe_known_str("NaN", 3, buff, bufc);
} else {
safe_known_str("Inf", 3, buff, bufc);
}
}
return fp_exp;
}
static void fval(buff, bufc, result)
char *buff, **bufc;
double result;
{
char *p, *buf1;
switch (fp_check_weird(buff, bufc, result)) {
case FP_EXP_WEIRD: return;
case FP_EXP_ZERO: result = 0.0; /* FALLTHRU */
default: break;
}
buf1 = *bufc;
safe_tprintf_str(buff, bufc, "%.6f", result); /* get double val
* into buffer
*/
**bufc = '\0';
p = strrchr(buf1, '0');
if (p == NULL) { /* remove useless trailing 0's */
return;
} else if (*(p + 1) == '\0') {
while (*p == '0') {
*p-- = '\0';
}
*bufc = p + 1;
}
p = strrchr(buf1, '.'); /* take care of dangling '.' */
if ((p != NULL) && (*(p + 1) == '\0')) {
*p = '\0';
*bufc = p;
}
/* Handle bogus result of "-0" from sprintf. Yay, cclib. */
if (!strcmp(buf1, "-0")) {
*buf1 = '0';
*bufc = buf1 + 1;
}
}
#else
#define fval(b,p,n) safe_ltos(b,p,n)
#endif
/* ---------------------------------------------------------------------------
* Constant math funcs: PI, E
*/
FUNCTION(fun_pi)
{
safe_known_str("3.141592654", 11, buff, bufc);
}
FUNCTION(fun_e)
{
safe_known_str("2.718281828", 11, buff, bufc);
}
/* ---------------------------------------------------------------------------
* Math operations on one number: SIGN, ABS, FLOOR, CEIL, ROUND, TRUNC,
* INC, DEC, SQRT, EXP, LN, [A][SIN,COS,TAN][D]
*/
FUNCTION(fun_sign)
{
NVAL num;
num = aton(fargs[0]);
if (num < 0) {
safe_known_str("-1", 2, buff, bufc);
} else {
safe_bool(buff, bufc, (num > 0));
}
}
FUNCTION(fun_abs)
{
NVAL num;
num = aton(fargs[0]);
if (num == 0) {
safe_chr('0', buff, bufc);
} else if (num < 0) {
fval(buff, bufc, -num);
} else {
fval(buff, bufc, num);
}
}
FUNCTION(fun_floor)
{
#ifdef FLOATING_POINTS
char *oldp;
NVAL x;
oldp = *bufc;
x = floor(aton(fargs[0]));
switch (fp_check_weird(buff, bufc, x)) {
case FP_EXP_WEIRD: return;
case FP_EXP_ZERO: x = 0.0; /* FALLTHRU */
default: break;
}
safe_tprintf_str(buff, bufc, "%.0f", x);
/* Handle bogus result of "-0" from sprintf. Yay, cclib. */
if (!strcmp(oldp, "-0")) {
*oldp = '0';
*bufc = oldp + 1;
}
#else
fval(buff, bufc, aton(fargs[0]));
#endif
}
FUNCTION(fun_ceil)
{
#ifdef FLOATING_POINTS
char *oldp;
NVAL x;
oldp = *bufc;
x = ceil(aton(fargs[0]));
switch (fp_check_weird(buff, bufc, x)) {
case FP_EXP_WEIRD: return;
case FP_EXP_ZERO: x = 0.0; /* FALLTHRU */
default: break;
}
safe_tprintf_str(buff, bufc, "%.0f", x);
/* Handle bogus result of "-0" from sprintf. Yay, cclib. */
if (!strcmp(oldp, "-0")) {
*oldp = '0';
*bufc = oldp + 1;
}
#else
fval(buff, bufc, aton(fargs[0]));
#endif
}
FUNCTION(fun_round)
{
#ifdef FLOATING_POINTS
const char *fstr;
char *oldp;
NVAL x;
oldp = *bufc;
switch (atoi(fargs[1])) {
case 1:
fstr = "%.1f";
break;
case 2:
fstr = "%.2f";
break;
case 3:
fstr = "%.3f";
break;
case 4:
fstr = "%.4f";
break;
case 5:
fstr = "%.5f";
break;
case 6:
fstr = "%.6f";
break;
default:
fstr = "%.0f";
break;
}
x = aton(fargs[0]);
switch (fp_check_weird(buff, bufc, x)) {
case FP_EXP_WEIRD: return;
case FP_EXP_ZERO: x = 0.0; /* FALLTHRU */
default: break;
}
safe_tprintf_str(buff, bufc, (char *)fstr, x);
/* Handle bogus result of "-0" from sprintf. Yay, cclib. */
if (!strcmp(oldp, "-0")) {
*oldp = '0';
*bufc = oldp + 1;
}
#else
fval(buff, bufc, aton(fargs[0]));
#endif
}
FUNCTION(fun_trunc)
{
#ifdef FLOATING_POINTS
NVAL x;
x = aton(fargs[0]);
x = (x >= 0) ? floor(x) : ceil(x);
switch (fp_check_weird(buff, bufc, x)) {
case FP_EXP_WEIRD: return;
case FP_EXP_ZERO: x = 0.0; /* FALLTHRU */
default: break;
}
fval(buff, bufc, x);
#else
fval(buff, bufc, aton(fargs[0]));
#endif
}
FUNCTION(fun_inc)
{
safe_ltos(buff, bufc, atoi(fargs[0]) + 1);
}
FUNCTION(fun_dec)
{
safe_ltos(buff, bufc, atoi(fargs[0]) - 1);
}
FUNCTION(fun_sqrt)
{
NVAL val;
val = aton(fargs[0]);
if (val < 0) {
safe_str("#-1 SQUARE ROOT OF NEGATIVE", buff, bufc);
} else if (val == 0) {
safe_chr('0', buff, bufc);
} else {
fval(buff, bufc, sqrt((double)val));
}
}
FUNCTION(fun_exp)
{
fval(buff, bufc, exp((double)aton(fargs[0])));
}
FUNCTION(fun_ln)
{
NVAL val;
val = aton(fargs[0]);
if (val > 0)
fval(buff, bufc, log((double)val));
else
safe_str("#-1 LN OF NEGATIVE OR ZERO", buff, bufc);
}
FUNCTION(handle_trig)
{
NVAL val;
int oper, flag;
static double (* const trig_funcs[8])(double) = {
sin, cos, tan, NULL, /* XXX no cotangent function */
asin, acos, atan, NULL
};
flag = Func_Flags(fargs);
oper = flag & TRIG_OPER;
val = aton(fargs[0]);
if ((flag & TRIG_ARC) && !(flag & TRIG_TAN) &&
((val < -1) || (val > 1))) {
safe_tprintf_str(buff, bufc, "#-1 %s ARGUMENT OUT OF RANGE",
((FUN *)fargs[-1])->name);
return;
}
if ((flag & TRIG_DEG) && !(flag & TRIG_ARC))
val = val * (M_PI / 180);
val = (trig_funcs[oper])((double)val);
if ((flag & TRIG_DEG) && (flag & TRIG_ARC))
val = (val * 180) / M_PI;
fval(buff, bufc, val);
}
/* ---------------------------------------------------------------------------
* Math comparison funcs: GT, GTE, LT, LTE, EQ, NEQ, NCOMP
*/
FUNCTION(fun_gt)
{
safe_bool(buff, bufc, (aton(fargs[0]) > aton(fargs[1])));
}
FUNCTION(fun_gte)
{
safe_bool(buff, bufc, (aton(fargs[0]) >= aton(fargs[1])));
}
FUNCTION(fun_lt)
{
safe_bool(buff, bufc, (aton(fargs[0]) < aton(fargs[1])));
}
FUNCTION(fun_lte)
{
safe_bool(buff, bufc, (aton(fargs[0]) <= aton(fargs[1])));
}
FUNCTION(fun_eq)
{
safe_bool(buff, bufc, (aton(fargs[0]) == aton(fargs[1])));
}
FUNCTION(fun_neq)
{
safe_bool(buff, bufc, (aton(fargs[0]) != aton(fargs[1])));
}
FUNCTION(fun_ncomp)
{
NVAL x, y;
x = aton(fargs[0]);
y = aton(fargs[1]);
if (x == y) {
safe_chr('0', buff, bufc);
} else if (x < y) {
safe_str("-1", buff, bufc);
} else {
safe_chr('1', buff, bufc);
}
}
/* ---------------------------------------------------------------------------
* Two-argument math functions: SUB, DIV, FLOORDIV, FDIV, MODULO, REMAINDER,
* POWER, LOG
*/
FUNCTION(fun_sub)
{
fval(buff, bufc, aton(fargs[0]) - aton(fargs[1]));
}
FUNCTION(fun_div)
{
int top, bot;
/* The C / operator is only fully specified for non-negative
* operands, so we try not to give it negative operands here
*/
top = atoi(fargs[0]);
bot = atoi(fargs[1]);
if (bot == 0) {
safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
return;
}
if (top < 0) {
if (bot < 0)
top = (-top) / (-bot);
else
top = -((-top) / bot);
} else {
if (bot < 0)
top = -(top / (-bot));
else
top = top / bot;
}
safe_ltos(buff, bufc, top);
}
FUNCTION(fun_floordiv)
{
int top, bot, res;
/* The C / operator is only fully specified for non-negative
* operands, so we try not to give it negative operands here
*/
top = atoi(fargs[0]);
bot = atoi(fargs[1]);
if (bot == 0) {
safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
return;
}
if (top < 0) {
if (bot < 0) {
res = (-top) / (-bot);
} else {
res = -((-top) / bot);
if (top % bot)
res--;
}
} else {
if (bot < 0) {
res = -(top / (-bot));
if (top % bot)
res--;
} else {
res = top / bot;
}
}
safe_ltos(buff, bufc, res);
}
FUNCTION(fun_fdiv)
{
NVAL bot;
bot = aton(fargs[1]);
if (bot == 0) {
safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
} else {
fval(buff, bufc, (aton(fargs[0]) / bot));
}
}
FUNCTION(fun_modulo)
{
int top, bot;
/* The C % operator is only fully specified for non-negative
* operands, so we try not to give it negative operands here
*/
top = atoi(fargs[0]);
bot = atoi(fargs[1]);
if (bot == 0)
bot = 1;
if (top < 0) {
if (bot < 0)
top = -((-top) % (-bot));
else
top = (bot - ((-top) % bot)) % bot;
} else {
if (bot < 0)
top = -(((-bot) - (top % (-bot))) % (-bot));
else
top = top % bot;
}
safe_ltos(buff, bufc, top);
}
FUNCTION(fun_remainder)
{
int top, bot;
/* The C % operator is only fully specified for non-negative
* operands, so we try not to give it negative operands here
*/
top = atoi(fargs[0]);
bot = atoi(fargs[1]);
if (bot == 0)
bot = 1;
if (top < 0) {
if (bot < 0)
top = -((-top) % (-bot));
else
top = -((-top) % bot);
} else {
if (bot < 0)
top = top % (-bot);
else
top = top % bot;
}
safe_ltos(buff, bufc, top);
}
FUNCTION(fun_power)
{
NVAL val1, val2;
val1 = aton(fargs[0]);
val2 = aton(fargs[1]);
if (val1 < 0) {
safe_str("#-1 POWER OF NEGATIVE", buff, bufc);
} else {
fval(buff, bufc, pow((double)val1, (double)val2));
}
}
FUNCTION(fun_log)
{
NVAL val, base;
VaChk_Range(1, 2);
val = aton(fargs[0]);
if (nfargs == 2)
base = aton(fargs[1]);
else
base = 10;
if ((val <= 0) || (base <= 0))
safe_str("#-1 LOG OF NEGATIVE OR ZERO", buff, bufc);
else if (base == 1)
safe_str("#-1 DIVISION BY ZERO", buff, bufc);
else
fval(buff, bufc, log((double)val) / log((double)base));
}
/* ------------------------------------------------------------------------
* Bitwise two-argument integer math functions: SHL, SHR, BAND, BOR, BNAND
*/
FUNCTION(fun_shl)
{
safe_ltos(buff, bufc, atoi(fargs[0]) << atoi(fargs[1]));
}
FUNCTION(fun_shr)
{
safe_ltos(buff, bufc, atoi(fargs[0]) >> atoi(fargs[1]));
}
FUNCTION(fun_band)
{
safe_ltos(buff, bufc, atoi(fargs[0]) & atoi(fargs[1]));
}
FUNCTION(fun_bor)
{
safe_ltos(buff, bufc, atoi(fargs[0]) | atoi(fargs[1]));
}
FUNCTION(fun_bnand)
{
safe_ltos(buff, bufc, atoi(fargs[0]) & ~(atoi(fargs[1])));
}
/* ---------------------------------------------------------------------------
* Multi-argument math functions: ADD, MUL, MAX, MIN
*/
FUNCTION(fun_add)
{
NVAL sum;
int i;
if (nfargs < 2) {
safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
} else {
sum = aton(fargs[0]);
for (i = 1; i < nfargs; i++) {
sum += aton(fargs[i]);
}
fval(buff, bufc, sum);
}
return;
}
FUNCTION(fun_mul)
{
NVAL prod;
int i;
if (nfargs < 2) {
safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
} else {
prod = aton(fargs[0]);
for (i = 1; i < nfargs; i++) {
prod *= aton(fargs[i]);
}
fval(buff, bufc, prod);
}
return;
}
FUNCTION(fun_max)
{
int i;
NVAL max, val;
if (nfargs < 1) {
safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
} else {
max = aton(fargs[0]);
for (i = 1; i < nfargs; i++) {
val = aton(fargs[i]);
max = (max < val) ? val : max;
}
fval(buff, bufc, max);
}
}
FUNCTION(fun_min)
{
int i;
NVAL min, val;
if (nfargs < 1) {
safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
} else {
min = aton(fargs[0]);
for (i = 1; i < nfargs; i++) {
val = aton(fargs[i]);
min = (min > val) ? val : min;
}
fval(buff, bufc, min);
}
}
/* ---------------------------------------------------------------------------
* Integer point distance functions: DIST2D, DIST3D
*/
FUNCTION(fun_dist2d)
{
int d;
double r;
d = atoi(fargs[0]) - atoi(fargs[2]);
r = (double)(d * d);
d = atoi(fargs[1]) - atoi(fargs[3]);
r += (double)(d * d);
d = (int)(sqrt(r) + 0.5);
safe_ltos(buff, bufc, d);
}
FUNCTION(fun_dist3d)
{
int d;
double r;
d = atoi(fargs[0]) - atoi(fargs[3]);
r = (double)(d * d);
d = atoi(fargs[1]) - atoi(fargs[4]);
r += (double)(d * d);
d = atoi(fargs[2]) - atoi(fargs[5]);
r += (double)(d * d);
d = (int)(sqrt(r) + 0.5);
safe_ltos(buff, bufc, d);
}
/* ---------------------------------------------------------------------------
* Math "accumulator" operations on a list: LADD, LMAX, LMIN
*/
FUNCTION(fun_ladd)
{
NVAL sum;
char *cp, *curr;
Delim isep;
if (nfargs == 0) {
safe_chr('0', buff, bufc);
return;
}
VaChk_Only_In(2);
sum = 0;
cp = trim_space_sep(fargs[0], &isep);
while (cp) {
curr = split_token(&cp, &isep);
sum += aton(curr);
}
fval(buff, bufc, sum);
}
FUNCTION(fun_lmax)
{
NVAL max, val;
char *cp, *curr;
Delim isep;
VaChk_Only_In(2);
cp = trim_space_sep(fargs[0], &isep);
if (cp) {
curr = split_token(&cp, &isep);
max = aton(curr);
while (cp) {
curr = split_token(&cp, &isep);
val = aton(curr);
if (max < val)
max = val;
}
fval(buff, bufc, max);
}
}
FUNCTION(fun_lmin)
{
NVAL min, val;
char *cp, *curr;
Delim isep;
VaChk_Only_In(2);
cp = trim_space_sep(fargs[0], &isep);
if (cp) {
curr = split_token(&cp, &isep);
min = aton(curr);
while (cp) {
curr = split_token(&cp, &isep);
val = aton(curr);
if (min > val)
min = val;
}
fval(buff, bufc, min);
}
}
/* ---------------------------------------------------------------------------
* Operations on a single vector: VMAG, VUNIT
* (VDIM is implemented by fun_words)
*/
FUNCTION(handle_vector)
{
char **v1;
int n, i, oper;
NVAL tmp, res = 0;
Delim isep, osep;
oper = Func_Mask(VEC_OPER);
if (oper == VEC_UNIT) {
VaChk_Only_In_Out(3);
} else {
VaChk_Only_In(2);
}
/* split the list up, or return if the list is empty */
if (!fargs[0] || !*fargs[0]) {
return;
}
n = list2arr(&v1, LBUF_SIZE, fargs[0], &isep);
/* calculate the magnitude */
for (i = 0; i < n; i++) {
tmp = aton(v1[i]);
res += tmp * tmp;
}
/* if we're just calculating the magnitude, return it */
if (oper == VEC_MAG) {
if (res > 0) {
fval(buff, bufc, sqrt(res));
} else {
safe_chr('0', buff, bufc);
}
XFREE(v1, "handle_vector.v1");
return;
}
if (res <= 0) {
safe_str("#-1 CAN'T MAKE UNIT VECTOR FROM ZERO-LENGTH VECTOR",
buff, bufc);
XFREE(v1, "handle_vector.v1");
return;
}
res = sqrt(res);
fval(buff, bufc, aton(v1[0]) / res);
for (i = 1; i < n; i++) {
print_sep(&osep, buff, bufc);
fval(buff, bufc, aton(v1[i]) / res);
}
XFREE(v1, "handle_vector.v1");
}
/* ---------------------------------------------------------------------------
* Operations on a pair of vectors: VADD, VSUB, VMUL, VDOT
*/
FUNCTION(handle_vectors)
{
Delim isep, osep;
int oper;
char **v1, **v2;
NVAL scalar;
int n, m, i;
oper = Func_Mask(VEC_OPER);
if (oper != VEC_DOT) {
VaChk_Only_In_Out(4);
} else {
/* dot product returns a scalar, so no output delim */
VaChk_Only_In(3);
}
/*
* split the list up, or return if the list is empty
*/
if (!fargs[0] || !*fargs[0] || !fargs[1] || !*fargs[1]) {
return;
}
n = list2arr(&v1, LBUF_SIZE, fargs[0], &isep);
m = list2arr(&v2, LBUF_SIZE, fargs[1], &isep);
/* It's okay to have vmul() be passed a scalar first or second arg,
* but everything else has to be same-dimensional.
*/
if ((n != m) &&
!((oper == VEC_MUL) && ((n == 1) || (m == 1)))) {
safe_str("#-1 VECTORS MUST BE SAME DIMENSIONS", buff, bufc);
XFREE(v1, "handle_vectors.v1");
XFREE(v2, "handle_vectors.v2");
return;
}
switch (oper) {
case VEC_ADD:
fval(buff, bufc, aton(v1[0]) + aton(v2[0]));
for (i = 1; i < n; i++) {
print_sep(&osep, buff, bufc);
fval(buff, bufc, aton(v1[i]) + aton(v2[i]));
}
break;
case VEC_SUB:
fval(buff, bufc, aton(v1[0]) - aton(v2[0]));
for (i = 1; i < n; i++) {
print_sep(&osep, buff, bufc);
fval(buff, bufc, aton(v1[i]) - aton(v2[i]));
}
break;
case VEC_MUL:
/* if n or m is 1, this is scalar multiplication.
* otherwise, multiply elementwise.
*/
if (n == 1) {
scalar = aton(v1[0]);
fval(buff, bufc, aton(v2[0]) * scalar);
for (i = 1; i < m; i++) {
print_sep(&osep, buff, bufc);
fval(buff, bufc, aton(v2[i]) * scalar);
}
} else if (m == 1) {
scalar = aton(v2[0]);
fval(buff, bufc, aton(v1[0]) * scalar);
for (i = 1; i < n; i++) {
print_sep(&osep, buff, bufc);
fval(buff, bufc, aton(v1[i]) * scalar);
}
} else {
/* vector elementwise product.
*
* Note this is a departure from TinyMUX, but an imitation
* of the PennMUSH behavior: the documentation in Penn
* claims it's a dot product, but the actual behavior
* isn't. We implement dot product separately!
*/
fval(buff, bufc, aton(v1[0]) * aton(v2[0]));
for (i = 1; i < n; i++) {
print_sep(&osep, buff, bufc);
fval(buff, bufc, aton(v1[i]) * aton(v2[i]));
}
}
break;
case VEC_DOT:
/* dot product: (a,b,c) . (d,e,f) = ad + be + cf
*
* no cross product implementation yet: it would be
* (a,b,c) x (d,e,f) = (bf - ce, cd - af, ae - bd)
*/
scalar = 0;
for (i = 0; i < n; i++) {
scalar += aton(v1[i]) * aton(v2[i]);
}
fval(buff, bufc, scalar);
break;
default:
/* If we reached this, we're in trouble. */
safe_str("#-1 UNIMPLEMENTED", buff, bufc);
break;
}
XFREE(v1, "handle_vectors.v1");
XFREE(v2, "handle_vectors.v2");
}
/* ---------------------------------------------------------------------------
* Simple boolean funcs: NOT, NOTBOOL, T
*/
FUNCTION(fun_not)
{
safe_bool(buff, bufc, !atoi(fargs[0]));
}
FUNCTION(fun_notbool)
{
safe_bool(buff, bufc, !xlate(fargs[0]));
}
FUNCTION(fun_t)
{
safe_bool(buff, bufc, xlate(fargs[0]));
}
/* ---------------------------------------------------------------------------
* Multi-argument boolean funcs: various combinations of
* [L,C][AND,OR,XOR][BOOL]
*/
FUNCTION(handle_logic)
{
Delim isep;
int flag, oper, i, val;
char *str, *tbuf, *bp;
int (*cvtfun)(char *);
flag = Func_Flags(fargs);
cvtfun = (flag & LOGIC_BOOL) ? xlate : (int (*)(char *))atoi;
oper = (flag & LOGIC_OPER);
/* most logic operations on an empty string should be false */
val = 0;
if (flag & LOGIC_LIST) {
/* the arguments come in a pre-evaluated list */
VaChk_Only_In(2);
bp = trim_space_sep(fargs[0], &isep);
while (bp) {
tbuf = split_token(&bp, &isep);
val = ((oper == LOGIC_XOR) && val) ? !cvtfun(tbuf) : cvtfun(tbuf);
if (((oper == LOGIC_AND) && !val) ||
((oper == LOGIC_OR) && val))
break;
}
} else if (nfargs < 2) {
/* separate arguments, but not enough of them */
safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
return;
} else if (flag & FN_NO_EVAL) {
/* separate, unevaluated arguments */
tbuf = alloc_lbuf("handle_logic");
for (i = 0; i < nfargs; i++) {
str = fargs[i];
bp = tbuf;
exec(tbuf, &bp, player, caller, cause,
EV_EVAL | EV_STRIP | EV_FCHECK, &str, cargs, ncargs);
val = ((oper == LOGIC_XOR) && val) ? !cvtfun(tbuf) : cvtfun(tbuf);
if (((oper == LOGIC_AND) && !val) ||
((oper == LOGIC_OR) && val))
break;
}
free_lbuf(tbuf);
} else {
/* separate, pre-evaluated arguments */
for (i = 0; i < nfargs; i++) {
val = ((oper == LOGIC_XOR) && val) ? !cvtfun(fargs[i]) : cvtfun(fargs[i]);
if (((oper == LOGIC_AND) && !val) ||
((oper == LOGIC_OR) && val))
break;
}
}
safe_bool(buff, bufc, val);
}