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