home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Amiga 14 / MA_Cover_14.iso / source / c / q1source_amy / qw / client / cmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-12-21  |  13.8 KB  |  749 lines

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