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