/* Copyright (c) 1993 Stephen F. White */ #include "cool.h" #include "proto.h" #include "servers.h" #include "netio.h" #include "execute.h" static void do_match (int full); static void base_echo( char newline ); /********* * base_echo * By Robin Powell * * Does the echo work, taking an argument for whether to send * a newline or not ***********/ void base_echo( char newline ) { Var str, id; int fd = -1; if (nargs > 1) { id = pop(); if (id.type != NUM) { var_free(id); var_free(pop()); raise(E_ARGTYPE); return; } else { fd = id.v.num; } } str = pop(); if (str.type != STR) { raise (E_ARGTYPE); } else { str.v.str = string_output( str.v.str ); if (fd >= 0) { tell_fd(fd, str.v.str->str, newline); pushn(0); } else { tell(frame.this.id, str.v.str->str, newline); pushn (0); } } var_free(str); } void op_echo(void) { base_echo( 1 ); } /*********** * op_echon * By Robin Powell * * Echo without the trailing \r\n ***********/ void op_echon(void) { base_echo( 0 ); } #define LINELEN 256 void op_echo_file (void) { Var str, id; FILE *f; char line[LINELEN], *newline; char filename[MAX_PATH_LEN]; SOCKET fd = INVALID_SOCKET; if (nargs > 1) { id = pop(); if (id.type != NUM) { var_free(id); var_free(pop()); raise(E_ARGTYPE); return; } else { fd = id.v.num; } } str = pop(); if (str.type != STR) { raise (E_ARGTYPE); } else if (str_in("..", str.v.str->str) /* no ..'s allowed */ || str.v.str->str[0] == '/') { /* can't start with a / either */ raise (E_FILE); } else { sprintf(filename, "%s/%s", RUNDIR, str.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'; if (fd != INVALID_SOCKET) { tell_fd(fd, line, 1); } else { tell(frame.this.id, line, 1); } } fclose (f); pushn (0); } } var_free(str); } void op_connect(void) { Var hostname, port; int r; port = pop(); hostname = pop(); if (hostname.type != STR || port.type != NUM) { raise(E_ARGTYPE); } else { r = net_open_connect(frame.this.id, hostname.v.str->str, (short) port.v.num); if (r < 0 || r > -5) { switch (r) { case -1: /* address lookup failed */ raise(E_SERVERNF); case -2: /* address lookup timed out */ raise(E_TIMEOUT); case -3: /* connect() failed */ raise(E_SERVERNF); case -4: /* connection in progress */ pushn(-1); break; default: raise(E_SERVERNF); } } else { pushn(r); } } var_free(hostname); var_free(port); } void op_clone (void) { Var oid; Object *o = clone (frame.this); 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) { destroy (frame.this); disconnect(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; 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) { pushn ((long) time ((time_t *) 0)); } void op_ctime (void) { 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) { 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[RAND () % strlen (saltstuff)]; salt[1] = saltstuff[RAND () % 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; s.type = STR; s.v.str = string_cpy (check_malloc ()); push (s); } void op_cache_stats (void) { Var s; s.type = STR; s.v.str = string_cpy (cache_stats ()); push (s); } void op_explode (void) { Var str, tokv, ret; int nwords; char *sep; char space[2]=" "; sep = space; 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 = malloc( strlen( tokv.v.str->str ) + 1 ); strcpy( sep, tokv.v.str->str ); 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); if( sep != space ) free( sep ); } void op_random (void) { Var arg; arg = pop (); if (arg.type != NUM) { raise (E_ARGTYPE); } else if (arg.v.num <= 0) { raise (E_RANGE); } else { pushn (RAND () % arg.v.num + 1); } } void op_setadd (void) { Var new, list; 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; 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; 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 (!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; name = pop (); if (name.type != STR) { raise (E_ARGTYPE); } else { for (l = this->locks; l; l = l->next) { if (!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; int i; 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->on->ref++; e->m->ref++; e->blocked_on = BL_TIMER; gettimeofday (&e->timeout_at, 0); e->timeout_at.tv_sec += delay.v.num; e->timeout_at.tv_usec = 0; if (delay.v.num >= sleep_to_refresh) { e->ticks = 0; e->age = 0; } e->sp--; event_add (e); ex_state = BLOCKED; } } void op_typeof (void) { Var arg; arg = pop (); pushn (arg.type); } void op_lengthof (void) { Var arg; arg = pop (); switch (arg.type) { case STR: pushn (arg.v.str->len); break; case LIST: pushn (arg.v.list->len); break; case MAP: pushn (arg.v.map->num); break; default: raise (E_ARGTYPE); break; } var_free (arg); } void op_serverof (void) { Var arg; 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; 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; 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; 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; 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 prepad = 0; 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; 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; 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: case MAP: 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; 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: case MAP: 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; 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: case MAP: 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; 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); } void op_strcmp(void) { Var s1, s2; s2 = pop(); s1 = pop(); if (s1.type != STR || s2.type != STR) { raise(E_ARGTYPE); } else { pushn(strcmp(s1.v.str->str, s2.v.str->str)); } var_free(s1); var_free(s2); } void op_tolower(void) { Var arg, ret; int i; arg = pop(); if (arg.type != STR) { raise(E_ARGTYPE); } else { if (arg.v.str->ref == 1) { ret = arg; arg.v.str->ref++; } else { ret.type = STR; ret.v.str = string_cpy(arg.v.str->str); } for (i = 0; i < arg.v.str->len; i++) { ret.v.str->str[i] = tolower((int)arg.v.str->str[i]); } push(ret); } var_free(arg); } void op_toupper(void) { Var arg, ret; int i; arg = pop(); if (arg.type != STR) { raise(E_ARGTYPE); } else { if (arg.v.str->ref == 1) { ret = arg; arg.v.str->ref++; } else { ret.type = STR; ret.v.str = string_cpy(arg.v.str->str); } for (i = 0; i < arg.v.str->len; i++) { ret.v.str->str[i] = toupper((int)arg.v.str->str[i]); } push(ret); } var_free(arg); } void op_f_index(void) { Var s1, s2, case_matters; case_matters.type = NUM; case_matters.v.num = 0; if (nargs > 2) { case_matters = pop(); } s2 = pop(); s1 = pop(); if (s1.type != STR || s2.type != STR || case_matters.type != NUM) { raise(E_ARGTYPE); } else { pushn(strindex(s1.v.str->str, s2.v.str->str, case_matters.v.num)); } var_free(s1); var_free(s2); var_free(case_matters); } void op_rindex(void) { Var s1, s2, case_matters; case_matters.type = NUM; case_matters.v.num = 0; if (nargs > 2) { case_matters = pop(); } s2 = pop(); s1 = pop(); if (s1.type != STR || s2.type != STR || case_matters.type != NUM) { raise(E_ARGTYPE); } else { pushn(strrindex(s1.v.str->str, s2.v.str->str, case_matters.v.num)); } var_free(s1); var_free(s2); var_free(case_matters); }