/* @@@HEAD@@@ // Function operators // // This file contains functions inherent to the system, which are actually // operators, but nobody needs to know. // // Many of these functions require information from the current frame, // which is why they are not modularized (such as object functions) or // they are inherent to the functionality of ColdC // */ #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> #include <fcntl.h> #include "config.h" #include "defs.h" #include "y.tab.h" #include "lookup.h" #include "functions.h" #include "execute.h" #include "data.h" #include "ident.h" #include "object.h" #include "grammar.h" #include "cache.h" #include "dbpack.h" #include "memory.h" #include "opcodes.h" #include "log.h" /* op_log() */ #include "dump.h" /* func_text_dump(), func_backup() */ void func_null_function(void) { if (!func_init_0()) return; push_int(1); } void func_add_parameter(void) { data_t * args; long result; /* Accept a symbol argument a data value to assign to the variable. */ if (!func_init_1(&args, SYMBOL)) return; result = object_add_param(cur_frame->object, args[0].u.symbol); if (result == paramexists_id) { cthrow(paramexists_id, "Parameter %I already exists.", args[0].u.symbol); } else { pop(1); push_int(1); } } void func_del_parameter(void) { data_t * args; long result; /* Accept one symbol argument. */ if (!func_init_1(&args, SYMBOL)) return; result = object_del_param(cur_frame->object, args[0].u.symbol); if (result == paramnf_id) { cthrow(paramnf_id, "Parameter %I does not exist.", args[0].u.symbol); } else { pop(1); push_int(1); } } void func_parameters(void) { list_t * params; object_t * obj; int i; Var * var; data_t d; /* Accept no arguments. */ if (!func_init_0()) return; /* Construct the list of variable names. */ obj = cur_frame->object; params = list_new(0); d.type = SYMBOL; for (i = 0; i < obj->vars.size; i++) { var = &obj->vars.tab[i]; if (var->name != -1 && var->cclass == obj->dbref) { d.u.symbol = var->name; params = list_add(params, &d); } } /* Push the list onto the stack. */ push_list(params); list_discard(params); } void func_set_var(void) { data_t * args, d; long result; /* Accept a symbol for the variable name and a data value of any type. */ if (!func_init_2(&args, SYMBOL, 0)) return; result = object_assign_var(cur_frame->object, cur_frame->method->object, args[0].u.symbol, &args[1]); if (result == paramnf_id) { cthrow(paramnf_id, "No such parameter %I.", args[0].u.symbol); } else { /* This is just a stupid way of returning args[1] */ data_dup(&d, &args[1]); pop(2); data_dup(&stack[stack_pos++], &d); data_discard(&d); } } void func_get_var(void) { data_t * args, d; long result; /* Accept a symbol argument. */ if (!func_init_1(&args, SYMBOL)) return; result = object_retrieve_var(cur_frame->object, cur_frame->method->object, args[0].u.symbol, &d); if (result == paramnf_id) { cthrow(paramnf_id, "No such parameter %I.", args[0].u.symbol); } else { pop(1); data_dup(&stack[stack_pos], &d); stack_pos++; } } void func_clear_var(void) { data_t * args; long result; /* Accept a symbol argument. */ if (!func_init_1(&args, SYMBOL)) return; result = object_delete_var(cur_frame->object, cur_frame->method->object, args[0].u.symbol); if (result == paramnf_id) { cthrow(paramnf_id, "No such parameter %I.", args[0].u.symbol); } else { pop(1); push_int(1); } } void func_compile_to_method(void) { data_t * args, * d; method_t * method; list_t * code, * errors; int flags=-1, state=-1; /* Accept a list of lines of code and a symbol for the name. */ if (!func_init_2(&args, LIST, SYMBOL)) return; method = object_find_method(cur_frame->object->dbref, args[1].u.symbol); if (method && (method->m_flags & MF_LOCK)) { cthrow(perm_id, "Method is locked, and cannot be changed."); return; } /* keep these for later reference, if its already around */ if (method) { flags = method->m_flags; state = method->m_state; } code = args[0].u.list; /* Make sure that every element in the code list is a string. */ for (d = list_first(code); d; d = list_next(code, d)) { if (d->type != STRING) { cthrow(type_id, "Line %d (%D) is not a string.", d - list_first(code), d); return; } } method = compile(cur_frame->object, code, &errors); if (method) { if (flags != -1) method->m_flags = flags; if (state != -1) method->m_state = state; object_add_method(cur_frame->object, args[1].u.symbol, method); method_discard(method); } pop(2); push_list(errors); list_discard(errors); } #define LADD(__s) { \ d.u.symbol = __s; \ list = list_add(list, &d); \ } void func_method_flags(void) { data_t * args, d; list_t * list; int flags; if (!func_init_1(&args, SYMBOL)) return; flags = object_get_method_flags(cur_frame->object, args[0].u.symbol); if (flags == F_FAILURE) flags = MF_NONE; list = list_new(0); d.type = SYMBOL; if (flags & MF_NOOVER) LADD(noover_id); if (flags & MF_SYNC) LADD(sync_id); if (flags & MF_LOCK) LADD(locked_id); if (flags & MF_NATIVE) LADD(native_id); pop(1); push_list(list); list_discard(list); } #undef LADD(__s) #define _THROW_(__e, __m) { cthrow(__e, __m); return; } void func_set_method_flags(void) { data_t * args, * d; list_t * list; int flags, new_flags = MF_NONE; if (!func_init_2(&args, SYMBOL, LIST)) return; flags = object_get_method_flags(cur_frame->object, args[0].u.symbol); if (flags == -1) _THROW_(methodnf_id, "Method not found."); if (flags & MF_LOCK) _THROW_(perm_id, "Method is locked and cannot be changed."); if (flags & MF_NATIVE) _THROW_(perm_id,"Method is native and cannot be changed."); list = args[1].u.list; for (d = list_first(list); d; d = list_next(list, d)) { if (d->type != SYMBOL) { cthrow(type_id, "Invalid method flag (%D).", d); return; } if (d->u.symbol == noover_id) { new_flags |= MF_NOOVER; } else if (d->u.symbol == sync_id) { new_flags |= MF_SYNC; } else if (d->u.symbol == locked_id) { new_flags |= MF_LOCK; } else if (d->u.symbol == native_id) { _THROW_(perm_id, "Native flag can only be set by the driver."); } else { cthrow(perm_id, "Unknown method flag (%D).", d); return; } } object_set_method_flags(cur_frame->object, args[0].u.symbol, new_flags); pop(2); push_int(new_flags); } void func_method_state(void) { int state; data_t * args; if (!func_init_1(&args, SYMBOL)) return; state = object_get_method_state(cur_frame->object, args[0].u.symbol); pop(1); switch(state) { case MS_PUBLIC: push_symbol(public_id); break; case MS_PROTECTED: push_symbol(protected_id); break; case MS_PRIVATE: push_symbol(private_id); break; case MS_ROOT: push_symbol(root_id); break; case MS_DRIVER: push_symbol(driver_id); break; default: push_int(0); break; } } void func_set_method_state(void) { int state = 0; data_t * args; Ident sym; if (!func_init_2(&args, SYMBOL, SYMBOL)) return; sym = args[1].u.symbol; if (sym == public_id) state = MS_PUBLIC; else if (sym == protected_id) state = MS_PROTECTED; else if (sym == private_id) state = MS_PRIVATE; else if (sym == root_id) state = MS_ROOT; else if (sym == driver_id) state = MS_DRIVER; else cthrow(type_id, "Invalid method state flag."); object_set_method_state(cur_frame->object, args[0].u.symbol, state); if (state == -1) cthrow(type_id, "Method %D not found.", args[0]); pop(2); push_int(state); } void func_method_args(void) { data_t * args; method_t * method; string_t * str; char * s; int i; /* A symbol for the Method name. */ if (!func_init_1(&args, SYMBOL)) return; method = object_find_method(cur_frame->object->dbref, args[0].u.symbol); if (!method) { cthrow(methodnf_id, "Method not found."); return; } str = string_new(0); if (method->num_args || method->rest != -1) { for (i = method->num_args - 1; i >= 0; i--) { s = ident_name(object_get_ident(method->object, method->argnames[i])); str = string_add_chars(str, s, strlen(s)); if (i > 0 || method->rest != -1) str = string_add_chars(str, ", ", 2); } if (method->rest != -1) { str = string_addc(str, '['); s = ident_name(object_get_ident(method->object, method->rest)); str = string_add_chars(str, s, strlen(s)); str = string_addc(str, ']'); } str = string_addc(str, ';'); } pop(1); push_string(str); string_discard(str); } void func_methods(void) { list_t * methods; data_t d; object_t * obj; int i; /* Accept no arguments. */ if (!func_init_0()) return; /* Construct the list of method names. */ obj = cur_frame->object; methods = list_new(obj->methods.size); for (i = 0; i < obj->methods.size; i++) { if (obj->methods.tab[i].m) { d.type = SYMBOL; d.u.symbol = obj->methods.tab[i].m->name; methods = list_add(methods, &d); } } /* Push the list onto the stack. */ check_stack(1); push_list(methods); list_discard(methods); } void func_find_method(void) { data_t * args; method_t * method; /* Accept a symbol argument giving the method name. */ if (!func_init_1(&args, SYMBOL)) return; /* Look for the method on the current object. */ method = object_find_method(cur_frame->object->dbref, args[0].u.symbol); pop(1); if (method) { push_dbref(method->object->dbref); cache_discard(method->object); } else { cthrow(methodnf_id, "Method %s not found.", ident_name(args[0].u.symbol)); } } void func_find_next_method(void) { data_t * args; method_t * method; /* Accept a symbol argument giving the method name, and a dbref giving the * object to search past. */ if (!func_init_2(&args, SYMBOL, DBREF)) return; /* Look for the method on the current object. */ method = object_find_next_method(cur_frame->object->dbref, args[0].u.symbol, args[1].u.dbref); if (method) { push_dbref(method->object->dbref); cache_discard(method->object); } else { cthrow(methodnf_id, "Method %s not found.", ident_name(args[0].u.symbol)); } } void func_list_method(void) { int num_args, indent, parens; data_t * args; list_t * code; /* Accept a symbol for the method name, an optional integer for the * indentation, and an optional integer to specify full * parenthesization. */ if (!func_init_1_to_3(&args, &num_args, SYMBOL, INTEGER, INTEGER)) return; indent = (num_args >= 2) ? args[1].u.val : DEFAULT_INDENT; indent = (indent < 0) ? 0 : indent; parens = (num_args == 3) ? (args[2].u.val != 0) : 0; code = object_list_method(cur_frame->object, args[0].u.symbol, indent, parens); if (code) { pop(num_args); push_list(code); list_discard(code); } else { cthrow(methodnf_id, "Method %s not found.", ident_name(args[0].u.symbol)); } } void func_del_method(void) { data_t * args; int status; /* Accept a symbol for the method name. */ if (!func_init_1(&args, SYMBOL)) return; status = object_del_method(cur_frame->object, args[0].u.symbol); if (status == 0) { cthrow(methodnf_id, "No method named %s was found.", ident_name(args[0].u.symbol)); } else if (status == -1) { cthrow(perm_id, "Method is locked, and cannot be removed."); } else { pop(1); push_int(1); } } void func_parents(void) { /* Accept no arguments. */ if (!func_init_0()) return; /* Push the parents list onto the stack. */ push_list(cur_frame->object->parents); } void func_children(void) { /* Accept no arguments. */ if (!func_init_0()) return; /* Push the children list onto the stack. */ push_list(cur_frame->object->children); } void func_ancestors(void) { list_t * ancestors; /* Accept no arguments. */ if (!func_init_0()) return; /* Get an ancestors list from the object. */ ancestors = object_ancestors(cur_frame->object->dbref); push_list(ancestors); list_discard(ancestors); } void func_has_ancestor(void) { data_t * args; int result; /* Accept a dbref to check as an ancestor. */ if (!func_init_1(&args, DBREF)) return; result = object_has_ancestor(cur_frame->object->dbref, args[0].u.dbref); pop(1); push_int(result); } void func_size(void) { /* Accept no arguments. */ if (!func_init_0()) return; /* Push size of current object. */ push_int(size_object(cur_frame->object)); } void func_create(void) { data_t *args, *d; list_t *parents; object_t *obj; /* Accept a list of parents. */ if (!func_init_1(&args, LIST)) return; /* Get parents list from second argument. */ parents = args[0].u.list; /* Verify that all parents are dbrefs. */ for (d = list_first(parents); d; d = list_next(parents, d)) { if (d->type != DBREF) { cthrow(type_id, "Parent %D is not a dbref.", d); return; } else if (!cache_check(d->u.dbref)) { cthrow(objnf_id, "Parent %D does not refer to an object.", d); return; } } /* Create the new object. */ obj = object_new(-1, parents); pop(1); push_dbref(obj->dbref); cache_discard(obj); } void func_chparents(void) { data_t * args, * d; object_t * obj; int wrong; /* Accept a dbref and a list of parents to change to. */ if (!func_init_2(&args, DBREF, LIST)) return; if (args[0].u.dbref == ROOT_DBREF) { cthrow(perm_id, "You cannot change the root object's parents."); return; } obj = cache_retrieve(args[0].u.dbref); if (!obj) { cthrow(objnf_id, "Object #%l not found.", args[0].u.dbref); return; } if (!list_length(args[1].u.list)) { cthrow(perm_id, "You must specify at least one parent."); return; } /* Call object_change_parents(). This will return the number of a * parent which was invalid, or -1 if they were all okay. */ wrong = object_change_parents(obj, args[1].u.list); if (wrong >= 0) { d = list_elem(args[1].u.list, wrong); if (d->type != DBREF) { cthrow(type_id, "New parent %D is not a dbref.", d); } else if (d->u.dbref == args[0].u.dbref) { cthrow(parent_id, "New parent %D is the same as %D.", d, &args[0]); } else if (!cache_check(d->u.dbref)) { cthrow(objnf_id, "New parent %D does not exist.", d); } else { cthrow(parent_id, "New parent %D is a descendent of %D.", d, &args[0]); } } else { pop(2); push_int(1); } cache_discard(obj); } void func_destroy(void) { data_t * args; object_t * obj; /* Accept a dbref to destroy. */ if (!func_init_1(&args, DBREF)) return; if (args[0].u.dbref == ROOT_DBREF) { cthrow(perm_id, "You can't destroy the root object."); } else if (args[0].u.dbref == SYSTEM_DBREF) { cthrow(perm_id, "You can't destroy the system object."); } else { obj = cache_retrieve(args[0].u.dbref); if (!obj) { cthrow(objnf_id, "Object #%l not found.", args[0].u.dbref); return; } /* Set the object dead, so it will go away when nothing is holding onto * it. cache_discard() will notice the dead flag, and call * object_destroy(). */ obj->dead = 1; cache_discard(obj); pop(1); push_int(1); } } /* MODULE_NOTE: remove this once the file module is fully functional */ void func_log(void) { data_t * args; /* Accept a string. */ if (!func_init_1(&args, STRING)) return; write_log("%S", args[0].u.str); pop(1); push_int(1); } /* // ----------------------------------------------------------------- // // Modifies: The object cache, identifier table, and binary database // files via cache_sync() and ident_dump(). // Effects: If called by the sytem object with no arguments, // performs a binary dump, ensuring that the files db and // db are consistent. Returns 1 if the binary dump // succeeds, or 0 if it fails. // */ #define SHELL_FAILURE 127 void func_backup(void) { /* Accept no arguments. */ if (!func_init_0()) return; if (binary_dump()) { if (system("cp -r binary binary.bak") != SHELL_FAILURE) { push_int(1); } else { push_int(-1); } } else { push_int(0); } } #undef SHELL_FAILURE /* // ----------------------------------------------------------------- // // Modifies: The object cache, identifier table, and binary database // files via cache_sync() and ident_dump(). // Effects: If called by the sytem object with no arguments, // performs a binary dump, ensuring that the files db and // db are consistent. Returns 1 if the binary dump // succeeds, or 0 if it fails. // */ void func_binary_dump(void) { /* Accept no arguments. */ if (!func_init_0()) return; push_int(binary_dump()); } /* // ----------------------------------------------------------------- // // Modifies: The object cache and binary database files via cache_sync() // and two sweeps through the database. Modifies the internal // dbm state use by dbm_firstkey() and dbm_nextkey(). // Effects: If called by the system object with no arguments, performs a // text dump, creating a file 'textdump' which contains a // representation of the database in terms of a few simple // commands and the ColdC language. Returns 1 if the text dump // succeeds, or 0 if it fails. // */ void func_text_dump(void) { /* Accept no arguments. */ if (!func_init_0()) return; push_int(text_dump()); } /* // ----------------------------------------------------------------- // // Modifies: The 'running' global (defs.h) may be set to 0. // // Effects: If called by the system object with no arguments, sets // 'running' to 0, causing the program to exit after this // iteration of the main loop finishes. Returns 1. // */ void func_shutdown(void) { /* Accept no arguments. */ if (!func_init_0()) return; running = 0; push_int(1); } /* // ----------------------------------------------------------------- // // run an executable from the filesystem // // MODULE_NOTE: Move this to the file module */ void func_execute(void) { data_t *args, *d; list_t *script_args; int num_args, argc, len, i, fd, status; pid_t pid; char *fname, **argv; /* Accept a name of a script to run, a list of arguments to give it, and * an optional flag signifying that we should not wait for completion. */ if (!func_init_2_or_3(&args, &num_args, STRING, LIST, INTEGER)) return; script_args = args[1].u.list; /* Verify that all items in argument list are strings. */ for (d = list_first(script_args), i=0; d; d = list_next(script_args, d), i++) { if (d->type != STRING) { cthrow(type_id, "Execute argument %d (%D) is not a string.", i+1, d); return; } } /* Don't allow walking back up the directory tree. */ if (strstr(string_chars(args[0].u.str), "../")) { cthrow(perm_id, "Filename %D is not legal.", &args[0]); return; } /* Construct the name of the script. */ len = string_length(args[0].u.str); fname = TMALLOC(char, len + 9); memcpy(fname, "scripts/", 8); memcpy(fname + 8, string_chars(args[0].u.str), len); fname[len + 8] = 0; /* Build an argument list. */ argc = list_length(script_args) + 1; argv = TMALLOC(char *, argc + 1); argv[0] = tstrdup(fname); for (d = list_first(script_args), i = 0; d; d = list_next(script_args, d), i++) argv[i + 1] = tstrdup(string_chars(d->u.str)); argv[argc] = NULL; pop(num_args); /* Fork off a process. */ #ifdef USE_VFORK pid = vfork(); #else pid = fork(); #endif if (pid == 0) { /* Pipe stdin and stdout to /dev/null, keep stderr. */ fd = open("/dev/null", O_RDWR); if (fd == -1) { write_err("EXEC: Failed to open /dev/null: %s.", strerror(errno)); _exit(-1); } dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); execv(fname, argv); write_err("EXEC: Failed to exec \"%s\": %s.", fname, strerror(errno)); _exit(-1); } else if (pid > 0) { if (num_args == 3 && args[2].u.val) { if (waitpid(pid, &status, WNOHANG) == 0) status = 0; } else { waitpid(pid, &status, 0); } } else { write_err("EXEC: Failed to fork: %s.", strerror(errno)); status = -1; } /* Free the argument list. */ for (i = 0; i < argc; i++) tfree_chars(argv[i]); TFREE(argv, argc + 1); push_int(status); } /* // ----------------------------------------------------------------- // // Modifies: heartbeat_freq (defs.h) // */ void func_set_heartbeat(void) { data_t *args; if (!func_init_1(&args, INTEGER)) return; if (args[0].u.val <= 0) args[0].u.val = -1; heartbeat_freq = args[0].u.val; pop(1); } /* // ----------------------------------------------------------------- // // Return all vars on an object. // // Completely destroys encapsulation, but it is needed for a run-time // environment. // // Modify later to specify which defining ancestor */ void func_data(void) { data_t *args, key, value; object_t *obj; Dict *dict; int i; if (!func_init_1(&args, DBREF)) return; obj = cache_retrieve(args[0].u.dbref); if (!obj) { cthrow(objnf_id, "No such object #%l", args[0].u.dbref); return; } /* Construct the dictionary. */ dict = dict_new_empty(); for (i = 0; i < obj->vars.size; i++) { if (obj->vars.tab[i].name == -1) continue; key.type = DBREF; key.u.dbref = obj->vars.tab[i].cclass; if (dict_find(dict, &key, &value) == keynf_id) { value.type = DICT; value.u.dict = dict_new_empty(); dict = dict_add(dict, &key, &value); } key.type = SYMBOL; key.u.symbol = obj->vars.tab[i].name; value.u.dict = dict_add(value.u.dict, &key, &obj->vars.tab[i].val); key.type = DBREF; key.u.dbref = obj->vars.tab[i].cclass; dict = dict_add(dict, &key, &value); dict_discard(value.u.dict); } cache_discard(obj); pop(1); push_dict(dict); dict_discard(dict); } /* // ----------------------------------------------------------------- */ void func_add_objname(void) { data_t *args; int result; if (!func_init_2(&args, SYMBOL, DBREF)) return; result = lookup_store_name(args[0].u.symbol, args[1].u.dbref); pop(2); push_int(result); } /* // ----------------------------------------------------------------- */ void func_del_objname(void) { data_t *args; if (!func_init_1(&args, SYMBOL)) return; if (!lookup_remove_name(args[0].u.symbol)) { cthrow(namenf_id, "Can't find object name %I.", args[0].u.symbol); return; } pop(1); push_int(1); } /* ----------------------------------------------------------------- */ /* cancel a suspended task */ void func_cancel(void) { data_t *args; if (!func_init_1(&args, INTEGER)) return; if (!task_lookup(args[0].u.val)) { cthrow(type_id, "No such task"); } else { task_cancel(args[0].u.val); pop(1); push_int(1); } } /* ----------------------------------------------------------------- */ /* suspend a task */ void func_suspend(void) { if (!func_init_0()) return; task_suspend(); /* we'll let task_resume push something onto the stack for us */ } /* ----------------------------------------------------------------- */ void func_resume(void) { data_t *args; int nargs; long tid; if (!func_init_1_or_2(&args, &nargs, INTEGER, 0)) return; tid = args[0].u.val; if (!task_lookup(tid)) { cthrow(type_id, "No such task"); } else { if (nargs == 1) task_resume(tid, NULL); else task_resume(tid, &args[1]); pop(nargs); push_int(0); } } /* ----------------------------------------------------------------- */ void func_pause(void) { if (!func_init_0()) return; push_int(0); task_pause(); } /* ----------------------------------------------------------------- */ void func_tasks(void) { list_t * list; if (!func_init_0()) return; list = task_list(); push_list(list); list_discard(list); } /* ----------------------------------------------------------------- */ void func_tick(void) { if (!func_init_0()) return; push_int(tick); } /* ----------------------------------------------------------------- */ void func_callers(void) { list_t * list; if (!func_init_0()) return; list = task_callers(); push_list(list); list_discard(list); } void func_bind_function(void) { data_t * args; int opcode; /* accept a symbol and dbref */ if (!func_init_2(&args, SYMBOL, DBREF)) return; opcode = find_function(ident_name(args[0].u.symbol)); if (opcode == -1) { cthrow(perm_id, "Attempt to bind function which does not exist."); return; } op_table[opcode].binding = args[1].u.dbref; pop(2); push_int(1); } void func_unbind_function(void) { data_t *args; int opcode; /* accept a symbol */ if (!func_init_1(&args, SYMBOL)) return; opcode = find_function(ident_name(args[0].u.symbol)); if (opcode == -1) { cthrow(perm_id, "Attempt to unbind function which does not exist."); return; } op_table[opcode].binding = INV_OBJNUM; pop(1); push_int(1); } void func_method(void) { if (!func_init_0()) return; push_symbol(cur_frame->method->name); } void func_this(void) { /* Accept no arguments, and push the dbref of the current object. */ if (!func_init_0()) return; push_dbref(cur_frame->object->dbref); } void func_definer(void) { /* Accept no arguments, and push the dbref of the method definer. */ if (!func_init_0()) return; push_dbref(cur_frame->method->object->dbref); } void func_sender(void) { /* Accept no arguments, and push the dbref of the sending object. */ if (!func_init_0()) return; if (cur_frame->sender == NOT_AN_IDENT) push_int(0); else push_dbref(cur_frame->sender); } void func_caller(void) { /* Accept no arguments, and push the dbref of the calling method's * definer. */ if (!func_init_0()) return; if (cur_frame->caller == NOT_AN_IDENT) push_int(0); else push_dbref(cur_frame->caller); } void func_task_id(void) { /* Accept no arguments, and push the task ID. */ if (!func_init_0()) return; push_int(task_id); } void func_ticks_left(void) { if (!func_init_0()) return; push_int(cur_frame->ticks); } void func_type(void) { data_t *args; int type; /* Accept one argument of any type. */ if (!func_init_1(&args, 0)) return; /* Replace argument with symbol for type name. */ type = args[0].type; pop(1); push_symbol(data_type_id(type)); } void func_class(void) { data_t *args; long cclass; /* Accept one argument of frob type. */ if (!func_init_1(&args, FROB)) return; /* Replace argument with class. */ cclass = args[0].u.frob->cclass; pop(1); push_dbref(cclass); } void func_toint(void) { data_t *args; long val = 0; /* Accept a string or integer to convert into an integer. */ if (!func_init_1(&args, 0)) return; if (args[0].type == INTEGER) { return; } else if (args[0].type == FLOAT) { val = (long) args[0].u.fval; } else if (args[0].type == STRING) { val = atol(string_chars(args[0].u.str)); } else if (args[0].type == DBREF) { val = args[0].u.dbref; } else { cthrow(type_id, "The first argument (%D) is not an integer or string.", &args[0]); } pop(1); push_int(val); } void func_tofloat(void) { data_t * args; float val = 0; /* Accept a string, integer or integer to convert into a float. */ if (!func_init_1(&args, 0)) return; if (args[0].type == STRING) { val = atof(string_chars(args[0].u.str)); } else if (args[0].type == INTEGER) { val = args[0].u.val; } else if (args[0].type == FLOAT) { return; } else if (args[0].type == DBREF) { val = args[0].u.dbref; } else { cthrow(type_id, "The first argument (%D) is not an integer or string.", &args[0]); } pop(1); push_float(val); } void func_tostr(void) { data_t *args; string_t *str; /* Accept one argument of any type. */ if (!func_init_1(&args, 0)) return; /* Replace the argument with its text version. */ str = data_tostr(&args[0]); pop(1); push_string(str); string_discard(str); } void func_toliteral(void) { data_t *args; string_t *str; /* Accept one argument of any type. */ if (!func_init_1(&args, 0)) return; /* Replace the argument with its unparsed version. */ str = data_to_literal(&args[0]); pop(1); push_string(str); string_discard(str); } void func_todbref(void) { data_t *args; /* Accept an integer to convert into a dbref. */ if (!func_init_1(&args, INTEGER)) return; if (args[0].u.val < 0) cthrow(type_id, "objnums must be 0 or greater"); args[0].u.dbref = args[0].u.val; args[0].type = DBREF; } void func_tosym(void) { data_t *args; long sym; /* Accept one string argument. */ if (!func_init_1(&args, STRING)) return; sym = ident_get(string_chars(args[0].u.str)); pop(1); push_symbol(sym); } void func_toerr(void) { data_t *args; long error; /* Accept one string argument. */ if (!func_init_1(&args, STRING)) return; error = ident_get(string_chars(args[0].u.str)); pop(1); push_error(error); } void func_valid(void) { data_t *args; int is_valid; /* Accept one argument of any type (only dbrefs can be valid, though). */ if (!func_init_1(&args, 0)) return; is_valid = (args[0].type == DBREF && cache_check(args[0].u.dbref)); pop(1); push_int(is_valid); } void func_error(void) { if (!func_init_0()) return; if (!cur_frame->handler_info) { cthrow(error_id, "Request for handler info outside handler."); return; } push_error(cur_frame->handler_info->error); } void func_traceback(void) { if (!func_init_0()) return; if (!cur_frame->handler_info) { cthrow(error_id, "Request for handler info outside handler."); return; } push_list(cur_frame->handler_info->traceback); } void func_throw(void) { data_t *args, error_arg; int num_args; string_t *str; if (!func_init_2_or_3(&args, &num_args, ERROR, STRING, 0)) return; /* Throw the error. */ str = string_dup(args[1].u.str); if (num_args == 3) { data_dup(&error_arg, &args[2]); user_error(args[0].u.error, str, &error_arg); data_discard(&error_arg); } else { user_error(args[0].u.error, str, NULL); } string_discard(str); } void func_rethrow(void) { data_t *args; list_t *traceback; if (!func_init_1(&args, ERROR)) return; if (!cur_frame->handler_info) { cthrow(error_id, "Request for handler info outside handler."); return; } /* Abort the current frame and propagate an error in the caller. */ traceback = list_dup(cur_frame->handler_info->traceback); frame_return(); propagate_error(traceback, args[0].u.error); }