/****************************************************************************** Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved. Portions of this code were written by Stephen White, aka ghond. Use and copying of this software and preparation of derivative works based upon this software are permitted. Any distribution of this software or derivative works must comply with all applicable United States export control laws. This software is made available AS IS, and Xerox Corporation makes no warranty about the software, its performance or its conformity to any specification. Any person obtaining a copy of this software is requested to send their name and post office or electronic mail address to: Pavel Curtis Xerox PARC 3333 Coyote Hill Rd. Palo Alto, CA 94304 Pavel@Xerox.Com *****************************************************************************/ #include "my-string.h" #include "config.h" #include "db.h" #include "exceptions.h" #include "execute.h" #include "functions.h" #include "list.h" #include "log.h" #include "match.h" #include "parse_cmd.h" #include "parser.h" #include "server.h" #include "storage.h" #include "unparse.h" #include "utils.h" #include "verbs.h" struct verb_data { Var r; int i; }; static int add_to_list(void *data, const char *verb_name) { struct verb_data *d = data; d->i++; d->r.v.list[d->i].type = TYPE_STR; d->r.v.list[d->i].v.str = str_ref(verb_name); return 0; } static package bf_verbs(Var arglist, Byte next, void *vdata, Objid progr) { /* (object) */ Objid oid = arglist.v.list[1].v.obj; free_var(arglist); if (!valid(oid)) return make_error_pack(E_INVARG); else if (!db_object_allows(oid, progr, FLAG_READ)) return make_error_pack(E_PERM); else { struct verb_data d; d.r = new_list(db_count_verbs(oid)); d.i = 0; db_for_all_verbs(oid, add_to_list, &d); return make_var_pack(d.r); } } static enum error validate_verb_info(Var v, Objid * owner, unsigned *flags, const char **names) { const char *s; if (!(v.type = TYPE_LIST && v.v.list[0].v.num == 3 && v.v.list[1].type == TYPE_OBJ && v.v.list[2].type == TYPE_STR && v.v.list[3].type == TYPE_STR)) return E_TYPE; *owner = v.v.list[1].v.obj; if (!valid(*owner)) return E_INVARG; for (*flags = 0, s = v.v.list[2].v.str; *s; s++) { switch (*s) { case 'r': case 'R': *flags |= VF_READ; break; case 'w': case 'W': *flags |= VF_WRITE; break; case 'x': case 'X': *flags |= VF_EXEC; break; case 'd': case 'D': *flags |= VF_DEBUG; break; default: return E_INVARG; } } *names = v.v.list[3].v.str; while (**names == ' ') (*names)++; if (**names == '\0') return E_INVARG; *names = str_dup(*names); return E_NONE; } static int match_arg_spec(const char *s, db_arg_spec * spec) { if (!mystrcasecmp(s, "none")) { *spec = ASPEC_NONE; return 1; } else if (!mystrcasecmp(s, "any")) { *spec = ASPEC_ANY; return 1; } else if (!mystrcasecmp(s, "this")) { *spec = ASPEC_THIS; return 1; } else return 0; } static int match_prep_spec(const char *s, db_prep_spec * spec) { if (!mystrcasecmp(s, "none")) { *spec = PREP_NONE; return 1; } else if (!mystrcasecmp(s, "any")) { *spec = PREP_ANY; return 1; } else return (*spec = db_match_prep(s)) != PREP_NONE; } static enum error validate_verb_args(Var v, db_arg_spec * dobj, db_prep_spec * prep, db_arg_spec * iobj) { if (!(v.type = TYPE_LIST && v.v.list[0].v.num == 3 && v.v.list[1].type == TYPE_STR && v.v.list[2].type == TYPE_STR && v.v.list[3].type == TYPE_STR)) return E_TYPE; if (!match_arg_spec(v.v.list[1].v.str, dobj) || !match_prep_spec(v.v.list[2].v.str, prep) || !match_arg_spec(v.v.list[3].v.str, iobj)) return E_INVARG; return E_NONE; } static package bf_add_verb(Var arglist, Byte next, void *vdata, Objid progr) { /* (object, info, args) */ Objid oid = arglist.v.list[1].v.obj; Var info = arglist.v.list[2]; Var args = arglist.v.list[3]; Var result; Objid owner; unsigned flags; const char *names; db_arg_spec dobj, iobj; db_prep_spec prep; enum error e; if ((e = validate_verb_info(info, &owner, &flags, &names)) != E_NONE) /* Already failed */ ; else if ((e = validate_verb_args(args, &dobj, &prep, &iobj)) != E_NONE) free_str(names); else if (!valid(oid)) { free_str(names); e = E_INVARG; } else if (!db_object_allows(oid, progr, FLAG_WRITE) || (progr != owner && !is_wizard(progr))) { free_str(names); e = E_PERM; } else { result.type = TYPE_INT; result.v.num = db_add_verb(oid, names, owner, flags, dobj, prep, iobj); } free_var(arglist); if (e == E_NONE) return make_var_pack(result); else return make_error_pack(e); } enum error validate_verb_descriptor(Var desc) { if (desc.type == TYPE_STR) return E_NONE; else if (desc.type != TYPE_INT) return E_TYPE; else if (desc.v.num <= 0) return E_INVARG; else return E_NONE; } db_verb_handle find_described_verb(Objid oid, Var desc) { if (desc.type == TYPE_INT) return db_find_indexed_verb(oid, desc.v.num); else { int flag = server_flag_option("support_numeric_verbname_strings"); return db_find_defined_verb(oid, desc.v.str, flag); } } static package bf_delete_verb(Var arglist, Byte next, void *vdata, Objid progr) { /* (object, verb-desc) */ Objid oid = arglist.v.list[1].v.obj; Var desc = arglist.v.list[2]; db_verb_handle h; enum error e; if ((e = validate_verb_descriptor(desc)) != E_NONE); /* Do nothing; e is already set. */ else if (!valid(oid)) e = E_INVARG; else if (!db_object_allows(oid, progr, FLAG_WRITE)) e = E_PERM; else { h = find_described_verb(oid, desc); if (h.ptr) db_delete_verb(h); else e = E_VERBNF; } free_var(arglist); if (e == E_NONE) return no_var_pack(); else return make_error_pack(e); } static package bf_verb_info(Var arglist, Byte next, void *vdata, Objid progr) { /* (object, verb-desc) */ Objid oid = arglist.v.list[1].v.obj; Var desc = arglist.v.list[2]; db_verb_handle h; Var r; unsigned flags; char *s; enum error e; if ((e = validate_verb_descriptor(desc)) != E_NONE || (e = E_INVARG, !valid(oid))) { free_var(arglist); return make_error_pack(e); } h = find_described_verb(oid, desc); free_var(arglist); if (!h.ptr) return make_error_pack(E_VERBNF); else if (!db_verb_allows(h, progr, VF_READ)) return make_error_pack(E_PERM); r = new_list(3); r.v.list[1].type = TYPE_OBJ; r.v.list[1].v.obj = db_verb_owner(h); r.v.list[2].type = TYPE_STR; r.v.list[2].v.str = s = str_dup("xxxx"); flags = db_verb_flags(h); if (flags & VF_READ) *s++ = 'r'; if (flags & VF_WRITE) *s++ = 'w'; if (flags & VF_EXEC) *s++ = 'x'; if (flags & VF_DEBUG) *s++ = 'd'; *s = '\0'; r.v.list[3].type = TYPE_STR; r.v.list[3].v.str = str_ref(db_verb_names(h)); return make_var_pack(r); } static package bf_set_verb_info(Var arglist, Byte next, void *vdata, Objid progr) { /* (object, verb-desc, {owner, flags, names}) */ Objid oid = arglist.v.list[1].v.obj; Var desc = arglist.v.list[2]; Var info = arglist.v.list[3]; Objid new_owner; unsigned new_flags; const char *new_names; enum error e; db_verb_handle h; if ((e = validate_verb_descriptor(desc)) != E_NONE); /* Do nothing; e is already set. */ else if (!valid(oid)) e = E_INVARG; else e = validate_verb_info(info, &new_owner, &new_flags, &new_names); if (e != E_NONE) { free_var(arglist); return make_error_pack(e); } h = find_described_verb(oid, desc); free_var(arglist); if (!h.ptr) { free_str(new_names); return make_error_pack(E_VERBNF); } else if (!db_verb_allows(h, progr, VF_WRITE) || (!is_wizard(progr) && db_verb_owner(h) != new_owner)) { free_str(new_names); return make_error_pack(E_PERM); } db_set_verb_owner(h, new_owner); db_set_verb_flags(h, new_flags); db_set_verb_names(h, new_names); return no_var_pack(); } static const char * unparse_arg_spec(db_arg_spec spec) { switch (spec) { case ASPEC_NONE: return str_dup("none"); case ASPEC_ANY: return str_dup("any"); case ASPEC_THIS: return str_dup("this"); default: panic("UNPARSE_ARG_SPEC: Unknown db_arg_spec!"); return ""; } } static package bf_verb_args(Var arglist, Byte next, void *vdata, Objid progr) { /* (object, verb-desc) */ Objid oid = arglist.v.list[1].v.obj; Var desc = arglist.v.list[2]; db_verb_handle h; db_arg_spec dobj, iobj; db_prep_spec prep; Var r; enum error e; if ((e = validate_verb_descriptor(desc)) != E_NONE || (e = E_INVARG, !valid(oid))) { free_var(arglist); return make_error_pack(e); } h = find_described_verb(oid, desc); free_var(arglist); if (!h.ptr) return make_error_pack(E_VERBNF); else if (!db_verb_allows(h, progr, VF_READ)) return make_error_pack(E_PERM); db_verb_arg_specs(h, &dobj, &prep, &iobj); r = new_list(3); r.v.list[1].type = TYPE_STR; r.v.list[1].v.str = unparse_arg_spec(dobj); r.v.list[2].type = TYPE_STR; r.v.list[2].v.str = str_dup(db_unparse_prep(prep)); r.v.list[3].type = TYPE_STR; r.v.list[3].v.str = unparse_arg_spec(iobj); return make_var_pack(r); } static package bf_set_verb_args(Var arglist, Byte next, void *vdata, Objid progr) { /* (object, verb-desc, {dobj, prep, iobj}) */ Objid oid = arglist.v.list[1].v.obj; Var desc = arglist.v.list[2]; Var args = arglist.v.list[3]; enum error e; db_verb_handle h; db_arg_spec dobj, iobj; db_prep_spec prep; if ((e = validate_verb_descriptor(desc)) != E_NONE); /* Do nothing; e is already set. */ else if (!valid(oid)) e = E_INVARG; else e = validate_verb_args(args, &dobj, &prep, &iobj); if (e != E_NONE) { free_var(arglist); return make_error_pack(e); } h = find_described_verb(oid, desc); free_var(arglist); if (!h.ptr) return make_error_pack(E_VERBNF); else if (!db_verb_allows(h, progr, VF_WRITE)) return make_error_pack(E_PERM); db_set_verb_arg_specs(h, dobj, prep, iobj); return no_var_pack(); } static void lister(void *data, const char *line) { Var *r = (Var *) data; Var v; v.type = TYPE_STR; v.v.str = str_dup(line); *r = listappend(*r, v); } static package bf_verb_code(Var arglist, Byte next, void *vdata, Objid progr) { /* (object, verb-desc [, fully-paren [, indent]]) */ int nargs = arglist.v.list[0].v.num; Objid oid = arglist.v.list[1].v.obj; Var desc = arglist.v.list[2]; int parens = nargs >= 3 && is_true(arglist.v.list[3]); int indent = nargs < 4 || is_true(arglist.v.list[4]); db_verb_handle h; Var code; enum error e; if ((e = validate_verb_descriptor(desc)) != E_NONE || (e = E_INVARG, !valid(oid))) { free_var(arglist); return make_error_pack(e); } h = find_described_verb(oid, desc); free_var(arglist); if (!h.ptr) return make_error_pack(E_VERBNF); else if (!db_verb_allows(h, progr, VF_READ)) return make_error_pack(E_PERM); code = new_list(0); unparse_program(db_verb_program(h), lister, &code, parens, indent, MAIN_VECTOR); return make_var_pack(code); } static package bf_set_verb_code(Var arglist, Byte next, void *vdata, Objid progr) { /* (object, verb-desc, code) */ Objid oid = arglist.v.list[1].v.obj; Var desc = arglist.v.list[2]; Var code = arglist.v.list[3]; int i; Program *program; db_verb_handle h; Var errors; enum error e; for (i = 1; i <= code.v.list[0].v.num; i++) if (code.v.list[i].type != TYPE_STR) { free_var(arglist); return make_error_pack(E_TYPE); } if ((e = validate_verb_descriptor(desc)) != E_NONE || (e = E_INVARG, !valid(oid))) { free_var(arglist); return make_error_pack(e); } h = find_described_verb(oid, desc); if (!h.ptr) { free_var(arglist); return make_error_pack(E_VERBNF); } else if (!is_programmer(progr) || !db_verb_allows(h, progr, VF_WRITE)) { free_var(arglist); return make_error_pack(E_PERM); } program = parse_list_as_program(code, &errors); if (program) { if (task_timed_out) free_program(program); else db_set_verb_program(h, program); } free_var(arglist); return make_var_pack(errors); } static package bf_eval(Var arglist, Byte next, void *data, Objid progr) { package p; if (next == 1) { if (!is_programmer(progr)) { free_var(arglist); p = make_error_pack(E_PERM); } else { Var errors; Program *program = parse_list_as_program(arglist, &errors); free_var(arglist); if (program) { free_var(errors); if (setup_activ_for_eval(program)) p = make_call_pack(2, 0); else { free_program(program); p = make_error_pack(E_MAXREC); } } else { Var r; r = new_list(2); r.v.list[1].type = TYPE_INT; r.v.list[1].v.num = 0; r.v.list[2] = errors; p = make_var_pack(r); } } } else { /* next == 2 */ Var r; r = new_list(2); r.v.list[1].type = TYPE_INT; r.v.list[1].v.num = 1; r.v.list[2] = arglist; p = make_var_pack(r); } return p; } void register_verbs(void) { register_function("verbs", 1, 1, bf_verbs, TYPE_OBJ); register_function("verb_info", 2, 2, bf_verb_info, TYPE_OBJ, TYPE_ANY); register_function("set_verb_info", 3, 3, bf_set_verb_info, TYPE_OBJ, TYPE_ANY, TYPE_LIST); register_function("verb_args", 2, 2, bf_verb_args, TYPE_OBJ, TYPE_ANY); register_function("set_verb_args", 3, 3, bf_set_verb_args, TYPE_OBJ, TYPE_ANY, TYPE_LIST); register_function("add_verb", 3, 3, bf_add_verb, TYPE_OBJ, TYPE_LIST, TYPE_LIST); register_function("delete_verb", 2, 2, bf_delete_verb, TYPE_OBJ, TYPE_ANY); register_function("verb_code", 2, 4, bf_verb_code, TYPE_OBJ, TYPE_ANY, TYPE_ANY, TYPE_ANY); register_function("set_verb_code", 3, 3, bf_set_verb_code, TYPE_OBJ, TYPE_ANY, TYPE_LIST); register_function("eval", 1, 1, bf_eval, TYPE_STR); } char rcsid_verbs[] = "$Id: verbs.c,v 1.4 2001/01/29 08:38:44 bjj Exp $"; /* * $Log: verbs.c,v $ * Revision 1.4 2001/01/29 08:38:44 bjj * Fix Sourceforge Bug #127620: add_verb() should return verbindex * And now it does. Old servers always returned 0, new servers will always * return a positive integer. * * Revision 1.3 1998/12/14 13:19:16 nop * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims * * Revision 1.2 1997/03/03 04:19:37 nop * GNU Indent normalization * * Revision 1.1.1.1 1997/03/03 03:45:01 nop * LambdaMOO 1.8.0p5 * * Revision 2.8 1996/05/12 21:29:46 pavel * Fixed memory leak for verb names string in bf_add_verb. Release 1.8.0p5. * * Revision 2.7 1996/03/19 07:19:13 pavel * Fixed off-by-one bug in type-checking in set_verb_code(). Release 1.8.0p2. * * Revision 2.6 1996/02/08 06:39:44 pavel * Renamed TYPE_NUM to TYPE_INT. Updated copyright notice for 1996. * Release 1.8.0beta1. * * Revision 2.5 1996/01/16 07:29:04 pavel * Fixed `parens' and `indent' arguments to verb_code() to allow values of any * type. Release 1.8.0alpha6. * * Revision 2.4 1996/01/11 07:51:42 pavel * Fixed bad free() in bf_add_verb(). Release 1.8.0alpha5. * * Revision 2.3 1995/12/31 03:28:27 pavel * Fixed C syntax botch in bf_delete_verb(). Release 1.8.0alpha4. * * Revision 2.2 1995/12/28 00:25:40 pavel * Added support for using numbers to designate defined verbs in built-in * functions. Release 1.8.0alpha3. * * Revision 2.1 1995/12/11 07:53:59 pavel * Accounted for verb programs never being NULL any more. * * Release 1.8.0alpha2. * * Revision 2.0 1995/11/30 04:43:36 pavel * New baseline version, corresponding to release 1.8.0alpha1. * * Revision 1.16 1992/10/23 23:03:47 pavel * Added copyright notice. * * Revision 1.15 1992/10/23 22:23:43 pavel * Eliminated all uses of the useless macro NULL. * * Revision 1.14 1992/10/21 03:02:35 pavel * Converted to use new automatic configuration system. * * Revision 1.13 1992/10/17 20:58:25 pavel * Global rename of strdup->str_dup, strref->str_ref, vardup->var_dup, and * varref->var_ref. * * Revision 1.12 1992/10/06 18:25:57 pavel * Changed name of global Parser_Client to avoid a name clash. * * Revision 1.11 1992/09/24 16:44:38 pavel * Made the parsing done by eval() and set_verb_code() abort if the task runs * out of seconds. * * Revision 1.10 1992/09/21 17:38:05 pavel * Restored RCS log information. * * Revision 1.9 1992/09/14 18:41:15 pjames * Moved rcsid to bottom. * Changed bf_eval to avoid a compiler warning when compiled un-optimized. * * Revision 1.8 1992/09/14 17:31:23 pjames * Moved db_modification code to db modules. * * Revision 1.7 1992/09/08 22:00:18 pjames * Renambed bf_verb.c to verbs.c * * Revision 1.6 1992/08/31 22:29:24 pjames * Changed some `char *'s to `const char *' * * Revision 1.5 1992/08/28 16:16:14 pjames * Changed myfree(*, M_STRING) to free_str(*). * Changed some strref's to strdup. * * Revision 1.4 1992/08/21 00:42:59 pavel * Renamed include file "parse_command.h" to "parse_cmd.h". * * Revision 1.3 1992/08/10 17:21:32 pjames * Updated #includes. Updated to use new registration format. Built in * functions now only receive programmer, instead of entire Parse_Info. * * Revision 1.2 1992/07/20 23:55:24 pavel * Added rcsid_ declaration to hold the RCS ident. string. * * Revision 1.1 1992/07/20 23:23:12 pavel * Initial RCS-controlled version. */