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