]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cmd.c
fix win32 stack overflows in CL_ParseServerInfo, it was choking on the sheer quantity...
[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 //=============================================================================
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 (const 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 (const 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 static char *CopyString (char *in)
327 {
328         char *out;
329
330         out = Z_Malloc (strlen(in)+1);
331         strcpy (out, in);
332         return out;
333 }
334
335 static void Cmd_Alias_f (void)
336 {
337         cmdalias_t      *a;
338         char            cmd[1024];
339         int                     i, c;
340         const char              *s;
341
342         if (Cmd_Argc() == 1)
343         {
344                 Con_Printf ("Current alias commands:\n");
345                 for (a = cmd_alias ; a ; a=a->next)
346                         Con_Printf ("%s : %s\n", a->name, a->value);
347                 return;
348         }
349
350         s = Cmd_Argv(1);
351         if (strlen(s) >= MAX_ALIAS_NAME)
352         {
353                 Con_Printf ("Alias name is too long\n");
354                 return;
355         }
356
357         // if the alias already exists, reuse it
358         for (a = cmd_alias ; a ; a=a->next)
359         {
360                 if (!strcmp(s, a->name))
361                 {
362                         Z_Free (a->value);
363                         break;
364                 }
365         }
366
367         if (!a)
368         {
369                 a = Z_Malloc (sizeof(cmdalias_t));
370                 a->next = cmd_alias;
371                 cmd_alias = a;
372         }
373         strcpy (a->name, s);
374
375 // copy the rest of the command line
376         cmd[0] = 0;             // start out with a null string
377         c = Cmd_Argc();
378         for (i=2 ; i< c ; i++)
379         {
380                 strcat (cmd, Cmd_Argv(i));
381                 if (i != c)
382                         strcat (cmd, " ");
383         }
384         strcat (cmd, "\n");
385
386         a->value = CopyString (cmd);
387 }
388
389 /*
390 =============================================================================
391
392                                         COMMAND EXECUTION
393
394 =============================================================================
395 */
396
397 typedef struct cmd_function_s
398 {
399         struct cmd_function_s *next;
400         const char *name;
401         xcommand_t function;
402 } cmd_function_t;
403
404
405 #define MAX_ARGS                80
406
407 static int cmd_argc;
408 static const char *cmd_argv[MAX_ARGS];
409 static const char *cmd_null_string = "";
410 static const char *cmd_args = NULL;
411
412 cmd_source_t cmd_source;
413
414
415 static cmd_function_t *cmd_functions;           // possible commands to execute
416
417 /*
418 ========
419 Cmd_List
420
421         CmdList Added by EvilTypeGuy eviltypeguy@qeradiant.com
422         Thanks to Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
423
424 ========
425 */
426 static void Cmd_List_f (void)
427 {
428         cmd_function_t *cmd;
429         const char *partial;
430         int len, count;
431
432         if (Cmd_Argc() > 1)
433         {
434                 partial = Cmd_Argv (1);
435                 len = strlen(partial);
436         }
437         else
438         {
439                 partial = NULL;
440                 len = 0;
441         }
442
443         count = 0;
444         for (cmd = cmd_functions; cmd; cmd = cmd->next)
445         {
446                 if (partial && strncmp(partial, cmd->name, len))
447                         continue;
448                 Con_Printf ("%s\n", cmd->name);
449                 count++;
450         }
451
452         Con_Printf ("%i Command%s", count, (count > 1) ? "s" : "");
453         if (partial)
454                 Con_Printf(" beginning with \"%s\"", partial);
455
456         Con_Printf ("\n\n");
457 }
458
459 /*
460 ============
461 Cmd_Init
462 ============
463 */
464 void Cmd_Init (void)
465 {
466         cmd_mempool = Mem_AllocPool("commands");
467
468 //
469 // register our commands
470 //
471         Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
472         Cmd_AddCommand ("exec",Cmd_Exec_f);
473         Cmd_AddCommand ("echo",Cmd_Echo_f);
474         Cmd_AddCommand ("alias",Cmd_Alias_f);
475         Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
476         Cmd_AddCommand ("wait", Cmd_Wait_f);
477         Cmd_AddCommand ("cmdlist", Cmd_List_f);         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
478         Cmd_AddCommand ("cvarlist", Cvar_List_f);       // 2000-01-09 CmdList, CvarList commands
479                                                                                                 // By Matthias "Maddes" Buecher
480 }
481
482 /*
483 ============
484 Cmd_Argc
485 ============
486 */
487 int             Cmd_Argc (void)
488 {
489         return cmd_argc;
490 }
491
492 /*
493 ============
494 Cmd_Argv
495 ============
496 */
497 const char *Cmd_Argv (int arg)
498 {
499         if (arg >= cmd_argc )
500                 return cmd_null_string;
501         return cmd_argv[arg];
502 }
503
504 /*
505 ============
506 Cmd_Args
507 ============
508 */
509 const char *Cmd_Args (void)
510 {
511         return cmd_args;
512 }
513
514
515 #define CMD_TOKENIZELENGTH 4096
516 char cmd_tokenizebuffer[CMD_TOKENIZELENGTH];
517
518 /*
519 ============
520 Cmd_TokenizeString
521
522 Parses the given string into command line tokens.
523 ============
524 */
525 static void Cmd_TokenizeString (const char *text)
526 {
527         int l;
528         int pos;
529         pos = 0;
530
531         cmd_argc = 0;
532         cmd_args = NULL;
533
534         while (1)
535         {
536 // skip whitespace up to a /n
537                 while (*text && *text <= ' ' && *text != '\n')
538                 {
539                         text++;
540                 }
541
542                 if (*text == '\n')
543                 {       // a newline seperates commands in the buffer
544                         text++;
545                         break;
546                 }
547
548                 if (!*text)
549                         return;
550
551                 if (cmd_argc == 1)
552                          cmd_args = text;
553
554                 if (!COM_ParseToken (&text))
555                         return;
556
557                 if (cmd_argc < MAX_ARGS)
558                 {
559                         l = strlen(com_token) + 1;
560                         if (pos + l > CMD_TOKENIZELENGTH)
561                                 Sys_Error("Cmd_TokenizeString: ran out of %i character buffer space for command arguements\n", CMD_TOKENIZELENGTH);
562                         strcpy (cmd_tokenizebuffer + pos, com_token);
563                         cmd_argv[cmd_argc] = cmd_tokenizebuffer + pos;
564                         pos += l;
565                         cmd_argc++;
566                 }
567         }
568
569 }
570
571
572 /*
573 ============
574 Cmd_AddCommand
575 ============
576 */
577 void Cmd_AddCommand (const char *cmd_name, xcommand_t function)
578 {
579         cmd_function_t *cmd;
580
581 // fail if the command is a variable name
582         if (Cvar_VariableString(cmd_name)[0])
583         {
584                 Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
585                 return;
586         }
587
588 // fail if the command already exists
589         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
590         {
591                 if (!strcmp (cmd_name, cmd->name))
592                 {
593                         Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
594                         return;
595                 }
596         }
597
598         cmd = Mem_Alloc(cmd_mempool, sizeof(cmd_function_t));
599         cmd->name = cmd_name;
600         cmd->function = function;
601         cmd->next = cmd_functions;
602         cmd_functions = cmd;
603 }
604
605 /*
606 ============
607 Cmd_Exists
608 ============
609 */
610 qboolean Cmd_Exists (const char *cmd_name)
611 {
612         cmd_function_t  *cmd;
613
614         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
615                 if (!strcmp (cmd_name,cmd->name))
616                         return true;
617
618         return false;
619 }
620
621
622 /*
623 ============
624 Cmd_CompleteCommand
625 ============
626 */
627 const char *Cmd_CompleteCommand (const char *partial)
628 {
629         cmd_function_t *cmd;
630         int len;
631
632         len = strlen(partial);
633
634         if (!len)
635                 return NULL;
636
637 // check functions
638         for (cmd = cmd_functions; cmd; cmd = cmd->next)
639                 if (!strncmp(partial, cmd->name, len))
640                         return cmd->name;
641
642         return NULL;
643 }
644
645 /*
646         Cmd_CompleteCountPossible
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 int Cmd_CompleteCountPossible (const char *partial)
655 {
656         cmd_function_t *cmd;
657         int len, h;
658
659         h = 0;
660         len = strlen(partial);
661
662         if (!len)
663                 return 0;
664
665         // Loop through the command list and count all partial matches
666         for (cmd = cmd_functions; cmd; cmd = cmd->next)
667                 if (!strncasecmp(partial, cmd->name, len))
668                         h++;
669
670         return h;
671 }
672
673 /*
674         Cmd_CompleteBuildList
675
676         New function for tab-completion system
677         Added by EvilTypeGuy
678         Thanks to Fett erich@heintz.com
679         Thanks to taniwha
680
681 */
682 const char **Cmd_CompleteBuildList (const char *partial)
683 {
684         cmd_function_t *cmd;
685         int len = 0;
686         int bpos = 0;
687         int sizeofbuf = (Cmd_CompleteCountPossible (partial) + 1) * sizeof (const char *);
688         const char **buf;
689
690         len = strlen(partial);
691         buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
692         // Loop through the alias list and print all matches
693         for (cmd = cmd_functions; cmd; cmd = cmd->next)
694                 if (!strncasecmp(partial, cmd->name, len))
695                         buf[bpos++] = cmd->name;
696
697         buf[bpos] = NULL;
698         return buf;
699 }
700
701 /*
702         Cmd_CompleteAlias
703
704         New function for tab-completion system
705         Added by EvilTypeGuy
706         Thanks to Fett erich@heintz.com
707         Thanks to taniwha
708
709 */
710 const char *Cmd_CompleteAlias (const char *partial)
711 {
712         cmdalias_t *alias;
713         int len;
714
715         len = strlen(partial);
716
717         if (!len)
718                 return NULL;
719
720         // Check functions
721         for (alias = cmd_alias; alias; alias = alias->next)
722                 if (!strncasecmp(partial, alias->name, len))
723                         return alias->name;
724
725         return NULL;
726 }
727
728 /*
729         Cmd_CompleteAliasCountPossible
730
731         New function for tab-completion system
732         Added by EvilTypeGuy
733         Thanks to Fett erich@heintz.com
734         Thanks to taniwha
735
736 */
737 int Cmd_CompleteAliasCountPossible (const char *partial)
738 {
739         cmdalias_t      *alias;
740         int                     len;
741         int                     h;
742
743         h = 0;
744
745         len = strlen(partial);
746
747         if (!len)
748                 return 0;
749
750         // Loop through the command list and count all partial matches
751         for (alias = cmd_alias; alias; alias = alias->next)
752                 if (!strncasecmp(partial, alias->name, len))
753                         h++;
754
755         return h;
756 }
757
758 /*
759         Cmd_CompleteAliasBuildList
760
761         New function for tab-completion system
762         Added by EvilTypeGuy
763         Thanks to Fett erich@heintz.com
764         Thanks to taniwha
765
766 */
767 const char **Cmd_CompleteAliasBuildList (const char *partial)
768 {
769         cmdalias_t *alias;
770         int len = 0;
771         int bpos = 0;
772         int sizeofbuf = (Cmd_CompleteAliasCountPossible (partial) + 1) * sizeof (const char *);
773         const char **buf;
774
775         len = strlen(partial);
776         buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
777         // Loop through the alias list and print all matches
778         for (alias = cmd_alias; alias; alias = alias->next)
779                 if (!strncasecmp(partial, alias->name, len))
780                         buf[bpos++] = alias->name;
781
782         buf[bpos] = NULL;
783         return buf;
784 }
785
786 /*
787 ============
788 Cmd_ExecuteString
789
790 A complete command line has been parsed, so try to execute it
791 FIXME: lookupnoadd the token to speed search?
792 ============
793 */
794 void Cmd_ExecuteString (const char *text, cmd_source_t src)
795 {
796         cmd_function_t *cmd;
797         cmdalias_t *a;
798
799         cmd_source = src;
800         Cmd_TokenizeString (text);
801
802 // execute the command line
803         if (!Cmd_Argc())
804                 return;         // no tokens
805
806 // check functions
807         for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
808         {
809                 if (!Q_strcasecmp (cmd_argv[0],cmd->name))
810                 {
811                         cmd->function ();
812                         return;
813                 }
814         }
815
816 // check alias
817         for (a=cmd_alias ; a ; a=a->next)
818         {
819                 if (!Q_strcasecmp (cmd_argv[0], a->name))
820                 {
821                         Cbuf_InsertText (a->value);
822                         return;
823                 }
824         }
825
826 // check cvars
827         if (!Cvar_Command ())
828                 Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
829 }
830
831
832 /*
833 ===================
834 Cmd_ForwardToServer
835
836 Sends the entire command line over to the server
837 ===================
838 */
839 void Cmd_ForwardToServer (void)
840 {
841         if (cls.state != ca_connected)
842         {
843                 Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
844                 return;
845         }
846
847         if (cls.demoplayback)
848                 return;         // not really connected
849
850         MSG_WriteByte (&cls.message, clc_stringcmd);
851         if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
852         {
853                 SZ_Print (&cls.message, Cmd_Argv(0));
854                 SZ_Print (&cls.message, " ");
855         }
856         if (Cmd_Argc() > 1)
857                 SZ_Print (&cls.message, Cmd_Args());
858         else
859                 SZ_Print (&cls.message, "\n");
860 }
861
862
863 /*
864 ================
865 Cmd_CheckParm
866
867 Returns the position (1 to argc-1) in the command's argument list
868 where the given parameter apears, or 0 if not present
869 ================
870 */
871
872 int Cmd_CheckParm (const char *parm)
873 {
874         int i;
875
876         if (!parm)
877                 Sys_Error ("Cmd_CheckParm: NULL");
878
879         for (i = 1; i < Cmd_Argc (); i++)
880                 if (!Q_strcasecmp (parm, Cmd_Argv (i)))
881                         return i;
882
883         return 0;
884 }
885