/* commands.c */ #include "config.h" /* * This file is part of TeenyMUD II. * Copyright(C) 1993, 1994, 1995 by Jason Downs. * All rights reserved. * * TeenyMUD II is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * TeenyMUD II is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file 'COPYING'); if not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * */ /* AIX requires this to be the first thing in the file. */ #ifdef __GNUC__ #define alloca __builtin_alloca #else /* not __GNUC__ */ #ifdef HAVE_ALLOCA_H #include <alloca.h> #else /* not HAVE_ALLOCA_H */ #ifdef _AIX #pragma alloca #endif /* not _AIX */ #endif /* not HAVE_ALLOCA_H */ #endif /* not __GNUC__ */ #include <stdio.h> #include <ctype.h> #include <sys/types.h> #ifdef HAVE_STRING_H #include <string.h> #else #include <strings.h> #endif /* HAVE_STRING_H */ #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif /* HAVE_STDLIB_H */ #include "conf.h" #include "teeny.h" #include "commands.h" #include "externs.h" #include "hash/hash.h" /* all new main command parser. */ /* all command "switch" arguments are of this type. */ typedef struct cmd_switch { char *name; int matlen; int code; int flags; } SWITCH; /* all commands are of this type. */ typedef struct commands { char *name; VOID (*function) (); SWITCH *switches; int nargs; int perms; } CMDS; /* flag definitions - permissions. */ #define CMDS_NORMAL 0x00000000 #define CMDS_GOD 0x00000001 #define CMDS_WIZARD 0x00000002 #define CMDS_BUILDER 0x00000003 #define CMDS_MASK 0x00000007 #define CMDS_NGUEST 0x00000010 #define CMDS_PLAYER 0x00000020 #define CMDS_NOSED 0x00000040 #define CMDS_SPEECH 0x00000080 /* speech command */ /* switch types. */ #define SWITCH_MULTIPLE 0x00001000 #define SWITCH_SINGULAR 0x00002000 /* misc. flags. */ #define CMDS_NOEXEC 0x00100000 #define CMDS_VARSLASH 0x00200000 #define CMDS_LOCREQ 0x00400000 #define CMDS_VARARGS -1 /* "switch" declarations. */ /* * First come the ``global'' switches-- ones that are parsed for in * every command. The command itself may or may not actually support * them, of course. */ static SWITCH globalcmd_switches[] = { {"quiet", 5, CMD_QUIET, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *)NULL, 0, 0, 0} }; static SWITCH ex_switches[] = { {"rooms", 1, EXAMINE_ROOMS, CMDS_NORMAL | SWITCH_MULTIPLE}, {"parents", 1, EXAMINE_PARENT, CMDS_NORMAL | SWITCH_MULTIPLE}, {"attrs", 1, EXAMINE_ATTRS, CMDS_NORMAL | SWITCH_MULTIPLE}, {"sort", 1, EXAMINE_SORT, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *) NULL, 0, 0, 0} }; static SWITCH recycle_switches[] = { {"override", 4, RECYCLE_OVERRIDE, CMDS_WIZARD | SWITCH_SINGULAR}, {(char *) NULL, 0, 0, 0} }; static SWITCH link_switches[] = { {"no_chown", 2, LINK_NOCHOWN, CMDS_WIZARD | SWITCH_SINGULAR}, {(char *) NULL, 0, 0, 0} }; static SWITCH stats_switches[] = { {"full", 3, STATS_FULL, CMDS_NORMAL | SWITCH_SINGULAR}, {(char *) NULL, 0, 0, 0} }; static SWITCH find_switches[] = { {"owned", 3, FIND_OWNED, CMDS_WIZARD | SWITCH_SINGULAR}, {(char *) NULL, 0, 0, 0} }; static SWITCH kill_switches[] = { {"slay", 4, KILL_SLAY, CMDS_WIZARD | SWITCH_SINGULAR}, {(char *) NULL, 0, 0, 0} }; static SWITCH wall_switches[] = { {"god", 1, WALL_GOD, CMDS_GOD | SWITCH_SINGULAR}, {"wiz", 1, WALL_WIZ, CMDS_WIZARD | SWITCH_SINGULAR}, {"pose", 1, WALL_POSE, CMDS_WIZARD | SWITCH_MULTIPLE}, {"no_space", 1, WALL_NOSPACE, CMDS_WIZARD | SWITCH_MULTIPLE}, {"emit", 1, WALL_EMIT, CMDS_WIZARD | SWITCH_MULTIPLE}, {(char *) NULL, 0, 0, 0} }; static SWITCH halt_switches[] = { {"all", 3, HALT_ALL, CMDS_WIZARD | SWITCH_MULTIPLE}, /*{"quiet", 1, HALT_QUIET, CMDS_WIZARD | SWITCH_MULTIPLE},*/ {(char *)NULL, 0, 0, 0} }; static SWITCH ps_switches[] = { {"all", 3, PS_ALL, CMDS_WIZARD | SWITCH_SINGULAR}, {"cause", 1, PS_CAUSE, CMDS_NORMAL | SWITCH_MULTIPLE}, {"owned", 3, PS_OWNED, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *)NULL, 0, 0, 0} }; static SWITCH pose_switches[] = { {"no_space", 1, POSE_NOSPACE, CMDS_NORMAL | SWITCH_SINGULAR}, {(char *) NULL, 0, 0, 0} }; static SWITCH version_switches[] = { {"full", 1, VERSION_FULL, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *) NULL, 0, 0, 0} }; static SWITCH config_switches[] = { {"set", 1, CONFIG_SET, CMDS_GOD | SWITCH_SINGULAR}, {"alias", 1, CONFIG_ALIAS, CMDS_GOD | SWITCH_SINGULAR}, {"unalias", 1, CONFIG_UNALIAS, CMDS_GOD | SWITCH_SINGULAR}, {"expand", 1, CONFIG_EXPAND, CMDS_GOD | SWITCH_SINGULAR}, {(char *)NULL, 0, 0, 0} }; static SWITCH notify_switches[] = { {"all", 1, NOTIFY_ALL, CMDS_NORMAL | SWITCH_MULTIPLE}, {"drain", 1, NOTIFY_DRAIN, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *)NULL, 0, 0, 0} }; static SWITCH motd_switches[] = { {"set", 1, MOTD_SET, CMDS_WIZARD | SWITCH_SINGULAR}, {(char *)NULL, 0, 0, 0} }; static SWITCH boot_switches[] = { {"nomesg", 2, BOOT_NOMESG, CMDS_WIZARD | SWITCH_SINGULAR}, {(char *)NULL, 0, 0, 0} }; static SWITCH case_switches[] = { {"first", 2, CASE_FIRST, CMDS_NORMAL | SWITCH_SINGULAR}, {(char *)NULL, 0, 0, 0} }; static SWITCH group_switches[] = { {"delete", 6, GROUP_DISOLVE, CMDS_NGUEST | SWITCH_SINGULAR}, {"open", 4, GROUP_OPEN, CMDS_NGUEST | SWITCH_SINGULAR}, {"close", 5, GROUP_CLOSE, CMDS_NGUEST | SWITCH_SINGULAR}, {"boot", 4, GROUP_BOOT, CMDS_NGUEST | SWITCH_SINGULAR}, {"say", 1, GROUP_SAY, CMDS_NORMAL | SWITCH_SINGULAR}, {"emote", 1, GROUP_EMOTE, CMDS_NORMAL | SWITCH_SINGULAR}, {"display", 3, GROUP_DISPLAY, CMDS_NORMAL | SWITCH_SINGULAR}, {"all", 3, GROUP_ALL, CMDS_NORMAL | SWITCH_MULTIPLE}, {"follow", 3, GROUP_FOLLOW, CMDS_NORMAL | SWITCH_SINGULAR}, {"join", 1, GROUP_JOIN, CMDS_NORMAL | SWITCH_SINGULAR}, {"silence", 3, GROUP_SILENCE, CMDS_NORMAL | SWITCH_SINGULAR}, {"noisy", 3, GROUP_NOISY, CMDS_NORMAL | SWITCH_SINGULAR}, {(char *)NULL, 0, 0, 0} }; static SWITCH emit_switches[] = { {"others", 1, EMIT_OTHERS, CMDS_NORMAL | SWITCH_SINGULAR}, {"html", 4, EMIT_HTML, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *)NULL, 0, 0, 0} }; static SWITCH pemit_switches[] = { {"html", 4, PEMIT_HTML, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *)NULL, 0, 0, 0} }; static SWITCH edit_switches[] = { {"all", 1, EDIT_ALL, CMDS_NORMAL | SWITCH_MULTIPLE}, {"no_space", 2, EDIT_NOSPACE, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *)NULL, 0, 0, 0} }; static SWITCH lockout_switches[] = { {"all", 3, LOCKOUT_ALL, CMDS_WIZARD | SWITCH_SINGULAR}, {"allow", 5, LOCKOUT_ALLOW, CMDS_WIZARD | SWITCH_SINGULAR}, {"clear", 5, LOCKOUT_CLEAR, CMDS_WIZARD | SWITCH_SINGULAR}, {"enable", 2, LOCKOUT_ENABLE, CMDS_WIZARD | SWITCH_SINGULAR}, {"dump", 3, LOCKOUT_DUMP, CMDS_WIZARD | SWITCH_SINGULAR}, {"register", 2, LOCKOUT_REGISTER, CMDS_WIZARD | SWITCH_SINGULAR}, {(char *)NULL, 0, 0, 0} }; static SWITCH copy_switches[] = { {"move", 1, COPY_MOVE, CMDS_NORMAL | SWITCH_SINGULAR}, {(char *)NULL, 0, 0, 0} }; static SWITCH savestty_switches[] = { {"clear", 1, SAVESTTY_CLEAR, CMDS_NORMAL | SWITCH_SINGULAR}, {"restore", 1, SAVESTTY_RESTORE, CMDS_NORMAL | SWITCH_SINGULAR}, {(char *)NULL, 0, 0, 0} }; static SWITCH help_switches[] = { {"reload", 1, HELP_RELOAD, CMDS_WIZARD | SWITCH_SINGULAR}, {"nohtml", 3, HELP_NOHTML, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *)NULL, 0, 0, 0} }; static SWITCH news_switches[] = { {"reload", 1, NEWS_RELOAD, CMDS_WIZARD | SWITCH_SINGULAR}, {"nohtml", 3, NEWS_NOHTML, CMDS_NORMAL | SWITCH_MULTIPLE}, {(char *)NULL, 0, 0, 0} }; /* static commands table. */ static CMDS cmddefs[] = { /* drop */ {"drop", do_drop, NULL, 1, CMDS_NORMAL|CMDS_NOSED}, /* enter */ {"enter", do_enter, NULL, 1, CMDS_NORMAL|CMDS_NOSED}, /* examine */ {"examine", do_examine, ex_switches, 2, CMDS_NORMAL}, /* give */ {"give", do_give, NULL, 2, CMDS_NGUEST|CMDS_NOSED}, /* go */ {"go", do_go, NULL, 1, CMDS_NORMAL|CMDS_NOSED}, /* gripe */ {"gripe", do_gripe, NULL, 1, CMDS_NORMAL}, /* hand */ {"hand", do_hand, NULL, 2, CMDS_NORMAL}, /* help */ {"help", do_help, help_switches, 1, CMDS_NORMAL}, /* home */ {"home", do_home, NULL, 0, CMDS_NORMAL|CMDS_NOSED}, /* inventory */ {"inventory", do_inventory, NULL, 0, CMDS_NORMAL}, /* kill */ {"kill", do_kill, kill_switches, 2, CMDS_NGUEST}, /* leave */ {"leave", do_leave, NULL, 0, CMDS_NORMAL|CMDS_NOSED}, /* look */ {"look", do_look, NULL, 1, CMDS_NORMAL}, /* news */ {"news", do_news, news_switches, 1, CMDS_NORMAL}, /* page */ {"page", do_page, NULL, 2, CMDS_NORMAL|CMDS_SPEECH}, /* pose */ {"pose", do_pose, pose_switches, 1, CMDS_NORMAL|CMDS_SPEECH}, /* say */ {"say", do_say, NULL, 1, CMDS_NORMAL|CMDS_SPEECH}, /* score */ {"score", do_score, NULL, 0, CMDS_NORMAL}, /* take */ {"take", do_take, NULL, 1, CMDS_NORMAL|CMDS_NOSED}, /* use */ {"use", do_use, NULL, 1, CMDS_NORMAL}, /* whisper */ {"whisper", do_whisper, NULL, 2, CMDS_NORMAL|CMDS_SPEECH}, /* @attach */ {"@attach", do_attach, NULL, 2, CMDS_NGUEST}, /* @boot */ {"@boot", do_boot, boot_switches, 2, CMDS_WIZARD|CMDS_NGUEST}, /* @charge */ {"@charge", do_charge, NULL, 2, CMDS_NGUEST}, /* @case */ {"@case", do_case, case_switches, CMDS_VARARGS, CMDS_NORMAL}, /* @children */ {"@children", do_children, NULL, 2, CMDS_NGUEST}, /* @chownall */ {"@chownall", do_chownall, NULL, 2, CMDS_WIZARD|CMDS_NGUEST}, /* @chown */ {"@chown", do_chown, NULL, 2, CMDS_NGUEST}, /* @clone */ {"@clone", do_clone, NULL, 2, CMDS_BUILDER|CMDS_NGUEST|CMDS_NOSED}, /* @config */ {"@config", do_config, config_switches, 2, CMDS_NORMAL}, /* @copy */ {"@copy", do_copy, copy_switches, 2, CMDS_NGUEST}, /* @cost */ {"@cost", do_cost, NULL, 2, CMDS_NGUEST}, /* @create */ {"@create", do_create, NULL, 1, CMDS_BUILDER|CMDS_NGUEST|CMDS_NOSED}, /* @dig */ {"@dig", do_dig, NULL, 2, CMDS_BUILDER|CMDS_NGUEST|CMDS_NOSED}, /* @doing */ {"@doing", do_doing, NULL, 2, CMDS_PLAYER|CMDS_SPEECH}, /* @dump */ {"@dump", do_dump, NULL, 0, CMDS_WIZARD}, /* @edit */ {"@edit", do_edit, edit_switches, CMDS_VARARGS, CMDS_NGUEST|CMDS_VARSLASH|CMDS_NOEXEC}, /* @emit */ {"@emit", do_emit, emit_switches, 1, CMDS_NORMAL|CMDS_SPEECH}, /* @entrances */ {"@entrances", do_entrances, NULL, 1, CMDS_NGUEST}, /* @foreach */ {"@foreach", do_foreach, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC}, /* @force */ {"@force", do_force, NULL, 2, CMDS_NGUEST}, /* @find */ {"@find", do_find, find_switches, 2, CMDS_NGUEST}, /* @group */ {"@group", do_group, group_switches, 1, CMDS_NORMAL}, /* @halt */ {"@halt", do_halt, halt_switches, 1, CMDS_NGUEST}, /* @lock */ {"@lock", do_lock, NULL, 2, CMDS_NGUEST}, /* @lockout */ {"@lockout", do_lockout, lockout_switches, 2, CMDS_WIZARD}, /* @link */ {"@link", do_link, link_switches, 2, CMDS_NGUEST}, /* @motd */ {"@motd", do_motd, motd_switches, 2, CMDS_NORMAL}, /* @name */ {"@name", do_name, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC}, /* @newpassword */ {"@newpassword", do_newpassword, NULL, 2, CMDS_WIZARD|CMDS_NGUEST|CMDS_NOEXEC}, /* @notify */ {"@notify", do_notify, notify_switches, 2, CMDS_NGUEST}, /* @open */ {"@open", do_open, NULL, 2, CMDS_BUILDER|CMDS_NGUEST|CMDS_NOSED}, /* @owned */ {"@owned", do_owned, NULL, 2, CMDS_WIZARD|CMDS_NGUEST}, /* @palias */ {"@palias", do_palias, NULL, 2, CMDS_NGUEST}, /* @parent */ {"@parent", do_parent, NULL, 2, CMDS_BUILDER|CMDS_NGUEST}, /* @password */ {"@password", do_password, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC}, /* @pemit */ {"@pemit", do_pemit, pemit_switches, 2, CMDS_NORMAL|CMDS_SPEECH}, /* @pcreate */ {"@pcreate", do_pcreate, NULL, 2, CMDS_GOD|CMDS_NGUEST|CMDS_NOSED}, /* @poll */ {"@poll", do_poll, NULL, 1, CMDS_WIZARD|CMDS_NGUEST}, /* @poor */ {"@poor", do_poor, NULL, 1, CMDS_GOD|CMDS_NGUEST}, /* @ps */ {"@ps", do_ps, ps_switches, 1, CMDS_NGUEST}, /* @purge */ {"@purge", do_purge, NULL, 1, CMDS_WIZARD | CMDS_NGUEST}, /* @quota */ {"@quota", do_quota, NULL, 2, CMDS_NGUEST}, /* @recycle */ {"@recycle", do_recycle, recycle_switches, 1, CMDS_NGUEST}, /* @savestty */ {"@savestty", do_savestty, savestty_switches, 1, CMDS_NGUEST}, /* @semaphore */ {"@semaphore", do_semaphore, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC}, /* @set */ {"@set", do_set, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC}, /* @shutdown */ {"@shutdown", do_shutdown, NULL, 0, CMDS_WIZARD|CMDS_NGUEST}, /* @stats */ {"@stats", do_stats, stats_switches, 1, CMDS_NGUEST}, /* @sweep */ {"@sweep", do_sweep, NULL, 1, CMDS_NORMAL}, /* @systat */ {"@systat", do_systat, NULL, 0, CMDS_WIZARD|CMDS_NGUEST}, /* @teleport */ {"@teleport", do_teleport, NULL, 2, CMDS_NGUEST}, /* @toad */ {"@toad", do_toad, NULL, 2, CMDS_WIZARD|CMDS_NGUEST}, /* @trace */ {"@trace", do_trace, NULL, 2, CMDS_NGUEST}, /* @trigger */ {"@trigger", do_trigger, NULL, CMDS_VARARGS, CMDS_NGUEST|CMDS_VARSLASH}, /* @unlink */ {"@unlink", do_unlink, NULL, 1, CMDS_NGUEST}, /* @unlock */ {"@unlock", do_unlock, NULL, 1, CMDS_NGUEST}, /* @version */ {"@version", do_version, version_switches, 0, CMDS_NORMAL}, /* @wait */ {"@wait", do_wait, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC}, /* @wall */ {"@wall", do_wall, wall_switches, 2, CMDS_WIZARD|CMDS_NGUEST}, /* @wipe */ {"@wipe", do_wipe, NULL, CMDS_VARARGS, CMDS_NGUEST|CMDS_VARSLASH|CMDS_NOEXEC}, {NULL, NULL, NULL, 0, 0} }; static int CmdHash_Inited = 0; static Hash_Table CmdHash; static int AliasSw_Inited = 0; static Hash_Table AliasSw; static int fexec = 0; static void cmdhash_init _ANSI_ARGS_((void)); static int cmdaccess _ANSI_ARGS_((int, int)); static void notify_huh _ANSI_ARGS_((int, int)); static void notify_halt _ANSI_ARGS_((int, int)); static void parse_argtwo _ANSI_ARGS_((int, int, char *, char **, char **, int, int, char *[])); static void parse_argvee _ANSI_ARGS_((int, int, char *, int *, char *[], int, int, char *[])); static int handle_exit_cmd _ANSI_ARGS_((int, int, char *)); static int parse_switches _ANSI_ARGS_((int, int, CMDS *, char *)); int command_alias(cmd, alias, switches) char *cmd, *alias, *switches; { Hash_Entry *find, *entry; int new; if((cmd == (char *)NULL) || (*cmd == '\0') || (alias == (char *)NULL) || (*alias == '\0')) return(-1); if(!CmdHash_Inited) cmdhash_init(); if((find = Hash_FindEntry(&CmdHash, cmd)) == NULL) return(-1); if((entry = Hash_FindEntry(&CmdHash, alias)) == NULL) { entry = Hash_CreateEntry(&CmdHash, alias, (int *)NULL); Hash_SetValue(entry, Hash_GetValue(find)); if(switches != (char *)NULL) { if(!AliasSw_Inited) { Hash_InitTable(&AliasSw, 20, HASH_STRCASE_KEYS); AliasSw_Inited++; } entry = Hash_CreateEntry(&AliasSw, alias, &new); if(!new) ty_free((VOID *)Hash_GetValue(entry)); Hash_SetValue(entry, (int *)ty_strdup(switches, "command_alias")); } } else return(-1); return(0); } int command_unalias(cmd) char *cmd; { Hash_Entry *entry; if((cmd == (char *)NULL) || (*cmd == '\0')) return(-1); if(!CmdHash_Inited) cmdhash_init(); if((entry = Hash_FindEntry(&CmdHash, cmd)) == NULL) return(-1); Hash_DeleteEntry(&CmdHash, entry); if(AliasSw_Inited) { entry = Hash_FindEntry(&AliasSw, cmd); if(entry != NULL) { ty_free((VOID *)Hash_GetValue(entry)); Hash_DeleteEntry(&AliasSw, entry); } } return(0); } int command_aliasexp(cmd, buf, bufsiz) char *cmd, *buf; int bufsiz; { register Hash_Entry *centry, *sentry; register char *bptr, *iptr; if((cmd == (char *)NULL) || (cmd[0] == '\0') || !CmdHash_Inited) return(-1); if((centry = Hash_FindEntry(&CmdHash, cmd)) == (Hash_Entry *)NULL) return(-1); if(AliasSw_Inited) { sentry = Hash_FindEntry(&AliasSw, cmd); } else sentry = (Hash_Entry *)NULL; /* Copy it in. */ bptr = buf; iptr = (char *)Hash_GetValue(centry); while(((bptr - buf) < (bufsiz - 1)) && *iptr) *bptr++ = *iptr++; if((bptr - buf) < (bufsiz - 1)) *bptr++ = '/'; if(sentry != (Hash_Entry *)NULL) { iptr = (char *)Hash_GetValue(sentry); while(((bptr - buf) < (bufsiz - 1)) && *iptr) *bptr++ = *iptr++; } *bptr = '\0'; return(0); } static void cmdhash_init() { register CMDS *cptr; register Hash_Entry *entry; Hash_InitTable(&CmdHash, sizeof(cmddefs) / sizeof(CMDS), HASH_STRCASE_KEYS); for(cptr = cmddefs; cptr->name != NULL; cptr++){ entry = Hash_CreateEntry(&CmdHash, cptr->name, NULL); Hash_SetValue(entry, (int *)cptr); } CmdHash_Inited = 1; } static int cmdaccess(player, perms) int player, perms; { if ((perms & CMDS_NGUEST) && isGUEST(player)) return (0); if ((perms & CMDS_PLAYER) && !isPLAYER(player)) return (0); if ((perms & CMDS_NOSED) && (isROOM(player) || isEXIT(player))) return (0); switch (perms & CMDS_MASK) { case CMDS_NORMAL: return (1); case CMDS_GOD: return (isGOD(player)); case CMDS_WIZARD: return (isWIZARD(player)); case CMDS_BUILDER: return (isBUILDER(player) || isWIZARD(player)); } return (0); } static void notify_huh(player, cause) int player, cause; { notify_player(player, cause, player, "Huh? (Type \"help\" for help.)", 0); } static void notify_halt(player, cause) int player, cause; { char buff[BUFFSIZ]; int owner; if(get_int_elt(player, OWNER, &owner) != -1) { snprintf(buff, sizeof(buff), "Attempt to execute command by halted object #%d.", player); notify_player(owner, cause, player, buff, 0); } } #define noexec(_f, _p) ((_f & CMDS_NOEXEC) \ || ((_f & CMDS_SPEECH) && isELOQUENT(_p))) static void parse_argtwo(player, cause, str, argone, argtwo, flags, eargc, eargv) int player, cause; char *str, **argone, **argtwo; int flags, eargc; char *eargv[]; { register char *ptr1, *ptr2; register int len; if(!*str) { *argone = (char *)NULL; *argtwo = (char *)NULL; return; } /* find argtwo, if there is one. */ ptr1 = parse_to(str, '\0', '='); /* eat trailing whitespace of argone, if there is one. */ if(*str) { for(ptr2 = &str[strlen(str)]; (ptr2 > str) && ((*ptr2 == '\0') || isspace(*ptr2)); ptr2--); ptr2[1] = '\0'; } /* set up return values. */ if(!fexec && noexec(flags, player)) { /* there isn't always an argone. */ if(*str) { len = strlen(str)+1; *argone = (char *)ty_malloc(len, "parse_argtwo.argone"); parse_copy(*argone, str, len); } else *argone = (char *)NULL; if(ptr1) { /* there is an argtwo. */ /* eat leading whitespace of argtwo. */ while(*ptr1 && isspace(*ptr1)) ptr1++; /* if we ran out, we loose. */ if(ptr1) { len = strlen(ptr1)+1; *argtwo = (char *)ty_malloc(len, "parse_argtwo.argtwo"); parse_copy(*argtwo, ptr1, len); } else *argtwo = NULL; } else *argtwo = NULL; } else { /* exec stuff. */ /* there isn't always an argone. */ if(*str) { *argone = exec(player, cause, player, str, 0, eargc, eargv); } else { *argone = (char *)NULL; } /* eat leading whitespace of argtwo. */ while(ptr1 && *ptr1 && isspace(*ptr1)) ptr1++; if(ptr1) { /* there is an argtwo. */ *argtwo = exec(player, cause, player, ptr1, 0, eargc, eargv); } else { *argtwo = NULL; } } } static void parse_argvee(player, cause, str, argc, argv, flags, eargc, eargv) int player, cause; register char *str; int *argc; char *argv[]; int flags, eargc; char *eargv[]; { register char *ptr1, *ptr2; register int count = 0, len; int fixhack = 0; *argc = 0; if(*str) { /* first argument: up to first '=', or '/'. */ if((flags & CMDS_VARSLASH) && (ptr1 = parse_to(str, '\0', '/'))) { if(*str) { for(ptr2 = &str[strlen(str)]; (ptr2 > str) && ((*ptr2 == '\0') || isspace(*ptr2)); ptr2--); ptr2[1] = '\0'; } if(!fexec && noexec(flags, player)) { len = strlen(str)+1; argv[count] = (char *)ty_malloc(len, "parse_argvee.argv"); parse_copy(argv[count++], str, len); } else argv[count++] = exec(player, cause, player, str, 0, eargc, eargv); str = ptr1; } else if((flags & CMDS_VARSLASH) && (ptr1 == NULL)) fixhack++; ptr1 = parse_to(str, '\0', '='); if(*str) { for(ptr2 = &str[strlen(str)]; (ptr2 > str) && ((*ptr2 == '\0') || isspace(*ptr2)); ptr2--); ptr2[1] = '\0'; } if(!fexec && noexec(flags, player)) { len = strlen(str)+1; argv[count] = (char *)ty_malloc(len, "parse_argvee.argv"); parse_copy(argv[count++], str, len); } else { argv[count++] = exec(player, cause, player, str, 0, eargc, eargv); } /* eat any leading whitespace. */ while(ptr1 && *ptr1 && isspace(*ptr1)) ptr1++; if(ptr1 && *ptr1) { /* skip one, if need be. */ count += fixhack; /* now parse up to ','. */ while(count < MAXARGS){ for(str = ptr1; *str && isspace(*str); str++); if(!*str) break; /* we're done. */ ptr1 = parse_to(str, '\0', ','); if(*str) { for(ptr2 = &str[strlen(str)]; (ptr2 > str) && ((*ptr2 == '\0') || isspace(*ptr2)); ptr2--); ptr2[1] = '\0'; } if(!fexec && noexec(flags, player)) { len = strlen(str)+1; argv[count] = (char *)ty_malloc(len, "parse_argvee.argv"); parse_copy(argv[count++], str, len); } else { argv[count++] = exec(player, cause, player, str, 0, eargc, eargv); } if(!ptr1 || !*ptr1) break; /* we're done. */ } } } *argc = count; } static int handle_exit_cmd(player, cause, str) int player, cause; char *str; { int here, theex; if((get_int_elt(player, LOC, &here) == -1) || !exists_object(here)) { logfile(LOG_ERROR, "handle_exit_cmd: bad location of player #%d\n", player); return(0); } theex = match_exit_command(player, cause, str); if(theex != -1) { do_go_attempt(player, cause, here, theex); return(1); } return(0); } /* * Switch parser, broken out for versatility's sake. * * This way, everything sees at least global switches. */ static int parse_switches(player, cause, cptr, swptr) int player, cause; CMDS *cptr; char *swptr; { register int overtotal = 0; /* overall total. */ register int intotal = 0; /* inner total. */ register int doglob = 0; /* doing globals. */ register SWITCH *sptr; int switches; switches = 0; if (swptr != NULL) { if((cptr != NULL) && (cptr->switches != NULL)) sptr = cptr->switches; else { doglob++; sptr = globalcmd_switches; } while (*swptr) { while(*swptr && (sptr->name != NULL)) { if (!strncasecmp(sptr->name, swptr, sptr->matlen)) { if (!cmdaccess(player, sptr->flags)) { notify_player(player, cause, player, "Permission denied.", NOT_QUIET); return(-1); } if (overtotal && (sptr->flags & SWITCH_SINGULAR)) { notify_player(player, cause, player, "Illegal combination of command switches.", NOT_QUIET); return(-1); } else { switches |= sptr->code; if (sptr->flags & SWITCH_SINGULAR) { overtotal++; } } intotal++; } sptr++; } /* * If we've not checked globals yet, do it now. */ if (!doglob) { doglob++; sptr = globalcmd_switches; continue; /* Go back to the top of the while. */ } else doglob = 0; if (!intotal) { notify_player(player, cause, player, "Illegal command switch.", NOT_QUIET); return(-1); } for (; *swptr && (*swptr != '/'); swptr++); if (*swptr) swptr++; } if (!switches) { notify_player(player, cause, player, "Illegal switches.", NOT_QUIET); return(-1); } } return(switches); } /* main command parser. */ void handle_cmd(player, cause, origcmd, eargc, eargv) int player, cause; char *origcmd; int eargc; char *eargv[]; { char *cmd, *argv[MAXARGS], *swptr, *argone, *argtwo; register char *ptr = (char *)NULL; register int i; register Hash_Entry *entry; CMDS *cptr; int argc = 0, switches = 0; char *name; if (mudconf.logcommands) { if(get_str_elt(player, NAME, &name) == -1) name = "???"; /* XXX */ logfile(LOG_COMMAND, "COMMAND: %s(#%d): %s\n", name, player, origcmd); } fexec = 0; /* Reset forced mode. */ /* Kill any leading and trailing whitespace. */ if((origcmd != (char *)NULL) && (origcmd[0] != '\0')) { while(*origcmd && isspace(*origcmd)) origcmd++; if(*origcmd) { ptr = &origcmd[strlen(origcmd)-1]; while(*ptr && isspace(*ptr) && (ptr >= origcmd)) ptr--; ptr[1] = '\0'; /* Leave ptr set for the exec check! */ } /* Check if we want forced execution. Use ptr from above. */ if((origcmd[0] == '<') && (ptr != (char *)NULL) && (*ptr == '>')) { fexec++; origcmd++; /* Skip past the leading token... */ *ptr = '\0'; /* ...And eat the trailing one. */ } } /* Check if we want forced execution. Use ptr from above. */ /* Perform sanity checks. */ if((origcmd == (char *)NULL) || (origcmd[0] == '\0')) return; if(!exists_object(player) || !exists_object(cause)) { logfile(LOG_ERROR, "handle_cmd: called with bad player (#%d) or cause (#%d)\n", player, cause); return; } /* Are they halted? */ if(!isPLAYER(player) && isHALT(player)) { notify_halt(player, cause); return; } /* init the hash table, if need be. */ if(!CmdHash_Inited) cmdhash_init(); /* check these first. */ if(origcmd[0] == '\"') { if(origcmd[1] && !fexec && !noexec(CMDS_SPEECH, player)) { ptr = exec(player, cause, player, &origcmd[1], 0, eargc, eargv); do_say(player, cause, switches, ptr); ty_free((VOID *)ptr); } else do_say(player, cause, switches, &origcmd[1]); return; } else if(origcmd[0] == ':') { if(origcmd[1] && !fexec && !noexec(CMDS_SPEECH, player)) { ptr = exec(player, cause, player, &origcmd[1], 0, eargc, eargv); do_pose(player, cause, switches, ptr); ty_free((VOID *)ptr); } else do_pose(player, cause, switches, &origcmd[1]); return; } else if((origcmd[0] == '@') && (origcmd[1] == '@')) { return; } /* lock the cache. */ cache_lock(); /* is this an exit? */ if((origcmd[0] != '@') && (origcmd[0] != '&')) { if(handle_exit_cmd(player, cause, origcmd)) { if(mudstat.status != MUD_DUMPING) cache_unlock(); return; } } /* save ourselves. */ cmd = (char *)alloca(strlen(origcmd)+1); if(cmd == (char *)NULL) panic("handle_cmd(): stack allocation failed.\n"); strcpy(cmd, origcmd); /* init argument array, for later. */ for(i = 0; i < MAXARGS; i++) argv[i] = NULL; /* argument 0 is the first word, up to whitespace *or* slash. */ for(ptr = cmd; *ptr && !isspace(*ptr) && (*ptr != '/'); ptr++); if(*ptr == '/') { /* parse switch, up to next whitespace. */ for(*ptr++ = '\0', swptr = ptr; *ptr && !isspace(*ptr); ptr++); } else swptr = NULL; /* no switches. */ if(*ptr) { /* skip next whitespace. */ for(*ptr++ = '\0'; *ptr && isspace(*ptr); ptr++); } /* check for aliased switches. */ if(AliasSw_Inited) { entry = Hash_FindEntry(&AliasSw, cmd); if(entry != NULL){ register char *tptr, *aptr; aptr = (char *)Hash_GetValue(entry); if(swptr != NULL) { tptr = (char *)alloca(strlen(aptr) + strlen(swptr) + 2); if(tptr == (char *)NULL) panic("handle_cmd: stack allocation failed.\n"); strcpy(tptr, aptr); strcat(tptr, "/"); strcat(tptr, swptr); } else { tptr = (char *)alloca(strlen(aptr) + 1); if(tptr == (char *)NULL) panic("handle_cmd: stack allocation failed.\n"); strcpy(tptr, (char *)Hash_GetValue(entry)); } swptr = tptr; } } /* try to find the command. */ entry = Hash_FindEntry(&CmdHash, cmd); if(entry != NULL){ cptr = (CMDS *)Hash_GetValue(entry); /* check access. */ if(!cmdaccess(player, cptr->perms)) { notify_player(player, cause, player, "Permission denied.", 0); if(mudstat.status != MUD_DUMPING) cache_unlock(); return; } /* parse switches. */ switches = parse_switches(player, cause, cptr, swptr); if(switches == -1) { /* abort */ if (mudstat.status != MUD_DUMPING) cache_unlock(); return; } /* Prevent excessive executing. */ if(fexec) switches |= ARG_FEXEC; /* parse extra arguments if needed, and/or exec them, and run command. */ switch(cptr->nargs){ case 0: /* simple case */ (cptr->function) (player, cause, switches); break; case 1: /* pretty simple case -- ptr is argument one. */ if(*ptr && (fexec || !noexec(cptr->perms, player))) { register char *tptr = exec(player, cause, player, ptr, 0, eargc, eargv); (cptr->function) (player, cause, switches, tptr); ty_free((VOID *)tptr); } else (cptr->function) (player, cause, switches, ptr); break; case 2: /* complex */ /* ptr is at first argument, '=' seperates them. */ parse_argtwo(player, cause, ptr, &argone, &argtwo, cptr->perms, eargc, eargv); (cptr->function) (player, cause, switches, argone, argtwo); ty_free((VOID *) argone); ty_free((VOID *) argtwo); break; case CMDS_VARARGS: /* AUGH */ /* ptr is at the start of the argument string. */ parse_argvee(player, cause, ptr, &argc, argv, cptr->perms, eargc, eargv); (cptr->function) (player, cause, switches, argc, argv); free_argv(argc, argv); break; } } else { /* parse switches. */ switches = parse_switches(player, cause, (CMDS *)NULL, swptr); if(switches == -1) { /* abort */ if (mudstat.status != MUD_DUMPING) cache_unlock(); return; } switch(cmd[0]){ case '@': /* standard attribute, or exit. */ for (i = 0; i < ATABLE_TOTAL; i++) { if (Atable[i].command && (stringprefix((Atable[i].calias) ? Atable[i].calias : Atable[i].name, cmd+1) || stringprefix(Atable[i].name, cmd+1)) && cmd[1] && cmd[2] && cmd[2] != ' ') { parse_argtwo(player, cause, ptr, &argone, &argtwo, CMDS_NOEXEC, eargc, eargv); do_set_string(player, cause, switches, argone, argtwo, &Atable[i]); ty_free((VOID *)argone); ty_free((VOID *)argtwo); if (mudstat.status != MUD_DUMPING) cache_unlock(); cache_trim(); return; } } /* check for an exit. */ if(!handle_exit_cmd(player, cause, origcmd)) notify_huh(player, cause); break; case '&': /* attribute. */ for (cmd++; *cmd && isspace(*cmd); cmd++); parse_argtwo(player, cause, ptr, &argone, &argtwo, CMDS_NOEXEC, eargc, eargv); i = resolve_object(player, cause, argone, (!(switches & CMD_QUIET) ? RSLV_EXITS|RSLV_NOISY : RSLV_EXITS)); if (i != -1) { if (cmd && *cmd) { set_attribute(player, cause, switches, i, cmd, argtwo); } else { if(!(switches & CMD_QUIET)) notify_player(player, cause, player, "Nothing to do.", NOT_QUIET); } ty_free((VOID *)argone); ty_free((VOID *)argtwo); } break; case '!': /* force */ for (cmd++; *cmd && isspace(*cmd); cmd++); do_force(player, cause, switches, cmd, ptr); break; default: /* try other things... */ if(!attr_command(player, cause, '$', origcmd)) notify_huh(player, cause); break; } } if(mudstat.status != MUD_DUMPING) cache_unlock(); } /* do the command thing, recursively. */ void handle_cmds(player, cause, str, eargc, eargv) int player, cause; char *str; int eargc; char *eargv[]; { register char *ptr, *cmds; if((str == NULL) || (*str == '\0')) return; /* we don't want to write into str- it might be an attribute. */ cmds = (char *)alloca(strlen(str)+1); if(cmds == (char *)NULL) panic("handle_cmds(): stack allocation failed\n"); strcpy(cmds, str); /* commands are seperated by an unquoted semi-colon, or enclosed in braces. */ /* we strip braces, but *don't* do anything with backslashes. */ while(1) { while(*cmds && isspace(*cmds)) cmds++; if(!*cmds) break; /* we're done. */ if(*cmds == '{') { cmds++; ptr = parse_to(cmds, '{', '}'); if(ptr == NULL) { /* fuck you, too. */ handle_cmd(player, cause, &cmds[-1], eargc, eargv); return; /* we're done. */ } handle_cmd(player, cause, cmds, eargc, eargv); cmds = ptr; } else { ptr = parse_to(cmds, '\0', ';'); handle_cmd(player, cause, cmds, eargc, eargv); if(ptr && *ptr) cmds = ptr; else break; /* we're done. */ } } }