// This file is part of SmallBASIC
//
// SmallBasic Variable Manager.
//
// This program is distributed under the terms of the GPL v2.0 or later
// Download the GNU Public License (GPL) from www.gnu.org
//
// Copyright(C) 2000 Nicholas Christopoulos
#include "common/sys.h"
#include "common/str.h"
#include "common/var.h"
#include "common/smbas.h"
#include "common/sberr.h"
#include "common/var_uds.h"
#include "common/var_hash.h"
#define ARR_ALLOC 256
/*
* initialize a variable
*/
void v_init(var_t *v) {
v->type = V_INT;
v->const_flag = 0;
v->v.i = 0;
}
/*
* creates and returns a new variable
*/
var_t *v_new() {
var_t *ptr;
ptr = (var_t *) tmp_alloc(sizeof(var_t));
v_init(ptr);
return ptr;
}
/*
* release variable
*/
void v_free(var_t *v) {
int i;
var_t *elem;
switch (v->type) {
case V_STR:
if (v->v.p.ptr) {
tmp_free(v->v.p.ptr);
}
v->v.p.ptr = NULL;
v->v.p.size = 0;
break;
case V_ARRAY:
if (v->v.a.size) {
if (v->v.a.ptr) {
for (i = 0; i < v->v.a.size; i++) {
elem = (var_t *) (v->v.a.ptr + (sizeof(var_t) * i));
v_free(elem);
}
tmp_free(v->v.a.ptr);
v->v.a.ptr = NULL;
v->v.a.size = 0;
}
}
break;
case V_UDS:
uds_free(v);
break;
case V_HASH:
hash_free_var(v);
break;
}
v_init(v);
}
/*
* returns true if the user's program must use this var as an empty var
* this is usefull for arrays
*/
int v_isempty(var_t *var) {
switch (var->type) {
case V_STR:
return (strlen((char *) var->v.p.ptr) == 0);
case V_INT:
return (var->v.i == 0);
case V_UDS:
return uds_is_empty(var);
case V_HASH:
return hash_is_empty(var);
case V_PTR:
return (var->v.ap.p == 0);
case V_NUM:
return (var->v.n == 0.0);
case V_ARRAY:
return (var->v.a.size == 0);
}
return 1;
}
/*
* returns the length of the variable
*/
int v_length(var_t *var) {
char tmpsb[64];
switch (var->type) {
case V_STR:
return strlen((char *) var->v.p.ptr);
case V_UDS:
return uds_length(var);
case V_HASH:
return hash_length(var);
case V_PTR:
ltostr(var->v.ap.p, tmpsb);
return strlen(tmpsb);
case V_INT:
ltostr(var->v.i, tmpsb);
return strlen(tmpsb);
case V_NUM:
ftostr(var->v.n, tmpsb);
return strlen(tmpsb);
case V_ARRAY:
return var->v.a.size;
}
return 1;
}
/*
* returns the floating-point value of the variable v
*/
var_num_t v_getval(var_t *v) {
if (v == NULL) {
err_evsyntax();
} else {
switch (v->type) {
case V_UDS:
return uds_to_int(v);
case V_HASH:
return hash_to_int(v);
case V_PTR:
return v->v.ap.p;
case V_INT:
return v->v.i;
case V_NUM:
return v->v.n;
case V_STR:
return numexpr_sb_strtof((char *) v->v.p.ptr);
default:
err_varisarray();
}
}
return 0;
}
/*
* returns the integer value of the variable v
*/
var_int_t v_igetval(var_t *v) {
if (v == 0) {
err_evsyntax();
} else {
switch (v->type) {
case V_UDS:
return uds_to_int(v);
case V_HASH:
return hash_to_int(v);
case V_PTR:
return v->v.ap.p;
case V_INT:
return v->v.i;
case V_NUM:
return v->v.n;
case V_STR:
return numexpr_strtol((char *) v->v.p.ptr);
default:
err_varisarray();
}
}
return 0;
}
/*
* return array element pointer
*/
#if defined(OS_ADDR16)
var_t *v_getelemptr(var_t *v, word index)
#else
var_t *v_getelemptr(var_t *v, dword index)
#endif
{
if (v->type == V_ARRAY) {
if (index < v->v.a.size)
return (var_t *) (v->v.a.ptr + (index * sizeof(var_t)));
else {
err_vararridx(index, v->v.a.size);
return NULL;
}
}
err_varisnotarray();
return NULL;
}
/*
* resize an existing array
*/
#if defined(OS_ADDR16)
void v_resize_array(var_t *v, word size)
#else
void v_resize_array(var_t *v, dword size)
#endif
{
byte *prev;
var_t *elem;
#if defined(OS_ADDR16)
word i;
#else
dword i;
#endif
if (v->type == V_ARRAY) {
if (size < 0) {
err_evargerr();
return;
}
if (size == 0) {
v_free(v);
v->type = V_ARRAY;
v->v.a.size = 0;
v->v.a.ptr = NULL;
v->v.a.ubound[0] = v->v.a.lbound[0] = opt_base;
v->v.a.maxdim = 1;
} else if (v->v.a.size > size) {
// resize down
// free vars
for (i = size; i < v->v.a.size; i++) {
elem = (var_t *) (v->v.a.ptr + (sizeof(var_t) * i));
v_free(elem);
}
#if defined(OS_ADDR32)
// do not resize array
prev = NULL;
#else
// resize & copy
prev = v->v.a.ptr;
v->v.a.ptr = tmp_alloc(size * sizeof(var_t));
memcpy(v->v.a.ptr, prev, size * sizeof(var_t));
#endif
// array data
v->v.a.size = size;
v->v.a.ubound[0] = v->v.a.lbound[0] + (size - 1);
v->v.a.maxdim = 1;
//
if (prev) {
tmp_free(prev);
}
} else if (v->v.a.size < size) {
// resize up
#if defined(OS_ADDR32)
// if there is space do not resize
if (v->v.a.size == 0) {
prev = v->v.a.ptr;
v->v.a.ptr = tmp_alloc((size + ARR_ALLOC) * sizeof(var_t));
} else {
if (mem_handle_size((mem_t)v->v.a.ptr) < size * sizeof(var_t)) {
// resize & copy
prev = v->v.a.ptr;
v->v.a.ptr = tmp_alloc((size + ARR_ALLOC) * sizeof(var_t));
if (v->v.a.size > 0)
memcpy(v->v.a.ptr, prev, v->v.a.size * sizeof(var_t));
} else
prev = NULL;
}
#else
// resize & copy
prev = v->v.a.ptr;
v->v.a.ptr = tmp_alloc(size * sizeof(var_t));
if (v->v.a.size > 0) {
memcpy(v->v.a.ptr, prev, v->v.a.size * sizeof(var_t));
}
#endif
// init vars
for (i = v->v.a.size; i < size; i++) {
elem = (var_t *) (v->v.a.ptr + (sizeof(var_t) * i));
v_init(elem);
}
// array data
v->v.a.size = size;
v->v.a.ubound[0] = v->v.a.lbound[0] + (size - 1);
v->v.a.maxdim = 1;
//
if (prev) {
tmp_free(prev);
}
}
} else {
err_varisnotarray();
}
}
/*
* create RxC array
*/
void v_tomatrix(var_t *v, int r, int c) {
var_t *e;
int i;
v_free(v);
v->type = V_ARRAY;
// create data
v->v.a.size = r * c;
v->v.a.ptr = tmp_alloc(sizeof(var_t) * v->v.a.size);
for (i = 0; i < r * c; i++) {
e = (var_t *) (v->v.a.ptr + (sizeof(var_t) * i));
v_init(e);
}
// array info
v->v.a.lbound[0] = v->v.a.lbound[1] = opt_base;
v->v.a.ubound[0] = opt_base + (r - 1);
v->v.a.ubound[1] = opt_base + (c - 1);
v->v.a.maxdim = 2;
}
/*
* create RxC array
*/
var_t *v_new_matrix(int r, int c) {
var_t *v;
v = v_new();
v_tomatrix(v, r, c);
return v;
}
/*
* create array
*/
#if defined(OS_ADDR16)
void v_toarray1(var_t *v, word r)
#else
void v_toarray1(var_t *v, dword r)
#endif
{
var_t *e;
#if defined(OS_ADDR16)
word i;
#else
dword i;
#endif
v_free(v);
v->type = V_ARRAY;
if (r > 0) {
// create data
v->v.a.size = r;
#if defined(OS_ADDR32)
v->v.a.ptr = tmp_alloc(sizeof(var_t) * (v->v.a.size + ARR_ALLOC));
#else
v->v.a.ptr = tmp_alloc(sizeof(var_t) * v->v.a.size);
#endif
for (i = 0; i < r; i++) {
e = (var_t *) (v->v.a.ptr + (sizeof(var_t) * i));
v_init(e);
}
// array info
v->v.a.maxdim = 1;
v->v.a.lbound[0] = opt_base;
v->v.a.ubound[0] = opt_base + (r - 1);
} else {
v->v.a.size = 0;
v->v.a.ptr = NULL;
v->v.a.lbound[0] = v->v.a.ubound[0] = opt_base;
v->v.a.maxdim = 1;
}
}
/*
* returns true if the variable v is not empty (0 for nums)
*/
int v_is_nonzero(var_t *v) {
switch (v->type) {
case V_INT:
return (v->v.i != 0);
case V_NUM:
// return (v->v.n != 0.0 && v->v.n != -0.0);
return (ABS(v->v.n) > 1E-308);
case V_STR:
return (v->v.p.size != 0);
case V_UDS:
return !uds_is_empty(v);
case V_HASH:
return !hash_is_empty(v);
case V_PTR:
return (v->v.ap.p != 0);
case V_ARRAY:
return (v->v.a.size != 0);
};
return 0;
}
/*
* compare the variable a with the variable b
* returns
* -1 a < b
* +1 a > b
* 0 a = b
*/
int v_compare(var_t *a, var_t *b) {
var_num_t dt;
var_int_t di;
int i, ci;
var_t *ea, *eb;
if (a == 0 || b == 0) {
err_evsyntax();
return 0;
}
if (a->type == V_INT && b->type == V_INT) {
di = (a->v.i - b->v.i);
i = di < 0 ? -1 : di > 0 ? 1 : 0;
return i;
} else if ((a->type == V_INT || a->type == V_NUM) && (b->type == V_INT || b->type == V_NUM)) {
var_num_t left = (a->type == V_NUM) ? a->v.n : a->v.i;
var_num_t right = (b->type == V_NUM) ? b->v.n : b->v.i;
dt = (left - right);
i = dt < 0.0 ? -1 : dt > 0.0 ? 1 : 0;
return i;
}
if ((a->type == V_STR) && (b->type == V_STR)) {
return strcmp(a->v.p.ptr, b->v.p.ptr);
}
if ((a->type == V_STR) && (b->type == V_NUM)) {
if (a->v.p.ptr[0] == '\0' || is_number((char *) a->v.p.ptr)) { // compare
// nums
dt = v_getval(a);
return (dt < b->v.n) ? -1 : ((dt == b->v.n) ? 0 : 1);}
return 1;
}
if ((a->type == V_NUM) && (b->type == V_STR)) {
if (b->v.p.ptr[0] == '\0' || is_number((char *) b->v.p.ptr)) { // compare
// nums
dt = v_getval(b);
return (dt < a->v.n) ? 1 : ((dt == a->v.n) ? 0 : -1);}return
- 1;
}
if ((a->type == V_STR) && (b->type == V_INT)) {
if (a->v.p.ptr[0] == '\0' || is_number((char *) a->v.p.ptr)) { // compare
// nums
di = v_igetval(a);
return (di < b->v.i) ? -1 : ((di == b->v.i) ? 0 : 1);}
return 1;
}
if ((a->type == V_INT) && (b->type == V_STR)) {
if (b->v.p.ptr[0] == '\0' || is_number((char *) b->v.p.ptr)) { // compare
// nums
di = v_igetval(b);
return (di < a->v.i) ? 1 : ((di == a->v.i) ? 0 : -1);}return
- 1;
}
if ((a->type == V_ARRAY) && (b->type == V_ARRAY)) {
// check size
if (a->v.a.size != b->v.a.size) {
if (a->v.a.size < b->v.a.size) {
return -1;
}
return 1;
}
// check every element
for (i = 0; i < a->v.a.size; i++) {
ea = (var_t *) (a->v.a.ptr + sizeof(var_t) * i);
eb = (var_t *) (b->v.a.ptr + sizeof(var_t) * i);
if ((ci = v_compare(ea, eb)) != 0) {
return ci;
}
}
return 0; // equal
}
if (a->type == V_UDS && b->type == V_UDS) {
return uds_compare(a, b);
}
if (a->type == V_HASH && b->type == V_HASH) {
return hash_compare(a, b);
}
err_evtype(); // ndc 01/08/2001
return 1;
}
/*
*/
int v_addtype(var_t *a, var_t *b) {
if (a->type == V_STR) {
return V_STR;
}
if (a->type == V_NUM || b->type == V_NUM) {
return V_NUM;
}
if (a->type == V_INT || b->type == V_STR) {
return V_NUM;
}
return V_INT;
}
/*
* add two variables
* result = a + b
*/
void v_add(var_t *result, var_t *a, var_t *b) {
char tmpsb[64];
if (a->type == V_STR && b->type == V_STR) {
result->type = V_STR;
result->v.p.ptr = (byte *) tmp_alloc(strlen((char *)a->v.p.ptr) + strlen((char *)b->v.p.ptr) +
1);
strcpy((char *) result->v.p.ptr, (char *) a->v.p.ptr);
strcat((char *) result->v.p.ptr, (char *) b->v.p.ptr);
result->v.p.size = strlen((char *) result->v.p.ptr) + 1;
return;
} else if (a->type == V_INT && b->type == V_INT) {
result->type = V_INT;
result->v.i = a->v.i + b->v.i;
return;
} else if (a->type == V_NUM && b->type == V_NUM) {
result->type = V_NUM;
result->v.n = a->v.n + b->v.n;
return;
} else if (a->type == V_NUM && b->type == V_INT) {
result->type = V_NUM;
result->v.n = a->v.n + b->v.i;
return;
} else if (a->type == V_INT && b->type == V_NUM) {
result->type = V_NUM;
result->v.n = a->v.i + b->v.n;
return;
} else if (a->type == V_STR && (b->type == V_INT || b->type == V_NUM)) {
if (is_number((char *) a->v.p.ptr)) {
result->type = V_NUM;
if (b->type == V_INT) {
result->v.n = b->v.i + v_getval(a);
} else {
result->v.n = b->v.n + v_getval(a);
}
} else {
result->type = V_STR;
result->v.p.ptr = (byte *) tmp_alloc(strlen((char *)a->v.p.ptr) + 64);
strcpy((char *) result->v.p.ptr, (char *) a->v.p.ptr);
if (b->type == V_INT) {
ltostr(b->v.i, tmpsb);
} else {
ftostr(b->v.n, tmpsb);
}
strcat((char *) result->v.p.ptr, tmpsb);
result->v.p.size = strlen((char *) result->v.p.ptr) + 1;
}
} else if ((a->type == V_INT || a->type == V_NUM) && b->type == V_STR) {
if (is_number((char *) b->v.p.ptr)) {
result->type = V_NUM;
if (a->type == V_INT
)
result->v.n = a->v.i + v_getval(b);
else
result->v.n = a->v.n + v_getval(b);
} else {
result->type = V_STR;
result->v.p.ptr = (byte *) tmp_alloc(strlen((char *)b->v.p.ptr) + 64);
if (a->type == V_INT) {
ltostr(a->v.i, tmpsb);
} else {
ftostr(a->v.n, tmpsb);
}
strcpy((char *) result->v.p.ptr, tmpsb);
strcat((char *) result->v.p.ptr, (char *) b->v.p.ptr);
result->v.p.size = strlen((char *) result->v.p.ptr) + 1;
}
}
}
/*
* assign (dest = src)
*/
void v_set(var_t *dest, const var_t *src) {
int i;
var_t *dest_vp, *src_vp;
if (src->type == V_UDS) {
uds_set(dest, (const var_p_t) src);
return;
} else if (dest->type == V_UDS) {
// lvalue struct assigned to non-struct rvalue
uds_clear(dest);
return;
} else if (src->type == V_HASH) {
hash_set(dest, (const var_p_t) src);
return;
} else if (dest->type == V_HASH) {
// lvalue struct assigned to non-struct rvalue
hash_clear(dest);
return;
}
v_free(dest);
*dest = *src;
dest->const_flag = 0;
switch (src->type) {
case V_STR:
dest->v.p.ptr = (byte *) tmp_alloc(strlen((char *)src->v.p.ptr) + 1);
strcpy((char *) dest->v.p.ptr, (char *) src->v.p.ptr);
break;
case V_ARRAY:
if (src->v.a.size) {
dest->v.a.ptr = tmp_alloc(src->v.a.size * sizeof(var_t));
// copy each element
for (i = 0; i < src->v.a.size; i++) {
src_vp = (var_t *) (src->v.a.ptr + (sizeof(var_t) * i));
dest_vp = (var_t *) (dest->v.a.ptr + (sizeof(var_t) * i));
v_init(dest_vp);
v_set(dest_vp, src_vp);
}
} else {
dest->v.a.size = 0;
dest->v.a.ptr = NULL;
dest->v.a.ubound[0] = dest->v.a.lbound[0] = opt_base;
dest->v.a.maxdim = 1;
}
break;
case V_PTR:
dest->v.ap = src->v.ap;
dest->type = src->type;
break;
}
}
/*
* return a full copy of the 'source'
*/
var_t *v_clone(const var_t *source) {
var_t *vnew;
vnew = (var_t *) tmp_alloc(sizeof(var_t));
v_init(vnew);
v_set(vnew, source);
return vnew;
}
/*
* add b to a
*/
void v_inc(var_t *a, var_t *b) {
if (a->type == V_INT && b->type == V_INT) {
a->v.i += b->v.i;
} else if (a->type == V_NUM && b->type == V_NUM) {
a->v.n += b->v.n;
} else if (a->type == V_NUM && b->type == V_INT) {
a->v.n += b->v.i;
} else if (a->type == V_INT && b->type == V_NUM) {
a->type = V_NUM;
a->v.n = a->v.i + b->v.n;
} else {
err_varnotnum();
}
}
/*
*/
int v_sign(var_t *x) {
if (x->type == V_INT) {
return (x->v.i < 0) ? -1 : ((x->v.i == 0) ? 0 : 1);}
else if (x->type == V_NUM) {
return (x->v.n < 0) ? -1 : ((x->v.n == 0) ? 0 : 1);
}
err_varnotnum();
return 0;
}
/*
* setup a string variable
*/
void v_createstr(var_t *v, const char *src) {
int l;
l = strlen(src) + 1;
v->type = V_STR;
v->v.p.ptr = tmp_alloc(l);
v->v.p.size = l;
strcpy(v->v.p.ptr, src);
}
/*
* converts the variable to string-variable
*/
void v_tostr(var_t *arg) {
if (arg->type != V_STR) {
char *tmp;
int l;
tmp = tmp_alloc(64);
switch (arg->type) {
case V_UDS:
uds_to_str(arg, tmp, 64);
uds_free(arg);
break;
case V_HASH:
hash_to_str(arg, tmp, 64);
hash_free_var(arg);
break;
case V_PTR:
ltostr(arg->v.ap.p, tmp);
break;
case V_INT:
ltostr(arg->v.i, tmp);
break;
case V_NUM:
ftostr(arg->v.n, tmp);
break;
default:
err_varisarray();
tmp_free(tmp);
return;
}
l = strlen(tmp) + 1;
arg->type = V_STR;
arg->v.p.ptr = tmp_alloc(l);
arg->v.p.size = l;
strcpy(arg->v.p.ptr, tmp);
tmp_free(tmp);
}
}
/*
* set the value of 'var' to string
*/
void v_setstr(var_t *var, const char *string) {
if (var->type != V_STR || strcmp(string, var->v.p.ptr) != 0) {
v_free(var);
var->type = V_STR;
var->v.p.size = strlen(string) + 1;
var->v.p.ptr = tmp_alloc(var->v.p.size);
strcpy(var->v.p.ptr, string);
}
}
void v_setstrn(var_t *var, const char *string, int len) {
if (var->type != V_STR || strncmp(string, var->v.p.ptr, len) != 0) {
v_free(var);
var->type = V_STR;
var->v.p.size = len + 1;
var->v.p.ptr = tmp_alloc(var->v.p.size);
strncpy(var->v.p.ptr, string, len);
var->v.p.ptr[len] = 0;
}
}
/*
* set the value of 'var' to string
*/
void v_setstrf(var_t *var, const char *fmt, ...) {
char *buf;
va_list ap;
va_start(ap, fmt);
#if defined(OS_LIMITED)
buf = tmp_alloc(1024);
#else
buf = tmp_alloc(0x10000);
#endif
vsnprintf(buf, 1024, fmt, ap);
v_setstr(var, buf);
tmp_free(buf);
va_end(ap);
}
/*
* adds a string to current string value
*/
void v_strcat(var_t *var, const char *string) {
if (var->type == V_INT || var->type == V_NUM) {
v_tostr(var);
}
if (var->type == V_STR) {
var->v.p.size = strlen((char *) var->v.p.ptr) + strlen(string) + 1;
var->v.p.ptr = tmp_realloc(var->v.p.ptr, var->v.p.size);
strcat((char *) var->v.p.ptr, string);
} else {
err_typemismatch();
}
}
/*
* set the value of 'var' to n
*/
void v_setreal(var_t *var, var_num_t n) {
v_free(var);
var->type = V_NUM;
var->v.n = n;
}
/*
* set the value of 'var' to i
*/
void v_setint(var_t *var, int32 i) {
v_free(var);
var->type = V_INT;
var->v.i = i;
}
/*
* return the string value of 'var'
*/
char *v_getstr(var_t *var) {
if (var->type != V_STR) {
v_tostr(var);
}
return (char *) var->v.p.ptr;
}
/*
* set the value of 'var' to 'itable' integer array
*/
void v_setintarray(var_t *var, int32 *itable, int count) {
int i;
var_t *elem_p;
v_toarray1(var, count);
for (i = 0; i < count; i++) {
elem_p = (var_t *) (var->v.a.ptr + sizeof(var_t) * i);
v_setint(elem_p, itable[i]);
}
}
/*
* set the value of 'var' to 'itable' real's array
*/
void v_setrealarray(var_t *var, var_num_t *ntable, int count) {
int i;
var_t *elem_p;
v_toarray1(var, count);
for (i = 0; i < count; i++) {
elem_p = (var_t *) (var->v.a.ptr + sizeof(var_t) * i);
v_setreal(elem_p, ntable[i]);
}
}
/*
* set the value of 'var' to 'itable' string array
*/
void v_setstrarray(var_t *var, char **ctable, int count) {
int i;
var_t *elem_p;
v_toarray1(var, count);
for (i = 0; i < count; i++) {
elem_p = (var_t *) (var->v.a.ptr + sizeof(var_t) * i);
v_setstr(elem_p, ctable[i]);
}
}
/*
* set an empty string
*/
void v_zerostr(var_t *r) {
v_free(r);
r->type = V_STR;
r->v.p.ptr = tmp_alloc(1);
r->v.p.ptr[0] = '\0';
r->v.p.size = 1;
}
/*
* convert's a user's string to variable
*
* its decides in what format to store the value
* its used mostly by 'input' functions
*/
void v_input2var(const char *str, var_t *var) {
v_free(var);
if (strlen(str) == 0) { // no data
v_setstr(var, str);
} else {
char *np, *sb;
char buf[64];
int type;
var_int_t lv;
var_num_t dv;
sb = tmp_strdup(str);
np = get_numexpr(sb, buf, &type, &lv, &dv);
if (type == 1 && *np == '\0') {
v_setint(var, lv);
} else if (type == 2 && *np == '\0') {
v_setreal(var, dv);
} else {
v_setstr(var, str);
}
tmp_free(sb);
}
}