/* Copyright (c) 1993 Stephen F. White */
#include <stdio.h>
#ifdef SYSV
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include "config.h"
#include "cool.h"
#include "proto.h"
#include "sys_proto.h"
#include "servers.h"
#include "netio.h"
#include "execute.h"
static void do_match(int full);
void
op_echo(void)
{
Var arg;
frame.pc++; /* ignore nargs */
arg = pop();
if (arg.type != STR) {
raise(E_ARGTYPE);
} else {
tell(frame.this.id, arg.v.str->str);
pushn(0);
}
var_free(arg);
}
#define LINELEN 256
void
op_echo_file(void)
{
Var arg;
FILE *f;
char line[LINELEN], *newline;
char filename[MAX_PATH_LEN];
frame.pc++; /* ignore nargs */
arg = pop();
if (arg.type != STR) {
raise(E_ARGTYPE);
} else if (str_in("..", arg.v.str->str) /* no ..'s allowed */
|| arg.v.str->str[0] == '/') { /* can't start with a / either */
raise(E_FILE);
} else {
sprintf(filename, "%s/%s", RUNDIR, arg.v.str->str);
if (!(f = fopen(filename, "r"))) {
raise(E_FILE);
} else {
while (fgets(line, LINELEN, f)) {
if ((newline = index(line, '\n'))) *newline = '\0';
if ((newline = index(line, '\r'))) *newline = '\0';
tell(frame.this.id, line);
}
fclose(f);
pushn(0);
}
}
var_free(arg);
}
void
op_clone(void)
{
Var oid;
Object *o = clone(frame.this);
frame.pc++; /* ignore nargs */
oid.type = OBJ;
if (o) {
oid.v.obj = o->id;
} else {
oid.v.obj.id = -1;
oid.v.obj.server = 0;
}
push(oid);
}
void
op_destroy(void)
{
frame.pc++; /* ignore nargs */
destroy(frame.this);
boot(frame.this.id); /* just in case it's a player */
this = 0; /* can't do anything with this object anymore */
pushn(0);
ex_state = STOPPED;
}
void
op_chparents(void)
{
Var newparents;
Error r;
frame.pc++; /* ignore nargs */
newparents = pop();
if (newparents.type != LIST) {
var_free(newparents);
raise(E_ARGTYPE);
} else if ((r = check_parents(frame.caller, this, newparents.v.list))
== E_NONE) {
list_free(this->parents);
this->parents = newparents.v.list;
cache_put(this, frame.this.id);
pushn(0);
} else {
var_free(newparents);
raise(r);
}
}
extern time_t time( time_t * );
void
op_time(void)
{
frame.pc++; /* ignore nargs */
pushn( (long) time( (time_t *) 0));
}
void
op_ctime(void)
{
int nargs = frame.m->code[frame.pc++];
Var arg, ret;
long t;
if (nargs == 0) {
t = time( (time_t *) 0);
} else {
arg = pop();
if (arg.type != NUM) {
raise(E_ARGTYPE);
return;
}
t = arg.v.num;
}
ret.type = STR;
ret.v.str = string_cpy(ctime((time_t *) &t));
ret.v.str->str[--ret.v.str->len] = '\0'; /* nuke the newline */
push(ret);
}
void
op_crypt(void)
{
int nargs = frame.m->code[frame.pc++];
Var a1, a2, r;
char salt[3];
static char saltstuff[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
if (nargs > 1) {
a2 = pop();
if (a2.type != STR) {
var_free(a2);
a2 = pop(); var_free(a2);
raise(E_ARGTYPE);
return;
} else if (a2.v.str->len != 2) {
var_free(a2);
a2 = pop(); var_free(a2);
raise(E_RANGE);
return;
}
salt[0] = a2.v.str->str[0];
salt[1] = a2.v.str->str[1];
var_free(a2);
} else {
salt[0] = saltstuff[random() % strlen(saltstuff)];
salt[1] = saltstuff[random() % strlen(saltstuff)];
}
salt[2] = '\0';
a1 = pop();
if (a1.type != STR) {
var_free(a1);
raise(E_ARGTYPE);
return;
}
r.type = STR;
r.v.str = string_cpy(crypt(a1.v.str->str, salt));
var_free(a1);
push(r);
}
void
op_checkmem(void)
{
Var s;
frame.pc++; /* ignore nargs */
s.type = STR;
s.v.str = string_cpy(check_malloc());
push(s);
}
void
op_cache_stats(void)
{
Var s;
frame.pc++; /* ignore nargs */
s.type = STR;
s.v.str = string_cpy(cache_stats());
push(s);
}
void
op_explode(void)
{
Var str, tokv, ret;
int nwords, nargs;
char sep = ' ';
nargs = frame.m->code[frame.pc++];
sep = ' ';
if (nargs > 1) {
tokv = pop();
if (tokv.type != STR) {
var_free(tokv); str = pop(); var_free(str);
raise(E_ARGTYPE); return;
} else if (!tokv.v.str->str[0]) {
var_free(tokv); str = pop(); var_free(str);
raise(E_RANGE); return;
} else {
sep = tokv.v.str->str[0]; var_free(tokv);
}
}
str = pop();
if (str.type != STR) {
raise(E_ARGTYPE);
} else {
nwords = count_words(str.v.str->str, sep);
ret.type = LIST;
ret.v.list = explode(str.v.str->str, sep, nwords);
push(ret);
}
var_free(str);
}
void
op_random(void)
{
Var arg;
frame.pc++; /* skip nargs */
arg = pop();
if (arg.type != NUM) {
raise(E_ARGTYPE);
} else if (arg.v.num <= 0) {
raise(E_RANGE);
} else {
pushn(random() % arg.v.num + 1);
}
}
void
op_setadd(void)
{
Var new, list;
frame.pc++; /* ignore nargs */
new = pop(); list = pop();
if (list.type != LIST) {
var_free(list); var_free(new);
raise(E_ARGTYPE);
} else {
list.v.list = list_setadd(list.v.list, new);
push(list);
}
}
void
op_setremove(void)
{
Var what, list;
frame.pc++; /* ignore nargs */
what = pop(); list = pop();
if (list.type != LIST) {
var_free(list);
raise(E_ARGTYPE);
} else {
list.v.list = list_setremove(list.v.list, what);
push(list);
}
var_free(what);
}
void
op_lock(void)
{
Var name;
Event *e;
Lock *l, *new;
frame.pc++; /* ignore nargs */
name = pop();
if (name.type != STR) {
raise(E_ARGTYPE);
} else {
pushn(0); /* lock() always returns 0 */
for (l = this->locks; l; l = l->next) {
if (!cool_strcasecmp(l->name->str, name.v.str->str)) {
/* found lock */
e = MALLOC(Event, 1);
*e = frame;
e->blocked_on = BL_LOCK;
e->lock = string_dup(name.v.str);
gettimeofday(&e->timeout_at, 0);
e->timeout_at.tv_sec += LOCK_TIMEOUT;
e->msg = 0;
event_add(e);
ex_state = BLOCKED;
break;
}
}
new = MALLOC(Lock, 1);
new->name = string_dup(name.v.str);
new->added_by = frame.msgid;
new->next = 0;
if (!this->locks) {
this->locks = new;
} else {
for (l = this->locks; l->next; l = l->next)
;
l->next = new;
}
}
var_free(name);
}
void
op_unlock(void)
{
Var name;
Lock *l, *prev = 0;
frame.pc++; /* ignore nargs */
name = pop();
if (name.type != STR) {
raise(E_ARGTYPE);
} else {
for (l = this->locks; l; l = l->next) {
if (!cool_strcasecmp(l->name->str, name.v.str->str)
&& l->added_by == frame.msgid) { /* found lock */
if (prev) { /* remove it */
prev->next = l->next;
} else {
this->locks = l->next;
}
string_free(l->name);
FREE(l);
pushn(1);
break;
}
prev = l;
}
if (!l) {
pushn(0);
}
}
var_free(name);
}
void
op_at(void)
{
int i, endat = frame.m->code[frame.pc++];
Var at_time;
at_time = pop();
if (at_time.type != NUM) {
raise(E_ARGTYPE);
pop(); /* at is a statement, has no value */
} else {
Event *e = MALLOC(Event, 1);
*e = frame;
e->blocked_on = BL_TIMER;
e->timeout_at.tv_sec = at_time.v.num;
e->timeout_at.tv_usec = 0;
e->on->ref++;
e->m->ref++;
e->args = list_dup(frame.args);
e->msg = 0;
for (i = 0; i < frame.nvars ;i++) {
e->stack[i] = var_dup(e->stack[i]);
}
e->sp = e->nvars;
e->stack[e->sp].type = NUM;
e->stack[e->sp].v.num = -1; /* add a new terminator */
e->sp++;
event_add(e);
}
frame.pc = endat;
}
void
op_sleep(void)
{
Var delay;
delay.type = NUM; delay.v.num = 0;
if (frame.m->code[frame.pc++]) {
delay = pop();
}
if (delay.type != NUM) {
raise(E_ARGTYPE);
} else {
Event *e = MALLOC(Event, 1);
pushn(0); /* sleep() always returns 0 */
*e = frame;
e->blocked_on = BL_TIMER;
gettimeofday(&e->timeout_at, 0);
e->timeout_at.tv_sec += delay.v.num;
event_add(e);
ex_state = BLOCKED;
}
}
void
op_typeof(void)
{
Var arg;
frame.pc++; /* ignore nargs */
arg = pop();
pushn(arg.type);
}
void
op_lengthof(void)
{
Var arg;
frame.pc++; /* ignore nargs */
arg = pop();
switch (arg.type) {
case STR:
pushn(arg.v.str->len);
break;
case LIST:
pushn(arg.v.list->len);
break;
default:
raise(E_ARGTYPE);
break;
}
var_free(arg);
}
void
op_serverof(void)
{
Var arg;
frame.pc++; /* ignore nargs */
arg = pop();
if (arg.type != OBJ) {
raise(E_ARGTYPE);
} else {
pushn(arg.v.obj.server);
}
var_free(arg);
}
void
op_servername(void)
{
Var arg, ret;
frame.pc++; /* skip nargs */
arg = pop();
if (arg.type != OBJ) {
var_free(arg);
raise(E_ARGTYPE);
} else {
ret.type = STR;
ret.v.str = string_cpy(serv_id2name(arg.v.obj.server));
push(ret);
}
}
void
op_verbs(void)
{
Verbdef *v;
int i, nverbs = 0;
Var ret;
List *info;
frame.pc++; /* ignore nargs */
for (v = this->verbs; v; v = v->next) {
nverbs++;
}
ret.type = LIST;
ret.v.list = list_new(nverbs);
for (v = this->verbs, i = 0; v; v = v->next, i++) {
info = list_new(3);
info->el[0].type = info->el[1].type = info->el[2].type = STR;
info->el[0].v.str = sym_get(this, v->verb);
if(v->prep >= 0) {
info->el[1].v.str = sym_get(this, v->prep);
} else {
info->el[1].v.str = sym_sys(BLANK);
}
info->el[2].v.str = sym_get(this, v->method);
info->el[0].v.str->ref++;
info->el[1].v.str->ref++;
info->el[2].v.str->ref++;
ret.v.list->el[i].type = LIST;
ret.v.list->el[i].v.list = info;
}
push(ret);
}
void
op_vars(void)
{
Vardef *v;
Var ret;
int i, hval;
frame.pc++; /* ignore nargs */
if (this->vars) {
ret.type = LIST;
ret.v.list = list_new(this->vars->num);
i = 0;
for (hval = 0; hval < this->vars->size; hval++) {
for (v = this->vars->table[hval]; v; v = v->next) {
ret.v.list->el[i].type = STR;
ret.v.list->el[i].v.str = sym_get(this, v->name);
ret.v.list->el[i].v.str->ref++;
i++;
}
}
} else {
ret.type = LIST;
ret.v.list = list_dup(empty_list);
}
push(ret);
}
void
op_methods(void)
{
Method *m;
Var ret;
int i, hval;
frame.pc++; /* ignore nargs */
if (this->methods) {
ret.type = LIST;
ret.v.list = list_new(this->methods->num);
i = 0;
for (hval = 0; hval < this->methods->size; hval++) {
for (m = (Method *) this->methods->table[hval]; m;
m = m->next) {
ret.v.list->el[i].type = STR;
ret.v.list->el[i].v.str = sym_get(this, m->name);
ret.v.list->el[i].v.str->ref++;
i++;
}
}
} else {
ret.type = LIST;
ret.v.list = list_dup(empty_list);
}
push(ret);
}
void
op_parents(void)
{
Var ret;
ret.type = LIST;
ret.v.list = list_dup(this->parents);
push(ret);
}
void
op_pad(void)
{
Var what, len, with, ret;
char tok = ' ';
int nargs, prepad = 0;
nargs = frame.m->code[frame.pc++];
if (nargs > 2) {
with = pop();
if (with.type != STR) {
var_free(with); raise(E_ARGTYPE); return;
} else if (with.v.str->len < 1) {
var_free(with); raise(E_RANGE); return;
} else {
tok = with.v.str->str[0];
}
}
len = pop(); what = pop();
if (len.type != NUM || what.type != STR) {
raise(E_ARGTYPE);
} else {
ret.type = STR;
if (what.v.str->ref == 1) { /* if just one ref, */
ret.v.str = string_dup(what.v.str); /* use this str */
} else {
ret.v.str = string_cpy(what.v.str->str); /* make a copy */
}
if (len.v.num < 0) {
len.v.num = -len.v.num;
prepad = 1;
}
if (len.v.num < what.v.str->len) {
ret.v.str->str[len.v.num] = '\0'; /* truncate */
} else if (prepad) { /* pad */
ret.v.str = string_prepad(ret.v.str, len.v.num - what.v.str->len, tok);
} else {
ret.v.str = string_pad(ret.v.str, len.v.num - what.v.str->len, tok);
}
ret.v.str->len = len.v.num;
push(ret);
}
var_free(len); var_free(what);
}
void
op_tostr(void)
{
Var what, ret;
frame.pc++; /* skip nargs */
what = pop();
ret.type = STR;
ret.v.str = string_new(0);
ret.v.str = var_tostring(ret.v.str, what, 1);
push (ret);
var_free(what);
}
void
op_tonum(void)
{
Var arg, ret;
frame.pc++; /* ignore nargs */
ret.type = NUM;
arg = pop();
switch (arg.type) {
case STR:
ret.v.num = atoi(arg.v.str->str);
break;
case NUM:
ret = arg;
break;
case OBJ:
ret.v.num = arg.v.obj.id;
break;
case LIST: case PC:
raise(E_ARGTYPE);
var_free(arg);
return;
case ERR:
ret.v.num = (int) arg.v.err;
break;
}
var_free(arg);
push(ret);
}
void
op_toobj(void)
{
Var arg, ret;
frame.pc++; /* ignore nargs */
ret.type = OBJ;
arg = pop();
switch (arg.type) {
case STR:
if (!parse_obj(arg.v.str->str, &ret.v.obj)) {
ret.v.obj.id = -1;
ret.v.obj.server = 0;
}
var_free(arg);
break;
case NUM:
ret.v.obj.server = 0;
ret.v.obj.id = arg.v.num;
break;
case OBJ:
ret = arg;
break;
case LIST: case PC:
var_free(arg);
raise(E_ARGTYPE);
return;
case ERR:
ret.v.obj.server = 0;
ret.v.obj.id = (int) arg.v.err;
break;
}
push (ret);
}
void
op_toerr(void)
{
Var arg, ret;
frame.pc++; /* ignore nargs */
ret.type = ERR;
ret.v.err = E_NONE;
arg = pop();
switch (arg.type) {
case STR:
if (!parse_err(arg.v.str->str, &ret.v.err)) {
ret.v.err = -1;
}
break;
case NUM:
ret.v.err = arg.v.num;
break;
case OBJ:
ret.v.err = arg.v.obj.id;
break;
case LIST: case PC:
raise(E_ARGTYPE);
var_free(arg);
return;
case ERR:
ret = arg;
break;
}
var_free(arg);
push (ret);
}
static void
do_match(int full)
{
Var template, str, marker;
int nargs = frame.m->code[frame.pc++];
char tok;
if (nargs == 3) {
marker = pop();
if (marker.type != STR || !(tok = marker.v.str->str[0])) {
var_free(marker);
str = pop(); template = pop();
var_free(str); var_free(template); /* free other 2 args */
raise(E_ARGTYPE);
return;
}
var_free(marker);
} else {
tok = ' '; /* default separator is a space */
}
str = pop(); template = pop(); /* get 2 args */
if (template.type != STR || str.type != STR) {
raise(E_ARGTYPE);
} else if (full) {
pushn(match_full(template.v.str->str, str.v.str->str, tok));
} else {
pushn(match(template.v.str->str, str.v.str->str, tok));
}
var_free(template); var_free(str);
}
void
op_match(void)
{
do_match(0);
}
void
op_match_full(void)
{
do_match(1);
}