Home | History | Annotate | Download | only in WinQuake
      1 /*
      2 Copyright (C) 1996-1997 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // cmd.c -- Quake script command processing module
     21 
     22 #include "quakedef.h"
     23 
     24 void Cmd_ForwardToServer (void);
     25 
     26 #define    MAX_ALIAS_NAME    32
     27 
     28 typedef struct cmdalias_s
     29 {
     30     struct cmdalias_s    *next;
     31     char    name[MAX_ALIAS_NAME];
     32     char    *value;
     33 } cmdalias_t;
     34 
     35 cmdalias_t    *cmd_alias;
     36 
     37 int trashtest;
     38 int *trashspot;
     39 
     40 qboolean    cmd_wait;
     41 
     42 //=============================================================================
     43 
     44 /*
     45 ============
     46 Cmd_Wait_f
     47 
     48 Causes execution of the remainder of the command buffer to be delayed until
     49 next frame.  This allows commands like:
     50 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
     51 ============
     52 */
     53 void Cmd_Wait_f (void)
     54 {
     55     cmd_wait = true;
     56 }
     57 
     58 /*
     59 =============================================================================
     60 
     61                         COMMAND BUFFER
     62 
     63 =============================================================================
     64 */
     65 
     66 sizebuf_t    cmd_text;
     67 
     68 /*
     69 ============
     70 Cbuf_Init
     71 ============
     72 */
     73 void Cbuf_Init (void)
     74 {
     75     SZ_Alloc (&cmd_text, 8192);        // space for commands and script files
     76 }
     77 
     78 
     79 /*
     80 ============
     81 Cbuf_AddText
     82 
     83 Adds command text at the end of the buffer
     84 ============
     85 */
     86 void Cbuf_AddText (const char *text)
     87 {
     88     int        l;
     89 
     90     l = Q_strlen (text);
     91 
     92     if (cmd_text.cursize + l >= cmd_text.maxsize)
     93     {
     94         Con_Printf ("Cbuf_AddText: overflow\n");
     95         return;
     96     }
     97 
     98     SZ_Write (&cmd_text, text, Q_strlen (text));
     99 }
    100 
    101 
    102 /*
    103 ============
    104 Cbuf_InsertText
    105 
    106 Adds command text immediately after the current command
    107 Adds a \n to the text
    108 FIXME: actually change the command buffer to do less copying
    109 ============
    110 */
    111 void Cbuf_InsertText (const char *text)
    112 {
    113     char    *temp;
    114     int        templen;
    115 
    116 // copy off any commands still remaining in the exec buffer
    117     templen = cmd_text.cursize;
    118     if (templen)
    119     {
    120         temp = (char*) Z_Malloc (templen);
    121         Q_memcpy (temp, cmd_text.data, templen);
    122         SZ_Clear (&cmd_text);
    123     }
    124     else
    125         temp = NULL;    // shut up compiler
    126 
    127 // add the entire text of the file
    128     Cbuf_AddText (text);
    129 
    130 // add the copied off data
    131     if (templen)
    132     {
    133         SZ_Write (&cmd_text, temp, templen);
    134         Z_Free (temp);
    135     }
    136 }
    137 
    138 /*
    139 ============
    140 Cbuf_Execute
    141 ============
    142 */
    143 void Cbuf_Execute (void)
    144 {
    145     int        i;
    146     char    *text;
    147     char    line[1024];
    148     int        quotes;
    149 
    150     while (cmd_text.cursize)
    151     {
    152 // find a \n or ; line break
    153         text = (char *)cmd_text.data;
    154 
    155         quotes = 0;
    156         for (i=0 ; i< cmd_text.cursize ; i++)
    157         {
    158             if (text[i] == '"')
    159                 quotes++;
    160             if ( !(quotes&1) &&  text[i] == ';')
    161                 break;    // don't break if inside a quoted string
    162             if (text[i] == '\n' || text[i] == '\r')
    163                 break;
    164         }
    165 
    166 
    167         memcpy (line, text, i);
    168         line[i] = 0;
    169 
    170 // delete the text from the command buffer and move remaining commands down
    171 // this is necessary because commands (exec, alias) can insert data at the
    172 // beginning of the text buffer
    173 
    174         if (i == cmd_text.cursize)
    175             cmd_text.cursize = 0;
    176         else
    177         {
    178             i++;
    179             cmd_text.cursize -= i;
    180             Q_memcpy (text, text+i, cmd_text.cursize);
    181         }
    182 
    183 // execute the command line
    184         Cmd_ExecuteString (line, src_command);
    185 
    186         if (cmd_wait)
    187         {    // skip out while text still remains in buffer, leaving it
    188             // for next frame
    189             cmd_wait = false;
    190             break;
    191         }
    192     }
    193 }
    194 
    195 /*
    196 ==============================================================================
    197 
    198                         SCRIPT COMMANDS
    199 
    200 ==============================================================================
    201 */
    202 
    203 /*
    204 ===============
    205 Cmd_StuffCmds_f
    206 
    207 Adds command line parameters as script statements
    208 Commands lead with a +, and continue until a - or another +
    209 quake +prog jctest.qp +cmd amlev1
    210 quake -nosound +cmd amlev1
    211 ===============
    212 */
    213 void Cmd_StuffCmds_f (void)
    214 {
    215     int        i, j;
    216     int        s;
    217     char    *text, *build, c;
    218 
    219     if (Cmd_Argc () != 1)
    220     {
    221         Con_Printf ("stuffcmds : execute command line parameters\n");
    222         return;
    223     }
    224 
    225 // build the combined string to parse from
    226     s = 0;
    227     for (i=1 ; i<com_argc ; i++)
    228     {
    229         if (!com_argv[i])
    230             continue;        // NEXTSTEP nulls out -NXHost
    231         s += Q_strlen (com_argv[i]) + 1;
    232     }
    233     if (!s)
    234         return;
    235 
    236     text = (char*) Z_Malloc (s+1);
    237     text[0] = 0;
    238     for (i=1 ; i<com_argc ; i++)
    239     {
    240         if (!com_argv[i])
    241             continue;        // NEXTSTEP nulls out -NXHost
    242         Q_strcat (text,com_argv[i]);
    243         if (i != com_argc-1)
    244             Q_strcat (text, " ");
    245     }
    246 
    247 // pull out the commands
    248     build = (char*) Z_Malloc (s+1);
    249     build[0] = 0;
    250 
    251     for (i=0 ; i<s-1 ; i++)
    252     {
    253         if (text[i] == '+')
    254         {
    255             i++;
    256 
    257             for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
    258                 ;
    259 
    260             c = text[j];
    261             text[j] = 0;
    262 
    263             Q_strcat (build, text+i);
    264             Q_strcat (build, "\n");
    265             text[j] = c;
    266             i = j-1;
    267         }
    268     }
    269 
    270     if (build[0])
    271         Cbuf_InsertText (build);
    272 
    273     Z_Free (text);
    274     Z_Free (build);
    275 }
    276 
    277 
    278 /*
    279 ===============
    280 Cmd_Exec_f
    281 ===============
    282 */
    283 void Cmd_Exec_f (void)
    284 {
    285     char    *f;
    286     int        mark;
    287 
    288     if (Cmd_Argc () != 2)
    289     {
    290         Con_Printf ("exec <filename> : execute a script file\n");
    291         return;
    292     }
    293 
    294     mark = Hunk_LowMark ();
    295     f = (char *)COM_LoadHunkFile (Cmd_Argv(1));
    296     if (!f)
    297     {
    298         Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
    299         return;
    300     }
    301     Con_Printf ("execing %s\n",Cmd_Argv(1));
    302 
    303     Cbuf_InsertText (f);
    304     Hunk_FreeToLowMark (mark);
    305 }
    306 
    307 
    308 /*
    309 ===============
    310 Cmd_Echo_f
    311 
    312 Just prints the rest of the line to the console
    313 ===============
    314 */
    315 void Cmd_Echo_f (void)
    316 {
    317     int        i;
    318 
    319     for (i=1 ; i<Cmd_Argc() ; i++)
    320         Con_Printf ("%s ",Cmd_Argv(i));
    321     Con_Printf ("\n");
    322 }
    323 
    324 /*
    325 ===============
    326 Cmd_Alias_f
    327 
    328 Creates a new command that executes a command string (possibly ; seperated)
    329 ===============
    330 */
    331 
    332 char *CopyString (const char *in)
    333 {
    334     char    *out;
    335 
    336     out = (char*) Z_Malloc (strlen(in)+1);
    337     strcpy (out, in);
    338     return out;
    339 }
    340 
    341 void Cmd_Alias_f (void)
    342 {
    343     cmdalias_t    *a;
    344     char        cmd[1024];
    345     int            i, c;
    346     const char        *s;
    347 
    348     if (Cmd_Argc() == 1)
    349     {
    350         Con_Printf ("Current alias commands:\n");
    351         for (a = cmd_alias ; a ; a=a->next)
    352             Con_Printf ("%s : %s\n", a->name, a->value);
    353         return;
    354     }
    355 
    356     s = Cmd_Argv(1);
    357     if (strlen(s) >= MAX_ALIAS_NAME)
    358     {
    359         Con_Printf ("Alias name is too long\n");
    360         return;
    361     }
    362 
    363     // if the alias allready exists, reuse it
    364     for (a = cmd_alias ; a ; a=a->next)
    365     {
    366         if (!strcmp(s, a->name))
    367         {
    368             Z_Free (a->value);
    369             break;
    370         }
    371     }
    372 
    373     if (!a)
    374     {
    375         a = (cmdalias_t*) Z_Malloc (sizeof(cmdalias_t));
    376         a->next = cmd_alias;
    377         cmd_alias = a;
    378     }
    379     strcpy (a->name, s);
    380 
    381 // copy the rest of the command line
    382     cmd[0] = 0;        // start out with a null string
    383     c = Cmd_Argc();
    384     for (i=2 ; i< c ; i++)
    385     {
    386         strcat (cmd, Cmd_Argv(i));
    387         if (i != c)
    388             strcat (cmd, " ");
    389     }
    390     strcat (cmd, "\n");
    391 
    392     a->value = CopyString (cmd);
    393 }
    394 
    395 /*
    396 =============================================================================
    397 
    398                     COMMAND EXECUTION
    399 
    400 =============================================================================
    401 */
    402 
    403 typedef struct cmd_function_s
    404 {
    405     struct cmd_function_s    *next;
    406     char                    *name;
    407     xcommand_t                function;
    408 } cmd_function_t;
    409 
    410 
    411 #define    MAX_ARGS        80
    412 
    413 static    int            cmd_argc;
    414 static    char        *cmd_argv[MAX_ARGS];
    415 static    char        *cmd_null_string = (char*) "";
    416 static    char        *cmd_args = NULL;
    417 
    418 cmd_source_t    cmd_source;
    419 
    420 
    421 static    cmd_function_t    *cmd_functions;        // possible commands to execute
    422 
    423 /*
    424 ============
    425 Cmd_Init
    426 ============
    427 */
    428 void Cmd_Init (void)
    429 {
    430 //
    431 // register our commands
    432 //
    433     Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
    434     Cmd_AddCommand ("exec",Cmd_Exec_f);
    435     Cmd_AddCommand ("echo",Cmd_Echo_f);
    436     Cmd_AddCommand ("alias",Cmd_Alias_f);
    437     Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
    438     Cmd_AddCommand ("wait", Cmd_Wait_f);
    439 }
    440 
    441 /*
    442 ============
    443 Cmd_Argc
    444 ============
    445 */
    446 int        Cmd_Argc (void)
    447 {
    448     return cmd_argc;
    449 }
    450 
    451 /*
    452 ============
    453 Cmd_Argv
    454 ============
    455 */
    456 char    *Cmd_Argv (int arg)
    457 {
    458     if ( arg >= cmd_argc )
    459         return cmd_null_string;
    460     return cmd_argv[arg];
    461 }
    462 
    463 /*
    464 ============
    465 Cmd_Args
    466 ============
    467 */
    468 char        *Cmd_Args (void)
    469 {
    470     return cmd_args;
    471 }
    472 
    473 
    474 /*
    475 ============
    476 Cmd_TokenizeString
    477 
    478 Parses the given string into command line tokens.
    479 ============
    480 */
    481 void Cmd_TokenizeString (char *text)
    482 {
    483     int        i;
    484 
    485 // clear the args from the last string
    486     for (i=0 ; i<cmd_argc ; i++)
    487         Z_Free (cmd_argv[i]);
    488 
    489     cmd_argc = 0;
    490     cmd_args = NULL;
    491 
    492     while (1)
    493     {
    494 // skip whitespace up to a /n
    495         while (*text && *text <= ' ' && *text != '\n')
    496         {
    497             text++;
    498         }
    499 
    500         if (*text == '\n')
    501         {    // a newline seperates commands in the buffer
    502             text++;
    503             break;
    504         }
    505 
    506         if (!*text)
    507             return;
    508 
    509         if (cmd_argc == 1)
    510              cmd_args = text;
    511 
    512         text = COM_Parse (text);
    513         if (!text)
    514             return;
    515 
    516         if (cmd_argc < MAX_ARGS)
    517         {
    518             cmd_argv[cmd_argc] = (char*) Z_Malloc (Q_strlen(com_token)+1);
    519             Q_strcpy (cmd_argv[cmd_argc], com_token);
    520             cmd_argc++;
    521         }
    522     }
    523 
    524 }
    525 
    526 
    527 /*
    528 ============
    529 Cmd_AddCommand
    530 ============
    531 */
    532 void    Cmd_AddCommand (const char *cmd_name, xcommand_t function)
    533 {
    534     cmd_function_t    *cmd;
    535 
    536     if (host_initialized)    // because hunk allocation would get stomped
    537         Sys_Error ("Cmd_AddCommand after host_initialized");
    538 
    539 // fail if the command is a variable name
    540     if (Cvar_VariableString(cmd_name)[0])
    541     {
    542         Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
    543         return;
    544     }
    545 
    546 // fail if the command already exists
    547     for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
    548     {
    549         if (!Q_strcmp (cmd_name, cmd->name))
    550         {
    551             Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
    552             return;
    553         }
    554     }
    555 
    556     cmd = (cmd_function_t*) Hunk_Alloc (sizeof(cmd_function_t));
    557     cmd->name = (char*) cmd_name;
    558     cmd->function = function;
    559     cmd->next = cmd_functions;
    560     cmd_functions = cmd;
    561 }
    562 
    563 /*
    564 ============
    565 Cmd_Exists
    566 ============
    567 */
    568 qboolean    Cmd_Exists (const char *cmd_name)
    569 {
    570     cmd_function_t    *cmd;
    571 
    572     for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
    573     {
    574         if (!Q_strcmp (cmd_name,cmd->name))
    575             return true;
    576     }
    577 
    578     return false;
    579 }
    580 
    581 
    582 
    583 /*
    584 ============
    585 Cmd_CompleteCommand
    586 ============
    587 */
    588 const char *Cmd_CompleteCommand (const char *partial)
    589 {
    590     cmd_function_t    *cmd;
    591     int                len;
    592 
    593     len = Q_strlen(partial);
    594 
    595     if (!len)
    596         return NULL;
    597 
    598 // check functions
    599     for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
    600         if (!Q_strncmp (partial,cmd->name, len))
    601             return cmd->name;
    602 
    603     return NULL;
    604 }
    605 
    606 /*
    607 ============
    608 Cmd_ExecuteString
    609 
    610 A complete command line has been parsed, so try to execute it
    611 FIXME: lookupnoadd the token to speed search?
    612 ============
    613 */
    614 void    Cmd_ExecuteString (char *text, cmd_source_t src)
    615 {
    616     cmd_function_t    *cmd;
    617     cmdalias_t        *a;
    618 
    619     cmd_source = src;
    620     Cmd_TokenizeString (text);
    621 
    622 // execute the command line
    623     if (!Cmd_Argc())
    624         return;        // no tokens
    625 
    626 // check functions
    627     for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
    628     {
    629         if (!Q_strcasecmp (cmd_argv[0],cmd->name))
    630         {
    631             cmd->function ();
    632             return;
    633         }
    634     }
    635 
    636 // check alias
    637     for (a=cmd_alias ; a ; a=a->next)
    638     {
    639         if (!Q_strcasecmp (cmd_argv[0], a->name))
    640         {
    641             Cbuf_InsertText (a->value);
    642             return;
    643         }
    644     }
    645 
    646 // check cvars
    647     if (!Cvar_Command ())
    648         Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
    649 
    650 }
    651 
    652 void    Cmd_ExecuteString2 (const char *text, cmd_source_t src)
    653 {
    654     char buf[100];
    655     Q_strncpy(buf, text, sizeof(buf));
    656     buf[sizeof(buf)-1] = 0;
    657     Cmd_ExecuteString(buf, src);
    658 }
    659 
    660 /*
    661 ===================
    662 Cmd_ForwardToServer
    663 
    664 Sends the entire command line over to the server
    665 ===================
    666 */
    667 void Cmd_ForwardToServer (void)
    668 {
    669     if (cls.state != ca_connected)
    670     {
    671         Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
    672         return;
    673     }
    674 
    675     if (cls.demoplayback)
    676         return;        // not really connected
    677 
    678     MSG_WriteByte (&cls.message, clc_stringcmd);
    679     if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
    680     {
    681         SZ_Print (&cls.message, Cmd_Argv(0));
    682         SZ_Print (&cls.message, " ");
    683     }
    684     if (Cmd_Argc() > 1)
    685         SZ_Print (&cls.message, Cmd_Args());
    686     else
    687         SZ_Print (&cls.message, "\n");
    688 }
    689 
    690 
    691 /*
    692 ================
    693 Cmd_CheckParm
    694 
    695 Returns the position (1 to argc-1) in the command's argument list
    696 where the given parameter apears, or 0 if not present
    697 ================
    698 */
    699 
    700 int Cmd_CheckParm (const char *parm)
    701 {
    702     int i;
    703 
    704     if (!parm)
    705         Sys_Error ("Cmd_CheckParm: NULL");
    706 
    707     for (i = 1; i < Cmd_Argc (); i++)
    708         if (! Q_strcasecmp (parm, Cmd_Argv (i)))
    709             return i;
    710 
    711     return 0;
    712 }
    713