]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cmd.c
PROTOCOL_DARKPLACES5
[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 }
459
460 /*
461 ============
462 Cmd_Argc
463 ============
464 */
465 int             Cmd_Argc (void)
466 {
467         return cmd_argc;
468 }
469
470 /*
471 ============
472 Cmd_Argv
473 ============
474 */
475 const char *Cmd_Argv (int arg)
476 {
477         if (arg >= cmd_argc )
478                 return cmd_null_string;
479         return cmd_argv[arg];
480 }
481
482 /*
483 ============
484 Cmd_Args
485 ============
486 */
487 const char *Cmd_Args (void)
488 {
489         return cmd_args;
490 }
491
492
493 /*
494 ============
495 Cmd_TokenizeString
496
497 Parses the given string into command line tokens.
498 ============
499 */
500 static void Cmd_TokenizeString (const char *text)
501 {
502         int l;
503
504         cmd_argc = 0;
505         cmd_args = NULL;
506
507         while (1)
508         {
509                 // skip whitespace up to a /n
510                 while (*text && *text <= ' ' && *text != '\n')
511                         text++;
512
513                 if (*text == '\n')
514                 {
515                         // a newline seperates commands in the buffer
516                         text++;
517                         break;
518                 }
519
520                 if (!*text)
521                         return;
522
523                 if (cmd_argc == 1)
524                          cmd_args = text;
525
526                 if (!COM_ParseTokenConsole(&text))
527                         return;
528
529                 if (cmd_argc < MAX_ARGS)
530                 {
531                         l = strlen(com_token) + 1;
532                         if (cmd_tokenizebufferpos + l > CMD_TOKENIZELENGTH)
533                                 Sys_Error("Cmd_TokenizeString: ran out of %i character buffer space for command arguements\n", CMD_TOKENIZELENGTH);
534                         strcpy (cmd_tokenizebuffer + cmd_tokenizebufferpos, com_token);
535                         cmd_argv[cmd_argc] = cmd_tokenizebuffer + cmd_tokenizebufferpos;
536                         cmd_tokenizebufferpos += l;
537                         cmd_argc++;
538                 }
539         }
540
541 }
542
543
544 /*
545 ============
546 Cmd_AddCommand
547 ============
548 */
549 void Cmd_AddCommand (const char *cmd_name, xcommand_t function)
550 {
551         cmd_function_t *cmd;
552
553 // fail if the command is a variable name
554         if (Cvar_VariableString(cmd_name)[0])
555         {
556                 Con_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
557                 return;
558         }
559
560 // fail if the command already exists
561         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
562         {
563                 if (!strcmp (cmd_name, cmd->name))
564                 {
565                         Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
566                         return;
567                 }
568         }
569
570         cmd = Mem_Alloc(cmd_mempool, sizeof(cmd_function_t));
571         cmd->name = cmd_name;
572         cmd->function = function;
573         cmd->next = cmd_functions;
574         cmd_functions = cmd;
575 }
576
577 /*
578 ============
579 Cmd_Exists
580 ============
581 */
582 qboolean Cmd_Exists (const char *cmd_name)
583 {
584         cmd_function_t  *cmd;
585
586         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
587                 if (!strcmp (cmd_name,cmd->name))
588                         return true;
589
590         return false;
591 }
592
593
594 /*
595 ============
596 Cmd_CompleteCommand
597 ============
598 */
599 const char *Cmd_CompleteCommand (const char *partial)
600 {
601         cmd_function_t *cmd;
602         int len;
603
604         len = strlen(partial);
605
606         if (!len)
607                 return NULL;
608
609 // check functions
610         for (cmd = cmd_functions; cmd; cmd = cmd->next)
611                 if (!strncmp(partial, cmd->name, len))
612                         return cmd->name;
613
614         return NULL;
615 }
616
617 /*
618         Cmd_CompleteCountPossible
619
620         New function for tab-completion system
621         Added by EvilTypeGuy
622         Thanks to Fett erich@heintz.com
623         Thanks to taniwha
624
625 */
626 int Cmd_CompleteCountPossible (const char *partial)
627 {
628         cmd_function_t *cmd;
629         int len, h;
630
631         h = 0;
632         len = strlen(partial);
633
634         if (!len)
635                 return 0;
636
637         // Loop through the command list and count all partial matches
638         for (cmd = cmd_functions; cmd; cmd = cmd->next)
639                 if (!strncasecmp(partial, cmd->name, len))
640                         h++;
641
642         return h;
643 }
644
645 /*
646         Cmd_CompleteBuildList
647
648         New function for tab-completion system
649         Added by EvilTypeGuy
650         Thanks to Fett erich@heintz.com
651         Thanks to taniwha
652
653 */
654 const char **Cmd_CompleteBuildList (const char *partial)
655 {
656         cmd_function_t *cmd;
657         int len = 0;
658         int bpos = 0;
659         int sizeofbuf = (Cmd_CompleteCountPossible (partial) + 1) * sizeof (const char *);
660         const char **buf;
661
662         len = strlen(partial);
663         buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
664         // Loop through the alias list and print all matches
665         for (cmd = cmd_functions; cmd; cmd = cmd->next)
666                 if (!strncasecmp(partial, cmd->name, len))
667                         buf[bpos++] = cmd->name;
668
669         buf[bpos] = NULL;
670         return buf;
671 }
672
673 /*
674         Cmd_CompleteAlias
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 const char *Cmd_CompleteAlias (const char *partial)
683 {
684         cmdalias_t *alias;
685         int len;
686
687         len = strlen(partial);
688
689         if (!len)
690                 return NULL;
691
692         // Check functions
693         for (alias = cmd_alias; alias; alias = alias->next)
694                 if (!strncasecmp(partial, alias->name, len))
695                         return alias->name;
696
697         return NULL;
698 }
699
700 /*
701         Cmd_CompleteAliasCountPossible
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 int Cmd_CompleteAliasCountPossible (const char *partial)
710 {
711         cmdalias_t      *alias;
712         int                     len;
713         int                     h;
714
715         h = 0;
716
717         len = strlen(partial);
718
719         if (!len)
720                 return 0;
721
722         // Loop through the command list and count all partial matches
723         for (alias = cmd_alias; alias; alias = alias->next)
724                 if (!strncasecmp(partial, alias->name, len))
725                         h++;
726
727         return h;
728 }
729
730 /*
731         Cmd_CompleteAliasBuildList
732
733         New function for tab-completion system
734         Added by EvilTypeGuy
735         Thanks to Fett erich@heintz.com
736         Thanks to taniwha
737
738 */
739 const char **Cmd_CompleteAliasBuildList (const char *partial)
740 {
741         cmdalias_t *alias;
742         int len = 0;
743         int bpos = 0;
744         int sizeofbuf = (Cmd_CompleteAliasCountPossible (partial) + 1) * sizeof (const char *);
745         const char **buf;
746
747         len = strlen(partial);
748         buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
749         // Loop through the alias list and print all matches
750         for (alias = cmd_alias; alias; alias = alias->next)
751                 if (!strncasecmp(partial, alias->name, len))
752                         buf[bpos++] = alias->name;
753
754         buf[bpos] = NULL;
755         return buf;
756 }
757
758 /*
759 ============
760 Cmd_ExecuteString
761
762 A complete command line has been parsed, so try to execute it
763 FIXME: lookupnoadd the token to speed search?
764 ============
765 */
766 void Cmd_ExecuteString (const char *text, cmd_source_t src)
767 {
768         int oldpos;
769         cmd_function_t *cmd;
770         cmdalias_t *a;
771
772         oldpos = cmd_tokenizebufferpos;
773         cmd_source = src;
774         Cmd_TokenizeString (text);
775
776 // execute the command line
777         if (!Cmd_Argc())
778         {
779                 cmd_tokenizebufferpos = oldpos;
780                 return;         // no tokens
781         }
782
783 // check functions (only after host_initialized)
784         if (host_initialized || !strcasecmp(cmd_argv[0], "exec"))
785         {
786                 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
787                 {
788                         if (!strcasecmp (cmd_argv[0],cmd->name))
789                         {
790                                 cmd->function ();
791                                 cmd_tokenizebufferpos = oldpos;
792                                 return;
793                         }
794                 }
795         }
796
797 // check alias (only after host_initialized)
798         if (host_initialized)
799         {
800                 for (a=cmd_alias ; a ; a=a->next)
801                 {
802                         if (!strcasecmp (cmd_argv[0], a->name))
803                         {
804                                 Cbuf_InsertText (a->value);
805                                 cmd_tokenizebufferpos = oldpos;
806                                 return;
807                         }
808                 }
809         }
810
811 // check cvars (always)
812         if (!Cvar_Command () && host_initialized)
813                 Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(0));
814
815         cmd_tokenizebufferpos = oldpos;
816 }
817
818
819 /*
820 ===================
821 Cmd_ForwardToServer
822
823 Sends the entire command line over to the server
824 ===================
825 */
826 void Cmd_ForwardToServer (void)
827 {
828         const char *s;
829         if (cls.state != ca_connected)
830         {
831                 Con_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0));
832                 return;
833         }
834
835         if (cls.demoplayback)
836                 return;         // not really connected
837
838         // LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
839         // attention, it has been eradicated from here, its only (former) use in
840         // all of darkplaces.
841         if (strcasecmp(Cmd_Argv(0), "cmd") != 0)
842                 s = va("%s %s", Cmd_Argv(0), Cmd_Argc() > 1 ? Cmd_Args() : "\n");
843         else
844                 s = Cmd_Argc() > 1 ? Cmd_Args() : "\n";
845         MSG_WriteByte(&cls.message, clc_stringcmd);
846         SZ_Write(&cls.message, s, strlen(s) + 1);
847 }
848
849
850 /*
851 ================
852 Cmd_CheckParm
853
854 Returns the position (1 to argc-1) in the command's argument list
855 where the given parameter apears, or 0 if not present
856 ================
857 */
858
859 int Cmd_CheckParm (const char *parm)
860 {
861         int i;
862
863         if (!parm)
864                 Sys_Error ("Cmd_CheckParm: NULL");
865
866         for (i = 1; i < Cmd_Argc (); i++)
867                 if (!strcasecmp (parm, Cmd_Argv (i)))
868                         return i;
869
870         return 0;
871 }
872