// cmd.c -- Quake script command processing module #include "quakedef.h" void Cmd_ForwardToServer (void); #define MAX_ALIAS_NAME 32 typedef struct cmdalias_s { struct cmdalias_s *next; char name[MAX_ALIAS_NAME]; char *value; }cmdalias_t; cmdalias_t *cmd_alias; int trashtest; int *trashspot; bool cmd_wait; /* * Causes execution of the remainder of the command buffer to be delayed until * next frame. This allows commands like: * bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2" */ void Cmd_Wait_f(void) { cmd_wait = true; } sizebuf_t cmd_text; void Cbuf_Init(void) { SZ_Alloc(&cmd_text, 8192); // space for commands and script files } void Cbuf_AddText(char *text) { int l; l = strlen(text); if (cmd_text.cursize + l >= cmd_text.maxsize) { Con_Printf("Cbuf_AddText: overflow\n"); return; } SZ_Write(&cmd_text, text, strlen (text)); } void Cbuf_InsertText(char *text) { char *temp; int templen; // copy off any commands still remaining in the exec buffer templen = cmd_text.cursize; if (templen) { temp = Z_Malloc(templen); memmove(temp, cmd_text.data, templen); SZ_Clear(&cmd_text); } else { temp = NULL; // shut up compiler } // add the entire text of the file Cbuf_AddText(text); // add the copied off data if (templen) { SZ_Write(&cmd_text, temp, templen); Z_Free(temp); } } void Cbuf_Execute(void) { int i; char *text; char line[1024]; int quotes; while (cmd_text.cursize) { // find a \n or ; line break text = (char *)cmd_text.data; quotes = 0; for (i = 0; i < cmd_text.cursize; i++) { if (text[i] == '"') quotes++; if (!(quotes&1) && text[i] == ';') break; // don't break if inside a quoted string if (text[i] == '\n') break; } memmove(line, text, i); line[i] = 0; // delete the text from the command buffer and move remaining commands down // this is necessary because commands (exec, alias) can insert data at the // beginning of the text buffer if (i == cmd_text.cursize) { cmd_text.cursize = 0; } else { i++; cmd_text.cursize -= i; memmove(text, text+i, cmd_text.cursize); } // execute the command line Cmd_ExecuteString (line, src_command); if (cmd_wait) { // skip out while text still remains in buffer, leaving it for next frame cmd_wait = false; break; } } } /* * Adds command line parameters as script statements * Commands lead with a +, and continue until a - or another + * quake +prog jctest.qp +cmd amlev1 * quake -nosound +cmd amlev1 */ void Cmd_StuffCmds_f(void) { int i, j; int s; char *text, *build, c; if (Cmd_Argc() != 1) { Con_Printf("stuffcmds: execute command line parameters\n"); return; } // build the combined string to parse from s = 0; for (i = 1; i < com_argc; i++) { if (!com_argv[i]) continue; s += strlen (com_argv[i]) + 1; } if (!s) return; text = Z_Malloc(s+1); text[0] = 0; for (i = 1; i < com_argc; i++) { if (!com_argv[i]) continue; strcat(text, com_argv[i]); if (i != com_argc-1) strcat(text, " "); } // pull out the commands build = Z_Malloc(s+1); build[0] = 0; for (i = 0; i < s-1; i++) { if (text[i] == '+') { i++; for (j = i; text[j] != '+' && text[j] != '-' && text[j] != 0; j++); c = text[j]; text[j] = 0; strcat(build, text+i); strcat(build, "\n"); text[j] = c; i = j-1; } } if (build[0]) Cbuf_InsertText(build); Z_Free(text); Z_Free(build); } void Cmd_Exec_f(void) { char *f; int mark; if (Cmd_Argc() != 2) { Con_Printf("exec : execute a script file\n"); return; } mark = Hunk_LowMark(); f = (char *)COM_LoadHunkFile(Cmd_Argv(1)); if (!f) { Con_Printf("couldn't exec %s\n", Cmd_Argv(1)); return; } Con_Printf("execing %s\n", Cmd_Argv(1)); Cbuf_InsertText(f); Hunk_FreeToLowMark(mark); } void Cmd_Echo_f(void) { int i; for (i = 1; i < Cmd_Argc(); i++) Con_Printf("%s ",Cmd_Argv(i)); Con_Printf("\n"); } char * CopyString(char *in) { char *out; out = Z_Malloc (strlen(in)+1); strcpy(out, in); return out; } void Cmd_Alias_f(void) { cmdalias_t *a; char cmd[1024]; int i, c; char *s; if (Cmd_Argc() == 1) { Con_Printf("Current alias commands:\n"); for (a = cmd_alias; a; a = a->next) Con_Printf("%s: %s\n", a->name, a->value); return; } s = Cmd_Argv(1); if (strlen(s) >= MAX_ALIAS_NAME) { Con_Printf("Alias name is too long\n"); return; } // if the alias allready exists, reuse it for (a = cmd_alias; a; a = a->next) { if (!strcmp(s, a->name)) { Z_Free (a->value); break; } } if (!a) { a = Z_Malloc(sizeof(cmdalias_t)); a->next = cmd_alias; cmd_alias = a; } strcpy(a->name, s); // copy the rest of the command line cmd[0] = 0; // start out with a null string c = Cmd_Argc(); for (i = 2; i < c; i++) { strcat(cmd, Cmd_Argv(i)); if (i != c) strcat(cmd, " "); } strcat(cmd, "\n"); a->value = CopyString(cmd); } typedef struct cmd_function_s { struct cmd_function_s *next; char *name; xcommand_t function; } cmd_function_t; #define MAX_ARGS 80 static int cmd_argc; static char *cmd_argv[MAX_ARGS]; static char *cmd_null_string = ""; static char *cmd_args = NULL; cmd_source_t cmd_source; static cmd_function_t *cmd_functions; // possible commands to execute void Cmd_Init(void) { Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f); Cmd_AddCommand ("exec",Cmd_Exec_f); Cmd_AddCommand ("echo",Cmd_Echo_f); Cmd_AddCommand ("alias",Cmd_Alias_f); Cmd_AddCommand ("cmd", Cmd_ForwardToServer); Cmd_AddCommand ("wait", Cmd_Wait_f); } int Cmd_Argc(void) { return cmd_argc; } char * Cmd_Argv(int arg) { if (arg >= cmd_argc || arg < 0) return cmd_null_string; return cmd_argv[arg]; } char * Cmd_Args(void) { return cmd_args; } void Cmd_TokenizeString(char *text) { int i; // clear the args from the last string for (i = 0; i < cmd_argc ; i++) Z_Free(cmd_argv[i]); cmd_argc = 0; cmd_args = NULL; while (1) { // skip whitespace up to a /n while (*text && *text <= ' ' && *text != '\n') text++; if (*text == '\n') { // a newline seperates commands in the buffer text++; break; } if (!*text) return; if (cmd_argc == 1) cmd_args = text; text = COM_Parse(text); if (!text) return; if (cmd_argc < MAX_ARGS) { cmd_argv[cmd_argc] = Z_Malloc(strlen(com_token)+1); strcpy(cmd_argv[cmd_argc], com_token); cmd_argc++; } } } void Cmd_AddCommand(char *cmd_name, xcommand_t function) { cmd_function_t *cmd; if (host_initialized) // because hunk allocation would get stomped Sys_Error("Cmd_AddCommand after host_initialized"); // fail if the command is a variable name if (Cvar_VariableString(cmd_name)[0]) { Con_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name); return; } // fail if the command already exists for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strcmp(cmd_name, cmd->name)) { Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name); return; } } cmd = Hunk_Alloc(sizeof(cmd_function_t)); cmd->name = cmd_name; cmd->function = function; cmd->next = cmd_functions; cmd_functions = cmd; } bool Cmd_Exists(char *cmd_name) { cmd_function_t *cmd; for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strcmp(cmd_name, cmd->name)) return true; } return false; } char * Cmd_CompleteCommand(char *partial) { cmd_function_t *cmd; int len; len = strlen(partial); if (!len) return NULL; // check functions for (cmd = cmd_functions; cmd; cmd = cmd->next) if (!strncmp(partial, cmd->name, len)) return cmd->name; return NULL; } void Cmd_ExecuteString(char *text, cmd_source_t src) { cmd_function_t *cmd; cmdalias_t *a; cmd_source = src; Cmd_TokenizeString (text); // execute the command line if (!Cmd_Argc()) return; // no tokens // check functions for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strcasecmp(cmd_argv[0], cmd->name)) { cmd->function(); return; } } // check alias for (a = cmd_alias; a; a = a->next) { if (!strcasecmp(cmd_argv[0], a->name)) { Cbuf_InsertText(a->value); return; } } // check cvars if (!Cvar_Command()) Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0)); } void Cmd_ForwardToServer(void) { if (cls.state != ca_connected) { Con_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } if (cls.demoplayback) return; // not really connected MSG_WriteByte(&cls.message, clc_stringcmd); if (strcasecmp(Cmd_Argv(0), "cmd") != 0) { SZ_Print(&cls.message, Cmd_Argv(0)); SZ_Print(&cls.message, " "); } SZ_Print(&cls.message, Cmd_Argc() > 1 ? Cmd_Args() : "\n"); } int Cmd_CheckParm(char *parm) { int i; if (!parm) Sys_Error("Cmd_CheckParm: NULL"); for (i = 1; i < Cmd_Argc(); i++) { if (!strcasecmp(parm, Cmd_Argv(i))) return i; } return 0; }