Annotation of quakeworld/client/cmd.c, revision 1.1.1.1

1.1       root        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: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.