/* Copyright (C) 1996-1997 Id Software, Inc. This program 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. This program 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // 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; qboolean cmd_wait; //============================================================================= /* ============ Cmd_Wait_f 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; } /* ============================================================================= COMMAND BUFFER ============================================================================= */ sizebuf_t cmd_text; /* ============ Cbuf_Init ============ */ void Cbuf_Init (void) { // 2000-01-09 CmdList command by Maddes start // SZ_Alloc (&cmd_text, 8192); // space for commands and script files SZ_Alloc (&cmd_text, 32*1024); // space for commands and script files // 2000-01-09 CmdList command by Maddes end } /* ============ Cbuf_AddText Adds command text at the end of the buffer ============ */ 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)); } /* ============ Cbuf_InsertText Adds command text immediately after the current command Adds a \n to the text FIXME: actually change the command buffer to do less copying ============ */ 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 (mainzone, templen); // 2001-09-20 Enhanced zone handling by Maddes Q_memcpy (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 (mainzone, temp); // 2001-09-20 Enhanced zone handling by Maddes } } /* ============ Cbuf_Execute ============ */ 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; } memcpy (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; Q_memcpy (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; } } } /* ============================================================================== SCRIPT COMMANDS ============================================================================== */ /* =============== Cmd_StuffCmds_f 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 : execute a script file\n"); return; } mark = Hunk_LowMark (); // 2001-09-12 Returning information about loaded file by Maddes start /* f = (char *)COM_LoadHunkFile (Cmd_Argv(1)); if (!f) */ fileinfo = COM_LoadHunkFile (Cmd_Argv(1)); if (!fileinfo) // 2001-09-12 Returning information about loaded file by Maddes end { Con_Printf ("couldn't exec %s\n",Cmd_Argv(1)); return; } f = (char *)fileinfo->data; // 2001-09-12 Returning information about loaded file by Maddes Con_Printf ("execing %s\n",Cmd_Argv(1)); Cbuf_InsertText (f); Hunk_FreeToLowMark (mark); } /* =============== Cmd_Echo_f Just prints the rest of the line to the console =============== */ void Cmd_Echo_f (void) { int i; for (i=1 ; inext) 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 already exists, reuse it for (a = cmd_alias ; a ; a=a->next) { if (!Q_strcasecmp(s, a->name)) { Z_Free (mainzone, a->value); // 2001-09-20 Enhanced zone handling by Maddes break; } } if (!a) { a = Z_Malloc (mainzone, sizeof(cmdalias_t)); // 2001-09-20 Enhanced zone handling by Maddes 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); } /* ============================================================================= COMMAND EXECUTION ============================================================================= */ 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 // 2000-01-09 CmdList command by Maddes start /* ======== Cmd_List ======== */ void Cmd_List_f (void) { cmd_function_t *cmd; int i; char *partial; int len; int count; if (Cmd_Argc() > 1) { partial = Cmd_Argv (1); len = strlen(partial); } else { partial = NULL; len = 0; } count=0; for (cmd=cmd_functions, i=0 ; cmd ; cmd=cmd->next, i++) { if (partial && Q_strncasecmp (partial, cmd->name, len)) { continue; } Con_Printf ("%s\n", cmd->name); count++; } Con_Printf ("------------\n"); if (partial) { Con_Printf ("%i beginning with \"%s\" out of ", count, partial); } Con_Printf ("%i commands\n", i); } // 2000-01-09 CmdList command by Maddes end /* ============ Cmd_Init ============ */ void Cmd_Init (void) { // // register our commands // 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); Cmd_AddCommand ("cmdlist", Cmd_List_f); // 2000-01-09 CmdList command by Maddes } /* ============ Cmd_Argc ============ */ int Cmd_Argc (void) { return cmd_argc; } /* ============ Cmd_Argv ============ */ char *Cmd_Argv (int arg) { if ( (unsigned)arg >= cmd_argc ) return cmd_null_string; return cmd_argv[arg]; } /* ============ Cmd_Args ============ */ char *Cmd_Args (void) { return cmd_args; } /* ============ Cmd_TokenizeString Parses the given string into command line tokens. ============ */ void Cmd_TokenizeString (char *text) { int i; // clear the args from the last string for (i=0 ; inext) { if (!Q_strcasecmp (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; } /* ============ Cmd_Exists ============ */ qboolean Cmd_Exists (char *cmd_name) { cmd_function_t *cmd; for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (!Q_strcasecmp (cmd_name,cmd->name)) // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes return true; } return false; } /* ============ Cmd_CompleteCommand ============ */ 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 (!Q_strncasecmp (partial,cmd->name, len)) return cmd->name; // 2001-12-15 Enhanced console command completion by Fett/Maddes start /* // 2001-12-15 Tab Completion of Alias Commands by Shelob start // check aliases cmdalias_t *a; for (a=cmd_alias ; a ; a=a->next) if (!Q_strncasecmp (partial, a->name, len)) return a->name; // 2001-12-15 Tab Completion of Alias Commands by Shelob end */ // 2001-12-15 Enhanced console command completion by Fett/Maddes end return NULL; } // 2001-12-15 Enhanced console command completion by Fett/Maddes start /* ============ Pad_CompletePrint Formats a string for completion lists: 19 chars of string and a space If string is smaller than 19 chars it is padded with spaces, if it is bigger then the 19th char is replaced with an asterik (*) ============ */ char *Pad_CompletePrint(char *input) { static char sout[21]; int len; len = strlen(input); if (len > 19) { strncpy(sout, input, 18); sout[18] = '*'; } else { strcpy(sout, input); // Pad with spaces for (; len<19;) { sout[len++] = ' '; sout[len] = 0; } } sout[19] = ' '; sout[20] = 0; return sout; } /* ============ Cmd_CompleteCountPossible ============ */ int Cmd_CompleteCountPossible (char *partial) { cmd_function_t *cmd; int len; int h; h=0; len = strlen(partial); if (!len) return 0; // Loop through the command list and count all partial matches for (cmd=cmd_functions ; cmd ; cmd=cmd->next) if (!Q_strncasecmp (partial,cmd->name, len)) h++; return h; } /* ============ Cmd_CompletePrintPossible ============ */ void Cmd_CompletePrintPossible (char *partial) { cmd_function_t *cmd; int len; int maxcnt, cnt; int con_linewidth; char *sout; len = strlen(partial); // Determine the width of the console - 1 con_linewidth = (vid.width >> 3) - 3; maxcnt = con_linewidth / 20; // entries per line cnt = 0; // Loop through the command list and print all matches for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (!Q_strncasecmp (partial,cmd->name, len)) { sout = Pad_CompletePrint(cmd->name); Con_Printf ("%s", sout); cnt++; if (cnt >= maxcnt) { cnt = 0; Con_Printf ("\n"); } } } if (cnt) { Con_Printf ("\n"); } Con_Printf ("\n"); } /* ============ Cmd_CompleteAlias ============ */ char *Cmd_CompleteAlias (char *partial) { cmdalias_t *alias; int len; len = strlen(partial); if (!len) return NULL; // check aliases for (alias=cmd_alias ; alias ; alias=alias->next) if (!Q_strncasecmp (partial,alias->name, len)) return alias->name; return NULL; } /* ============ Cmd_CompleteAliasCountPossible ============ */ int Cmd_CompleteAliasCountPossible (char *partial) { cmdalias_t *alias; int len; int h; h=0; len = strlen(partial); if (!len) return 0; // Loop through the command list and count all partial matches for (alias=cmd_alias ; alias ; alias=alias->next) if (!Q_strncasecmp (partial,alias->name, len)) h++; return h; } /* ============ Cmd_CompleteAliasPrintPossible ============ */ void Cmd_CompleteAliasPrintPossible (char *partial) { cmdalias_t *alias; int len; int maxcnt, cnt; int con_linewidth; char *sout; len = strlen(partial); // Determine the width of the console -1 con_linewidth = (vid.width >> 3) - 3; maxcnt = con_linewidth / 20; // entries per line cnt = 0; // Loop through the alias list and print all matches for (alias=cmd_alias ; alias ; alias=alias->next) { if (!Q_strncasecmp (partial,alias->name, len)) { sout = Pad_CompletePrint(alias->name); Con_Printf ("%s", sout); cnt++; if (cnt >= maxcnt) { cnt = 0; Con_Printf ("\n"); } } } if (cnt) { Con_Printf ("\n"); } Con_Printf ("\n"); } // 2001-12-15 Enhanced console command completion by Fett/Maddes end /* ============ Cmd_ExecuteString A complete command line has been parsed, so try to execute it FIXME: lookupnoadd the token to speed search? ============ */ 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 (!Q_strcasecmp (cmd_argv[0],cmd->name)) { cmd->function (); return; } } // check alias for (a=cmd_alias ; a ; a=a->next) { if (!Q_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)); } /* =================== Cmd_ForwardToServer Sends the entire command line over to the server =================== */ 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 (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0) { SZ_Print (&cls.message, Cmd_Argv(0)); SZ_Print (&cls.message, " "); } if (Cmd_Argc() > 1) SZ_Print (&cls.message, Cmd_Args()); else SZ_Print (&cls.message, "\n"); } /* ================ Cmd_CheckParm Returns the position (1 to argc-1) in the command's argument list where the given parameter apears, or 0 if not present ================ */ int Cmd_CheckParm (char *parm) { int i; if (!parm) Sys_Error ("Cmd_CheckParm: NULL"); for (i = 1; i < Cmd_Argc (); i++) if (! Q_strcasecmp (parm, Cmd_Argv (i))) return i; return 0; }