]> icculus.org git repositories - divverent/darkplaces.git/blob - cmd.c
more thorough checking of 'this should never happen' cases
[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 /*
514 ============
515 Cmd_TokenizeString
516
517 Parses the given string into command line tokens.
518 ============
519 */
520 static void Cmd_TokenizeString (char *text)
521 {
522         int             i;
523
524 // clear the args from the last string
525         for (i=0 ; i<cmd_argc ; i++)
526                 Z_Free (cmd_argv[i]);
527
528         cmd_argc = 0;
529         cmd_args = NULL;
530
531         while (1)
532         {
533 // skip whitespace up to a /n
534                 while (*text && *text <= ' ' && *text != '\n')
535                 {
536                         text++;
537                 }
538
539                 if (*text == '\n')
540                 {       // a newline seperates commands in the buffer
541                         text++;
542                         break;
543                 }
544
545                 if (!*text)
546                         return;
547
548                 if (cmd_argc == 1)
549                          cmd_args = text;
550
551                 text = COM_Parse (text);
552                 if (!text)
553                         return;
554
555                 if (cmd_argc < MAX_ARGS)
556                 {
557                         cmd_argv[cmd_argc] = Z_Malloc (strlen(com_token)+1);
558                         strcpy (cmd_argv[cmd_argc], com_token);
559                         cmd_argc++;
560                 }
561         }
562
563 }
564
565
566 /*
567 ============
568 Cmd_AddCommand
569 ============
570 */
571 void    Cmd_AddCommand (char *cmd_name, xcommand_t function)
572 {
573         cmd_function_t  *cmd;
574
575 //      if (host_initialized)   // because hunk allocation would get stomped
576 //              Sys_Error ("Cmd_AddCommand after host_initialized");
577
578 // fail if the command is a variable name
579         if (Cvar_VariableString(cmd_name)[0])
580         {
581                 Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
582                 return;
583         }
584
585 // fail if the command already exists
586         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
587         {
588                 if (!strcmp (cmd_name, cmd->name))
589                 {
590                         Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
591                         return;
592                 }
593         }
594
595         cmd = Mem_Alloc(cmd_mempool, sizeof(cmd_function_t));
596         cmd->name = cmd_name;
597         cmd->function = function;
598         cmd->next = cmd_functions;
599         cmd_functions = cmd;
600 }
601
602 /*
603 ============
604 Cmd_Exists
605 ============
606 */
607 qboolean        Cmd_Exists (char *cmd_name)
608 {
609         cmd_function_t  *cmd;
610
611         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
612                 if (!strcmp (cmd_name,cmd->name))
613                         return true;
614
615         return false;
616 }
617
618
619
620 /*
621 ============
622 Cmd_CompleteCommand
623 ============
624 */
625 char *Cmd_CompleteCommand (char *partial)
626 {
627         cmd_function_t  *cmd;
628         int                             len;
629
630         len = strlen(partial);
631
632         if (!len)
633                 return NULL;
634
635 // check functions
636         for (cmd = cmd_functions; cmd; cmd = cmd->next)
637                 if (!strncmp(partial, cmd->name, len))
638                         return cmd->name;
639
640         return NULL;
641 }
642
643 /*
644         Cmd_CompleteCountPossible
645
646         New function for tab-completion system
647         Added by EvilTypeGuy
648         Thanks to Fett erich@heintz.com
649         Thanks to taniwha
650
651 */
652 int Cmd_CompleteCountPossible (char *partial)
653 {
654         cmd_function_t  *cmd;
655         int                             len;
656         int                             h;
657
658         h = 0;
659         len = strlen(partial);
660
661         if (!len)
662                 return 0;
663
664         // Loop through the command list and count all partial matches
665         for (cmd = cmd_functions; cmd; cmd = cmd->next)
666                 if (!strncasecmp(partial, cmd->name, len))
667                         h++;
668
669         return h;
670 }
671
672 /*
673         Cmd_CompleteBuildList
674
675         New function for tab-completion system
676         Added by EvilTypeGuy
677         Thanks to Fett erich@heintz.com
678         Thanks to taniwha
679
680 */
681 char **Cmd_CompleteBuildList (char *partial)
682 {
683         cmd_function_t  *cmd;
684         int                             len = 0;
685         int                             bpos = 0;
686         int                             sizeofbuf = (Cmd_CompleteCountPossible (partial) + 1) * sizeof (char *);
687         char                    **buf;
688
689         len = strlen(partial);
690         buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (char *));
691         // Loop through the alias list and print all matches
692         for (cmd = cmd_functions; cmd; cmd = cmd->next)
693                 if (!strncasecmp(partial, cmd->name, len))
694                         buf[bpos++] = cmd->name;
695
696         buf[bpos] = NULL;
697         return buf;
698 }
699
700 /*
701         Cmd_CompleteAlias
702
703         New function for tab-completion system
704         Added by EvilTypeGuy
705         Thanks to Fett erich@heintz.com
706         Thanks to taniwha
707
708 */
709 char *Cmd_CompleteAlias (char * partial)
710 {
711         cmdalias_t      *alias;
712         int                     len;
713
714         len = strlen(partial);
715
716         if (!len)
717                 return NULL;
718
719         // Check functions
720         for (alias = cmd_alias; alias; alias = alias->next)
721                 if (!strncasecmp(partial, alias->name, len))
722                         return alias->name;
723
724         return NULL;
725 }
726
727 /*
728         Cmd_CompleteAliasCountPossible
729
730         New function for tab-completion system
731         Added by EvilTypeGuy
732         Thanks to Fett erich@heintz.com
733         Thanks to taniwha
734
735 */
736 int Cmd_CompleteAliasCountPossible (char *partial)
737 {
738         cmdalias_t      *alias;
739         int                     len;
740         int                     h;
741
742         h = 0;
743
744         len = strlen(partial);
745
746         if (!len)
747                 return 0;
748
749         // Loop through the command list and count all partial matches
750         for (alias = cmd_alias; alias; alias = alias->next)
751                 if (!strncasecmp(partial, alias->name, len))
752                         h++;
753
754         return h;
755 }
756
757 /*
758         Cmd_CompleteAliasBuildList
759
760         New function for tab-completion system
761         Added by EvilTypeGuy
762         Thanks to Fett erich@heintz.com
763         Thanks to taniwha
764
765 */
766 char **Cmd_CompleteAliasBuildList (char *partial)
767 {
768         cmdalias_t      *alias;
769         int                     len = 0;
770         int                     bpos = 0;
771         int                     sizeofbuf = (Cmd_CompleteAliasCountPossible (partial) + 1) * sizeof (char *);
772         char            **buf;
773
774         len = strlen(partial);
775         buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (char *));
776         // Loop through the alias list and print all matches
777         for (alias = cmd_alias; alias; alias = alias->next)
778                 if (!strncasecmp(partial, alias->name, len))
779                         buf[bpos++] = alias->name;
780
781         buf[bpos] = NULL;
782         return buf;
783 }
784
785 /*
786 ============
787 Cmd_ExecuteString
788
789 A complete command line has been parsed, so try to execute it
790 FIXME: lookupnoadd the token to speed search?
791 ============
792 */
793 void    Cmd_ExecuteString (char *text, cmd_source_t src)
794 {
795         cmd_function_t  *cmd;
796         cmdalias_t              *a;
797
798         cmd_source = src;
799         Cmd_TokenizeString (text);
800
801 // execute the command line
802         if (!Cmd_Argc())
803                 return;         // no tokens
804
805 // check functions
806         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
807         {
808                 if (!Q_strcasecmp (cmd_argv[0],cmd->name))
809                 {
810                         cmd->function ();
811                         return;
812                 }
813         }
814
815 // check alias
816         for (a=cmd_alias ; a ; a=a->next)
817         {
818                 if (!Q_strcasecmp (cmd_argv[0], a->name))
819                 {
820                         Cbuf_InsertText (a->value);
821                         return;
822                 }
823         }
824
825 // check cvars
826         if (!Cvar_Command ())
827                 Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
828 }
829
830
831 /*
832 ===================
833 Cmd_ForwardToServer
834
835 Sends the entire command line over to the server
836 ===================
837 */
838 void Cmd_ForwardToServer (void)
839 {
840         if (cls.state != ca_connected)
841         {
842                 Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
843                 return;
844         }
845
846         if (cls.demoplayback)
847                 return;         // not really connected
848
849         MSG_WriteByte (&cls.message, clc_stringcmd);
850         if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
851         {
852                 SZ_Print (&cls.message, Cmd_Argv(0));
853                 SZ_Print (&cls.message, " ");
854         }
855         if (Cmd_Argc() > 1)
856                 SZ_Print (&cls.message, Cmd_Args());
857         else
858                 SZ_Print (&cls.message, "\n");
859 }
860
861
862 /*
863 ================
864 Cmd_CheckParm
865
866 Returns the position (1 to argc-1) in the command's argument list
867 where the given parameter apears, or 0 if not present
868 ================
869 */
870
871 int Cmd_CheckParm (char *parm)
872 {
873         int i;
874
875         if (!parm)
876                 Sys_Error ("Cmd_CheckParm: NULL");
877
878         for (i = 1; i < Cmd_Argc (); i++)
879                 if (!Q_strcasecmp (parm, Cmd_Argv (i)))
880                         return i;
881
882         return 0;
883 }
884