]> icculus.org git repositories - divverent/darkplaces.git/blob - cmd.c
Lots of speedups and cleanups in client code, dynamically allocated cl_entities ...
[divverent/darkplaces.git] / cmd.c
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 #define MAX_ALIAS_NAME  32
25
26 typedef struct cmdalias_s
27 {
28         struct cmdalias_s       *next;
29         char    name[MAX_ALIAS_NAME];
30         char    *value;
31 } cmdalias_t;
32
33 static cmdalias_t *cmd_alias;
34
35 static qboolean cmd_wait;
36
37 static mempool_t *cmd_mempool;
38
39 //=============================================================================
40
41 /*
42 ============
43 Cmd_Wait_f
44
45 Causes execution of the remainder of the command buffer to be delayed until
46 next frame.  This allows commands like:
47 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
48 ============
49 */
50 static void Cmd_Wait_f (void)
51 {
52         cmd_wait = true;
53 }
54
55 /*
56 =============================================================================
57
58                                                 COMMAND BUFFER
59
60 =============================================================================
61 */
62
63 static sizebuf_t        cmd_text;
64
65 /*
66 ============
67 Cbuf_Init
68 ============
69 */
70 void Cbuf_Init (void)
71 {
72         // LordHavoc: inreased this from 8192 to 32768
73         SZ_Alloc (&cmd_text, 32768, "command buffer"); // space for commands and script files
74 }
75
76
77 /*
78 ============
79 Cbuf_AddText
80
81 Adds command text at the end of the buffer
82 ============
83 */
84 void Cbuf_AddText (char *text)
85 {
86         int             l;
87
88         l = strlen (text);
89
90         if (cmd_text.cursize + l >= cmd_text.maxsize)
91         {
92                 Con_Printf ("Cbuf_AddText: overflow\n");
93                 return;
94         }
95
96         SZ_Write (&cmd_text, text, strlen (text));
97 }
98
99
100 /*
101 ============
102 Cbuf_InsertText
103
104 Adds command text immediately after the current command
105 Adds a \n to the text
106 FIXME: actually change the command buffer to do less copying
107 ============
108 */
109 void Cbuf_InsertText (char *text)
110 {
111         char    *temp;
112         int             templen;
113
114 // copy off any commands still remaining in the exec buffer
115         templen = cmd_text.cursize;
116         if (templen)
117         {
118                 temp = Z_Malloc (templen);
119                 memcpy (temp, cmd_text.data, templen);
120                 SZ_Clear (&cmd_text);
121         }
122         else
123                 temp = NULL;    // shut up compiler
124
125 // add the entire text of the file
126         Cbuf_AddText (text);
127
128 // add the copied off data
129         if (templen)
130         {
131                 SZ_Write (&cmd_text, temp, templen);
132                 Z_Free (temp);
133         }
134 }
135
136 /*
137 ============
138 Cbuf_Execute
139 ============
140 */
141 void Cbuf_Execute (void)
142 {
143         int             i;
144         char    *text;
145         char    line[1024];
146         int             quotes;
147
148         while (cmd_text.cursize)
149         {
150 // find a \n or ; line break
151                 text = (char *)cmd_text.data;
152
153                 quotes = 0;
154                 for (i=0 ; i< cmd_text.cursize ; i++)
155                 {
156                         if (text[i] == '"')
157                                 quotes++;
158                         if ( !(quotes&1) &&  text[i] == ';')
159                                 break;  // don't break if inside a quoted string
160                         if (text[i] == '\n')
161                                 break;
162                 }
163
164                 memcpy (line, text, i);
165                 line[i] = 0;
166
167 // delete the text from the command buffer and move remaining commands down
168 // this is necessary because commands (exec, alias) can insert data at the
169 // beginning of the text buffer
170
171                 if (i == cmd_text.cursize)
172                         cmd_text.cursize = 0;
173                 else
174                 {
175                         i++;
176                         cmd_text.cursize -= i;
177                         memcpy (text, text+i, cmd_text.cursize);
178                 }
179
180 // execute the command line
181                 Cmd_ExecuteString (line, src_command);
182
183                 if (cmd_wait)
184                 {       // skip out while text still remains in buffer, leaving it
185                         // for next frame
186                         cmd_wait = false;
187                         break;
188                 }
189         }
190 }
191
192 /*
193 ==============================================================================
194
195                                                 SCRIPT COMMANDS
196
197 ==============================================================================
198 */
199
200 /*
201 ===============
202 Cmd_StuffCmds_f
203
204 Adds command line parameters as script statements
205 Commands lead with a +, and continue until a - or another +
206 quake +prog jctest.qp +cmd amlev1
207 quake -nosound +cmd amlev1
208 ===============
209 */
210 void Cmd_StuffCmds_f (void)
211 {
212         int             i, j;
213         int             s;
214         char    *text, *build, c;
215
216         if (Cmd_Argc () != 1)
217         {
218                 Con_Printf ("stuffcmds : execute command line parameters\n");
219                 return;
220         }
221
222 // build the combined string to parse from
223         s = 0;
224         for (i=1 ; i<com_argc ; i++)
225         {
226                 if (!com_argv[i])
227                         continue;               // NEXTSTEP nulls out -NXHost
228                 s += strlen (com_argv[i]) + 1;
229         }
230         if (!s)
231                 return;
232
233         text = Z_Malloc (s+1);
234         text[0] = 0;
235         for (i=1 ; i<com_argc ; i++)
236         {
237                 if (!com_argv[i])
238                         continue;               // NEXTSTEP nulls out -NXHost
239                 strcat (text,com_argv[i]);
240                 if (i != com_argc-1)
241                         strcat (text, " ");
242         }
243
244 // pull out the commands
245         build = Z_Malloc (s+1);
246         build[0] = 0;
247
248         for (i=0 ; i<s-1 ; i++)
249         {
250                 if (text[i] == '+')
251                 {
252                         i++;
253
254                         for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
255                                 ;
256
257                         c = text[j];
258                         text[j] = 0;
259
260                         strcat (build, text+i);
261                         strcat (build, "\n");
262                         text[j] = c;
263                         i = j-1;
264                 }
265         }
266
267         if (build[0])
268                 Cbuf_InsertText (build);
269
270         Z_Free (text);
271         Z_Free (build);
272 }
273
274
275 /*
276 ===============
277 Cmd_Exec_f
278 ===============
279 */
280 static void Cmd_Exec_f (void)
281 {
282         char    *f;
283
284         if (Cmd_Argc () != 2)
285         {
286                 Con_Printf ("exec <filename> : execute a script file\n");
287                 return;
288         }
289
290         f = (char *)COM_LoadFile (Cmd_Argv(1), false);
291         if (!f)
292         {
293                 Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
294                 return;
295         }
296         Con_Printf ("execing %s\n",Cmd_Argv(1));
297
298         Cbuf_InsertText (f);
299         Mem_Free(f);
300 }
301
302
303 /*
304 ===============
305 Cmd_Echo_f
306
307 Just prints the rest of the line to the console
308 ===============
309 */
310 static 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 static 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 static 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 already 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 cmd_source_t cmd_source;
414
415
416 static cmd_function_t *cmd_functions;           // possible commands to execute
417
418 /*
419 ========
420 Cmd_List
421
422         CmdList Added by EvilTypeGuy eviltypeguy@qeradiant.com
423         Thanks to Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
424
425 ========
426 */
427 static void Cmd_List_f (void)
428 {
429         cmd_function_t  *cmd;
430         char                    *partial;
431         int                             len;
432         int                             count;
433
434         if (Cmd_Argc() > 1) {
435                 partial = Cmd_Argv (1);
436                 len = strlen(partial);
437         } else {
438                 partial = NULL;
439                 len = 0;
440         }
441
442         count = 0;
443         for (cmd = cmd_functions; cmd; cmd = cmd->next) {
444                 if (partial && strncmp(partial, cmd->name, len))
445                         continue;
446                 Con_Printf ("%s\n", cmd->name);
447                 count++;
448         }
449
450         Con_Printf ("%i Command%s", count, (count > 1) ? "s" : "");
451         if (partial)
452                 Con_Printf(" beginning with \"%s\"", partial);
453
454         Con_Printf ("\n\n");
455 }
456
457 /*
458 ============
459 Cmd_Init
460 ============
461 */
462 void Cmd_Init (void)
463 {
464         cmd_mempool = Mem_AllocPool("commands");
465
466 //
467 // register our commands
468 //
469         Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
470         Cmd_AddCommand ("exec",Cmd_Exec_f);
471         Cmd_AddCommand ("echo",Cmd_Echo_f);
472         Cmd_AddCommand ("alias",Cmd_Alias_f);
473         Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
474         Cmd_AddCommand ("wait", Cmd_Wait_f);
475         Cmd_AddCommand ("cmdlist", Cmd_List_f);         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
476         Cmd_AddCommand ("cvarlist", Cvar_List_f);       // 2000-01-09 CmdList, CvarList commands
477                                                                                                 // By Matthias "Maddes" Buecher
478 }
479
480 /*
481 ============
482 Cmd_Argc
483 ============
484 */
485 int             Cmd_Argc (void)
486 {
487         return cmd_argc;
488 }
489
490 /*
491 ============
492 Cmd_Argv
493 ============
494 */
495 char    *Cmd_Argv (int arg)
496 {
497         if (arg >= cmd_argc )
498                 return cmd_null_string;
499         return cmd_argv[arg];
500 }
501
502 /*
503 ============
504 Cmd_Args
505 ============
506 */
507 char    *Cmd_Args (void)
508 {
509         return cmd_args;
510 }
511
512
513 #define CMD_TOKENIZELENGTH 4096
514 char cmd_tokenizebuffer[CMD_TOKENIZELENGTH];
515
516 /*
517 ============
518 Cmd_TokenizeString
519
520 Parses the given string into command line tokens.
521 ============
522 */
523 static void Cmd_TokenizeString (char *text)
524 {
525         int l;
526         int pos;
527         pos = 0;
528
529         cmd_argc = 0;
530         cmd_args = NULL;
531
532         while (1)
533         {
534 // skip whitespace up to a /n
535                 while (*text && *text <= ' ' && *text != '\n')
536                 {
537                         text++;
538                 }
539
540                 if (*text == '\n')
541                 {       // a newline seperates commands in the buffer
542                         text++;
543                         break;
544                 }
545
546                 if (!*text)
547                         return;
548
549                 if (cmd_argc == 1)
550                          cmd_args = text;
551
552                 text = COM_Parse (text);
553                 if (!text)
554                         return;
555
556                 if (cmd_argc < MAX_ARGS)
557                 {
558                         l = strlen(com_token) + 1;
559                         if (pos + l > CMD_TOKENIZELENGTH)
560                                 Sys_Error("Cmd_TokenizeString: ran out of %i character buffer space for command arguements\n", CMD_TOKENIZELENGTH);
561                         cmd_argv[cmd_argc] = cmd_tokenizebuffer + pos;
562                         pos += l;
563                         strcpy (cmd_argv[cmd_argc], com_token);
564                         cmd_argc++;
565                 }
566         }
567
568 }
569
570
571 /*
572 ============
573 Cmd_AddCommand
574 ============
575 */
576 void    Cmd_AddCommand (char *cmd_name, xcommand_t function)
577 {
578         cmd_function_t  *cmd;
579
580 // fail if the command is a variable name
581         if (Cvar_VariableString(cmd_name)[0])
582         {
583                 Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
584                 return;
585         }
586
587 // fail if the command already exists
588         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
589         {
590                 if (!strcmp (cmd_name, cmd->name))
591                 {
592                         Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
593                         return;
594                 }
595         }
596
597         cmd = Mem_Alloc(cmd_mempool, sizeof(cmd_function_t));
598         cmd->name = cmd_name;
599         cmd->function = function;
600         cmd->next = cmd_functions;
601         cmd_functions = cmd;
602 }
603
604 /*
605 ============
606 Cmd_Exists
607 ============
608 */
609 qboolean        Cmd_Exists (char *cmd_name)
610 {
611         cmd_function_t  *cmd;
612
613         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
614                 if (!strcmp (cmd_name,cmd->name))
615                         return true;
616
617         return false;
618 }
619
620
621 /*
622 ============
623 Cmd_CompleteCommand
624 ============
625 */
626 char *Cmd_CompleteCommand (char *partial)
627 {
628         cmd_function_t  *cmd;
629         int                             len;
630
631         len = strlen(partial);
632
633         if (!len)
634                 return NULL;
635
636 // check functions
637         for (cmd = cmd_functions; cmd; cmd = cmd->next)
638                 if (!strncmp(partial, cmd->name, len))
639                         return cmd->name;
640
641         return NULL;
642 }
643
644 /*
645         Cmd_CompleteCountPossible
646
647         New function for tab-completion system
648         Added by EvilTypeGuy
649         Thanks to Fett erich@heintz.com
650         Thanks to taniwha
651
652 */
653 int Cmd_CompleteCountPossible (char *partial)
654 {
655         cmd_function_t  *cmd;
656         int                             len;
657         int                             h;
658
659         h = 0;
660         len = strlen(partial);
661
662         if (!len)
663                 return 0;
664
665         // Loop through the command list and count all partial matches
666         for (cmd = cmd_functions; cmd; cmd = cmd->next)
667                 if (!strncasecmp(partial, cmd->name, len))
668                         h++;
669
670         return h;
671 }
672
673 /*
674         Cmd_CompleteBuildList
675
676         New function for tab-completion system
677         Added by EvilTypeGuy
678         Thanks to Fett erich@heintz.com
679         Thanks to taniwha
680
681 */
682 char **Cmd_CompleteBuildList (char *partial)
683 {
684         cmd_function_t  *cmd;
685         int                             len = 0;
686         int                             bpos = 0;
687         int                             sizeofbuf = (Cmd_CompleteCountPossible (partial) + 1) * sizeof (char *);
688         char                    **buf;
689
690         len = strlen(partial);
691         buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (char *));
692         // Loop through the alias list and print all matches
693         for (cmd = cmd_functions; cmd; cmd = cmd->next)
694                 if (!strncasecmp(partial, cmd->name, len))
695                         buf[bpos++] = cmd->name;
696
697         buf[bpos] = NULL;
698         return buf;
699 }
700
701 /*
702         Cmd_CompleteAlias
703
704         New function for tab-completion system
705         Added by EvilTypeGuy
706         Thanks to Fett erich@heintz.com
707         Thanks to taniwha
708
709 */
710 char *Cmd_CompleteAlias (char * partial)
711 {
712         cmdalias_t      *alias;
713         int                     len;
714
715         len = strlen(partial);
716
717         if (!len)
718                 return NULL;
719
720         // Check functions
721         for (alias = cmd_alias; alias; alias = alias->next)
722                 if (!strncasecmp(partial, alias->name, len))
723                         return alias->name;
724
725         return NULL;
726 }
727
728 /*
729         Cmd_CompleteAliasCountPossible
730
731         New function for tab-completion system
732         Added by EvilTypeGuy
733         Thanks to Fett erich@heintz.com
734         Thanks to taniwha
735
736 */
737 int Cmd_CompleteAliasCountPossible (char *partial)
738 {
739         cmdalias_t      *alias;
740         int                     len;
741         int                     h;
742
743         h = 0;
744
745         len = strlen(partial);
746
747         if (!len)
748                 return 0;
749
750         // Loop through the command list and count all partial matches
751         for (alias = cmd_alias; alias; alias = alias->next)
752                 if (!strncasecmp(partial, alias->name, len))
753                         h++;
754
755         return h;
756 }
757
758 /*
759         Cmd_CompleteAliasBuildList
760
761         New function for tab-completion system
762         Added by EvilTypeGuy
763         Thanks to Fett erich@heintz.com
764         Thanks to taniwha
765
766 */
767 char **Cmd_CompleteAliasBuildList (char *partial)
768 {
769         cmdalias_t      *alias;
770         int                     len = 0;
771         int                     bpos = 0;
772         int                     sizeofbuf = (Cmd_CompleteAliasCountPossible (partial) + 1) * sizeof (char *);
773         char            **buf;
774
775         len = strlen(partial);
776         buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (char *));
777         // Loop through the alias list and print all matches
778         for (alias = cmd_alias; alias; alias = alias->next)
779                 if (!strncasecmp(partial, alias->name, len))
780                         buf[bpos++] = alias->name;
781
782         buf[bpos] = NULL;
783         return buf;
784 }
785
786 /*
787 ============
788 Cmd_ExecuteString
789
790 A complete command line has been parsed, so try to execute it
791 FIXME: lookupnoadd the token to speed search?
792 ============
793 */
794 void    Cmd_ExecuteString (char *text, cmd_source_t src)
795 {
796         cmd_function_t  *cmd;
797         cmdalias_t              *a;
798
799         cmd_source = src;
800         Cmd_TokenizeString (text);
801
802 // execute the command line
803         if (!Cmd_Argc())
804                 return;         // no tokens
805
806 // check functions
807         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
808         {
809                 if (!Q_strcasecmp (cmd_argv[0],cmd->name))
810                 {
811                         cmd->function ();
812                         return;
813                 }
814         }
815
816 // check alias
817         for (a=cmd_alias ; a ; a=a->next)
818         {
819                 if (!Q_strcasecmp (cmd_argv[0], a->name))
820                 {
821                         Cbuf_InsertText (a->value);
822                         return;
823                 }
824         }
825
826 // check cvars
827         if (!Cvar_Command ())
828                 Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
829 }
830
831
832 /*
833 ===================
834 Cmd_ForwardToServer
835
836 Sends the entire command line over to the server
837 ===================
838 */
839 void Cmd_ForwardToServer (void)
840 {
841         if (cls.state != ca_connected)
842         {
843                 Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
844                 return;
845         }
846
847         if (cls.demoplayback)
848                 return;         // not really connected
849
850         MSG_WriteByte (&cls.message, clc_stringcmd);
851         if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
852         {
853                 SZ_Print (&cls.message, Cmd_Argv(0));
854                 SZ_Print (&cls.message, " ");
855         }
856         if (Cmd_Argc() > 1)
857                 SZ_Print (&cls.message, Cmd_Args());
858         else
859                 SZ_Print (&cls.message, "\n");
860 }
861
862
863 /*
864 ================
865 Cmd_CheckParm
866
867 Returns the position (1 to argc-1) in the command's argument list
868 where the given parameter apears, or 0 if not present
869 ================
870 */
871
872 int Cmd_CheckParm (char *parm)
873 {
874         int i;
875
876         if (!parm)
877                 Sys_Error ("Cmd_CheckParm: NULL");
878
879         for (i = 1; i < Cmd_Argc (); i++)
880                 if (!Q_strcasecmp (parm, Cmd_Argv (i)))
881                         return i;
882
883         return 0;
884 }
885