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