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