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 void Cmd_ForwardToServer (void);
25
26 #define MAX_ALIAS_NAME 32
27
28 typedef struct cmdalias_s
29 {
30 struct cmdalias_s *next;
31 char name[MAX_ALIAS_NAME];
32 char *value;
33 } cmdalias_t;
34
35 cmdalias_t *cmd_alias;
36
37 int trashtest;
38 int *trashspot;
39
40 qboolean cmd_wait;
41
42 //=============================================================================
43
44 /*
45 ============
46 Cmd_Wait_f
47
48 Causes execution of the remainder of the command buffer to be delayed until
49 next frame. This allows commands like:
50 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
51 ============
52 */
Cmd_Wait_f(void)53 void Cmd_Wait_f (void)
54 {
55 cmd_wait = true;
56 }
57
58 /*
59 =============================================================================
60
61 COMMAND BUFFER
62
63 =============================================================================
64 */
65
66 sizebuf_t cmd_text;
67
68 /*
69 ============
70 Cbuf_Init
71 ============
72 */
Cbuf_Init(void)73 void Cbuf_Init (void)
74 {
75 SZ_Alloc (&cmd_text, 8192); // space for commands and script files
76 }
77
78
79 /*
80 ============
81 Cbuf_AddText
82
83 Adds command text at the end of the buffer
84 ============
85 */
Cbuf_AddText(char * text)86 void Cbuf_AddText (char *text)
87 {
88 int l;
89
90 l = Q_strlen (text);
91
92 if (cmd_text.cursize + l >= cmd_text.maxsize)
93 {
94 Con_Printf ("Cbuf_AddText: overflow\n");
95 return;
96 }
97
98 SZ_Write (&cmd_text, text, Q_strlen (text));
99 }
100
101
102 /*
103 ============
104 Cbuf_InsertText
105
106 Adds command text immediately after the current command
107 Adds a \n to the text
108 FIXME: actually change the command buffer to do less copying
109 ============
110 */
Cbuf_InsertText(char * text)111 void Cbuf_InsertText (char *text)
112 {
113 char *temp;
114 int templen;
115
116 // copy off any commands still remaining in the exec buffer
117 templen = cmd_text.cursize;
118 if (templen)
119 {
120 temp = Z_Malloc (templen);
121 Q_memcpy (temp, cmd_text.data, templen);
122 SZ_Clear (&cmd_text);
123 }
124 else
125 temp = NULL; // shut up compiler
126
127 // add the entire text of the file
128 Cbuf_AddText (text);
129
130 // add the copied off data
131 if (templen)
132 {
133 SZ_Write (&cmd_text, temp, templen);
134 Z_Free (temp);
135 }
136 }
137
138 /*
139 ============
140 Cbuf_Execute
141 ============
142 */
Cbuf_Execute(void)143 void Cbuf_Execute (void)
144 {
145 int i;
146 char *text;
147 char line[1024];
148 int quotes;
149
150 while (cmd_text.cursize)
151 {
152 // find a \n or ; line break
153 text = (char *)cmd_text.data;
154
155 quotes = 0;
156 for (i=0 ; i< cmd_text.cursize ; i++)
157 {
158 if (text[i] == '"')
159 quotes++;
160 if ( !(quotes&1) && text[i] == ';')
161 break; // don't break if inside a quoted string
162 if (text[i] == '\n')
163 break;
164 }
165
166
167 memcpy (line, text, i);
168 line[i] = 0;
169
170 // delete the text from the command buffer and move remaining commands down
171 // this is necessary because commands (exec, alias) can insert data at the
172 // beginning of the text buffer
173
174 if (i == cmd_text.cursize)
175 cmd_text.cursize = 0;
176 else
177 {
178 i++;
179 cmd_text.cursize -= i;
180 Q_memcpy (text, text+i, cmd_text.cursize);
181 }
182
183 // execute the command line
184 Cmd_ExecuteString (line, src_command);
185
186 if (cmd_wait)
187 { // skip out while text still remains in buffer, leaving it
188 // for next frame
189 cmd_wait = false;
190 break;
191 }
192 }
193 }
194
195 /*
196 ==============================================================================
197
198 SCRIPT COMMANDS
199
200 ==============================================================================
201 */
202
203 /*
204 ===============
205 Cmd_StuffCmds_f
206
207 Adds command line parameters as script statements
208 Commands lead with a +, and continue until a - or another +
209 quake +prog jctest.qp +cmd amlev1
210 quake -nosound +cmd amlev1
211 ===============
212 */
Cmd_StuffCmds_f(void)213 void Cmd_StuffCmds_f (void)
214 {
215 int i, j;
216 int s;
217 char *text, *build, c;
218
219 if (Cmd_Argc () != 1)
220 {
221 Con_Printf ("stuffcmds : execute command line parameters\n");
222 return;
223 }
224
225 // build the combined string to parse from
226 s = 0;
227 for (i=1 ; i<com_argc ; i++)
228 {
229 if (!com_argv[i])
230 continue; // NEXTSTEP nulls out -NXHost
231 s += Q_strlen (com_argv[i]) + 1;
232 }
233 if (!s)
234 return;
235
236 text = Z_Malloc (s+1);
237 text[0] = 0;
238 for (i=1 ; i<com_argc ; i++)
239 {
240 if (!com_argv[i])
241 continue; // NEXTSTEP nulls out -NXHost
242 Q_strcat (text,com_argv[i]);
243 if (i != com_argc-1)
244 Q_strcat (text, " ");
245 }
246
247 // pull out the commands
248 build = Z_Malloc (s+1);
249 build[0] = 0;
250
251 for (i=0 ; i<s-1 ; i++)
252 {
253 if (text[i] == '+')
254 {
255 i++;
256
257 for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
258 ;
259
260 c = text[j];
261 text[j] = 0;
262
263 Q_strcat (build, text+i);
264 Q_strcat (build, "\n");
265 text[j] = c;
266 i = j-1;
267 }
268 }
269
270 if (build[0])
271 Cbuf_InsertText (build);
272
273 Z_Free (text);
274 Z_Free (build);
275 }
276
277
278 /*
279 ===============
280 Cmd_Exec_f
281 ===============
282 */
Cmd_Exec_f(void)283 void Cmd_Exec_f (void)
284 {
285 char *f;
286 int mark;
287
288 if (Cmd_Argc () != 2)
289 {
290 Con_Printf ("exec <filename> : execute a script file\n");
291 return;
292 }
293
294 mark = Hunk_LowMark ();
295 f = (char *)COM_LoadHunkFile (Cmd_Argv(1));
296 if (!f)
297 {
298 Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
299 return;
300 }
301 #if defined (__APPLE__) || defined (MACOSX)
302
303 // <AWE> Fix for stupid Windows linebreaks.
304 {
305 char * myData = f;
306
307 while (*myData != 0x00)
308 {
309 if (*myData == 0x0D)
310 {
311 *myData = 0x0A;
312 }
313 myData++;
314 }
315 }
316
317 #endif /* __APPLE__ ||�MACOSX */
318 Con_Printf ("execing %s\n",Cmd_Argv(1));
319
320 Cbuf_InsertText (f);
321 Hunk_FreeToLowMark (mark);
322 }
323
324
325 /*
326 ===============
327 Cmd_Echo_f
328
329 Just prints the rest of the line to the console
330 ===============
331 */
Cmd_Echo_f(void)332 void Cmd_Echo_f (void)
333 {
334 int i;
335
336 for (i=1 ; i<Cmd_Argc() ; i++)
337 Con_Printf ("%s ",Cmd_Argv(i));
338 Con_Printf ("\n");
339 }
340
341 /*
342 ===============
343 Cmd_Alias_f
344
345 Creates a new command that executes a command string (possibly ; seperated)
346 ===============
347 */
348
CopyString(char * in)349 char *CopyString (char *in)
350 {
351 char *out;
352
353 out = Z_Malloc (strlen(in)+1);
354 strcpy (out, in);
355 return out;
356 }
357
Cmd_Alias_f(void)358 void Cmd_Alias_f (void)
359 {
360 cmdalias_t *a;
361 char cmd[1024];
362 int i, c;
363 char *s;
364
365 if (Cmd_Argc() == 1)
366 {
367 Con_Printf ("Current alias commands:\n");
368 for (a = cmd_alias ; a ; a=a->next)
369 Con_Printf ("%s : %s\n", a->name, a->value);
370 return;
371 }
372
373 s = Cmd_Argv(1);
374 if (strlen(s) >= MAX_ALIAS_NAME)
375 {
376 Con_Printf ("Alias name is too long\n");
377 return;
378 }
379
380 // if the alias allready exists, reuse it
381 for (a = cmd_alias ; a ; a=a->next)
382 {
383 if (!strcmp(s, a->name))
384 {
385 Z_Free (a->value);
386 break;
387 }
388 }
389
390 if (!a)
391 {
392 a = Z_Malloc (sizeof(cmdalias_t));
393 a->next = cmd_alias;
394 cmd_alias = a;
395 }
396 strcpy (a->name, s);
397
398 // copy the rest of the command line
399 cmd[0] = 0; // start out with a null string
400 c = Cmd_Argc();
401 for (i=2 ; i< c ; i++)
402 {
403 strcat (cmd, Cmd_Argv(i));
404 if (i != c)
405 strcat (cmd, " ");
406 }
407 strcat (cmd, "\n");
408
409 a->value = CopyString (cmd);
410 }
411
412 /*
413 =============================================================================
414
415 COMMAND EXECUTION
416
417 =============================================================================
418 */
419
420 typedef struct cmd_function_s
421 {
422 struct cmd_function_s *next;
423 char *name;
424 xcommand_t function;
425 } cmd_function_t;
426
427
428 #define MAX_ARGS 80
429
430 static int cmd_argc;
431 static char *cmd_argv[MAX_ARGS];
432 static char *cmd_null_string = "";
433 static char *cmd_args = NULL;
434
435 cmd_source_t cmd_source;
436
437
438 static cmd_function_t *cmd_functions; // possible commands to execute
439
440 /*
441 ============
442 Cmd_Init
443 ============
444 */
Cmd_Init(void)445 void Cmd_Init (void)
446 {
447 //
448 // register our commands
449 //
450 Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
451 Cmd_AddCommand ("exec",Cmd_Exec_f);
452 Cmd_AddCommand ("echo",Cmd_Echo_f);
453 Cmd_AddCommand ("alias",Cmd_Alias_f);
454 Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
455 Cmd_AddCommand ("wait", Cmd_Wait_f);
456 Cmd_AddCommand ("cmdlist", Cmd_List_f); //Cmdlist - Eradicator
457 Cmd_AddCommand ("cvarlist", Cvar_List_f); //Cvarlist - Eradicator
458 }
459
460 /*
461 ============
462 Cmd_Argc
463 ============
464 */
Cmd_Argc(void)465 int Cmd_Argc (void)
466 {
467 return cmd_argc;
468 }
469
470 /*
471 ============
472 Cmd_Argv
473 ============
474 */
Cmd_Argv(int arg)475 char *Cmd_Argv (int arg)
476 {
477 if ( (unsigned)arg >= cmd_argc )
478 return cmd_null_string;
479 return cmd_argv[arg];
480 }
481
482 /*
483 ============
484 Cmd_Args
485 ============
486 */
Cmd_Args(void)487 char *Cmd_Args (void)
488 {
489 return cmd_args;
490 }
491
492
493 /*
494 ============
495 Cmd_TokenizeString
496
497 Parses the given string into command line tokens.
498 ============
499 */
Cmd_TokenizeString(char * text)500 void Cmd_TokenizeString (char *text)
501 {
502 int i;
503
504 // clear the args from the last string
505 for (i=0 ; i<cmd_argc ; i++)
506 Z_Free (cmd_argv[i]);
507
508 cmd_argc = 0;
509 cmd_args = NULL;
510
511 while (1)
512 {
513 // skip whitespace up to a /n
514 while (*text && *text <= ' ' && *text != '\n')
515 {
516 text++;
517 }
518
519 if (*text == '\n')
520 { // a newline seperates commands in the buffer
521 text++;
522 break;
523 }
524
525 if (!*text)
526 return;
527
528 if (cmd_argc == 1)
529 cmd_args = text;
530
531 text = COM_Parse (text);
532 if (!text)
533 return;
534
535 if (cmd_argc < MAX_ARGS)
536 {
537 cmd_argv[cmd_argc] = Z_Malloc (Q_strlen(com_token)+1);
538 Q_strcpy (cmd_argv[cmd_argc], com_token);
539 cmd_argc++;
540 }
541 }
542
543 }
544
545
546 /*
547 ============
548 Cmd_AddCommand
549 ============
550 */
Cmd_AddCommand(char * cmd_name,xcommand_t function)551 void Cmd_AddCommand (char *cmd_name, xcommand_t function)
552 {
553 cmd_function_t *cmd;
554
555 if (host_initialized) // because hunk allocation would get stomped
556 Sys_Error ("Cmd_AddCommand after host_initialized");
557
558 // fail if the command is a variable name
559 if (Cvar_VariableString(cmd_name)[0])
560 {
561 Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
562 return;
563 }
564
565 // fail if the command already exists
566 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
567 {
568 if (!Q_strcmp (cmd_name, cmd->name))
569 {
570 Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
571 return;
572 }
573 }
574
575 cmd = Hunk_Alloc (sizeof(cmd_function_t));
576 cmd->name = cmd_name;
577 cmd->function = function;
578 cmd->next = cmd_functions;
579 cmd_functions = cmd;
580 }
581
582 /*
583 ============
584 Cmd_Exists
585 ============
586 */
Cmd_Exists(char * cmd_name)587 qboolean Cmd_Exists (char *cmd_name)
588 {
589 cmd_function_t *cmd;
590
591 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
592 {
593 if (!Q_strcmp (cmd_name,cmd->name))
594 return true;
595 }
596
597 return false;
598 }
599
600
601
602 /*
603 ============
604 Cmd_CompleteCommand
605 ============
606 */
Cmd_CompleteCommand(char * partial)607 char *Cmd_CompleteCommand (char *partial)
608 {
609 cmd_function_t *cmd;
610 int len;
611
612 len = Q_strlen(partial);
613
614 if (!len)
615 return NULL;
616
617 // check functions
618 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
619 if (!Q_strncmp (partial,cmd->name, len))
620 return cmd->name;
621
622 return NULL;
623 }
624
625 /*
626 ============
627 Cmd_CompleteCountPossible
628 ============
629 */
Cmd_CompleteCountPossible(char * partial)630 int Cmd_CompleteCountPossible (char *partial)
631 {
632 cmd_function_t *cmd;
633 int len;
634 int h;
635
636 h=0;
637
638 len = Q_strlen(partial);
639
640 if (!len)
641 return 0;
642
643 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
644 if (!Q_strncmp (partial,cmd->name, len))
645 h++;
646 return h;
647 }
648
649 /*
650 ============
651 Cmd_CompletePrintPossible
652 ============
653 */
Cmd_CompletePrintPossible(char * partial)654 void Cmd_CompletePrintPossible (char *partial)
655 {
656 cmd_function_t *cmd;
657 int len;
658 int lpos;
659 int out;
660 int con_linewidth;
661 char sout[32];
662 char lout[2048];
663
664 lpos = 0;
665 len = Q_strlen(partial);
666 Q_strcpy(lout,"");
667
668 con_linewidth = (vid.width >> 3) - 3;
669
670 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
671 if (!Q_strncmp (partial,cmd->name, len))
672 {
673 Q_strcpy(sout, cmd->name);
674
675 out = Q_strlen(sout);
676 lpos += out;
677
678 for (out; out<20; out++)
679 {
680 if (lpos < con_linewidth)
681 Q_strcat (sout, " ");
682
683 lpos++;
684 }
685
686 Q_strcat (lout, sout);
687
688 if (lpos > con_linewidth - 24)
689 for (lpos; lpos < con_linewidth; lpos++)
690 Q_strcat(lout, " ");
691
692 if (lpos >= con_linewidth)
693 lpos = 0;
694 }
695
696 Con_Printf ("%s\n\n", lout);
697 }
698
699 /*
700 ============
701 Cmd_CompleteAlias
702
703 Alias Command Tab Completion - Eradicator
704 ============
705 */
Cmd_CompleteAlias(char * partial)706 char *Cmd_CompleteAlias (char *partial)
707 {
708 cmdalias_t *alias;
709 int len;
710
711 len = Q_strlen(partial);
712
713 if (!len)
714 return NULL;
715
716 for (alias=cmd_alias ; alias ; alias=alias->next)
717 if (!Q_strncmp (partial,alias->name, len))
718 return alias->name;
719 return NULL;
720 }
721
722 /*
723 ============
724 Cmd_CompleteAliasCountPossible
725 ============
726 */
Cmd_CompleteAliasCountPossible(char * partial)727 int Cmd_CompleteAliasCountPossible (char *partial)
728 {
729 cmdalias_t *alias;
730 int len;
731 int h;
732
733 h=0;
734
735 len = Q_strlen(partial);
736
737 if (!len)
738 return 0;
739
740 for (alias=cmd_alias ; alias ; alias=alias->next)
741 if (!Q_strncmp (partial,alias->name, len))
742 h++;
743 return h;
744 }
745
746 /*
747 ============
748 Cmd_CompleteAliasPrintPossible
749 ============
750 */
Cmd_CompleteAliasPrintPossible(char * partial)751 void Cmd_CompleteAliasPrintPossible (char *partial)
752 {
753 cmdalias_t *alias;
754 int len;
755 int lpos;
756 int out;
757 int con_linewidth;
758 char sout[32];
759 char lout[2048];
760
761 lpos = 0;
762 len = Q_strlen(partial);
763 Q_strcpy(lout,"");
764
765 con_linewidth = (vid.width >> 3) - 3;
766
767 for (alias=cmd_alias ; alias ; alias=alias->next)
768 if (!Q_strncmp (partial,alias->name, len))
769 {
770 Q_strcpy(sout, alias->name);
771
772 out = Q_strlen(sout);
773 lpos += out;
774
775 for (out; out<20; out++)
776 {
777 if (lpos < con_linewidth)
778 Q_strcat (sout, " ");
779
780 lpos++;
781 }
782
783 Q_strcat (lout, sout);
784
785 if (lpos > con_linewidth - 24)
786 for (lpos; lpos < con_linewidth; lpos++)
787 Q_strcat(lout, " ");
788
789 if (lpos >= con_linewidth)
790 lpos = 0;
791 }
792 Con_Printf ("%s\n\n", lout);
793 }
794
795 /*
796 ============
797 Cmd_ExecuteString
798
799 A complete command line has been parsed, so try to execute it
800 FIXME: lookupnoadd the token to speed search?
801 ============
802 */
Cmd_ExecuteString(char * text,cmd_source_t src)803 void Cmd_ExecuteString (char *text, cmd_source_t src)
804 {
805 cmd_function_t *cmd;
806 cmdalias_t *a;
807
808 cmd_source = src;
809 Cmd_TokenizeString (text);
810
811 // execute the command line
812 if (!Cmd_Argc())
813 return; // no tokens
814
815 // check functions
816 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
817 {
818 if (!Q_strcasecmp (cmd_argv[0],cmd->name))
819 {
820 cmd->function ();
821 return;
822 }
823 }
824
825 // check alias
826 for (a=cmd_alias ; a ; a=a->next)
827 {
828 if (!Q_strcasecmp (cmd_argv[0], a->name))
829 {
830 Cbuf_InsertText (a->value);
831 return;
832 }
833 }
834
835 // check cvars
836 if (!Cvar_Command ())
837 Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
838
839 }
840
841
842 /*
843 ===================
844 Cmd_ForwardToServer
845
846 Sends the entire command line over to the server
847 ===================
848 */
Cmd_ForwardToServer(void)849 void Cmd_ForwardToServer (void)
850 {
851 if (cls.state != ca_connected)
852 {
853 Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
854 return;
855 }
856
857 if (cls.demoplayback)
858 return; // not really connected
859
860 MSG_WriteByte (&cls.message, clc_stringcmd);
861 if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
862 {
863 SZ_Print (&cls.message, Cmd_Argv(0));
864 SZ_Print (&cls.message, " ");
865 }
866 if (Cmd_Argc() > 1)
867 SZ_Print (&cls.message, Cmd_Args());
868 else
869 SZ_Print (&cls.message, "\n");
870 }
871
872
873 /*
874 ================
875 Cmd_CheckParm
876
877 Returns the position (1 to argc-1) in the command's argument list
878 where the given parameter apears, or 0 if not present
879 ================
880 */
881
Cmd_CheckParm(char * parm)882 int Cmd_CheckParm (char *parm)
883 {
884 int i;
885
886 if (!parm)
887 Sys_Error ("Cmd_CheckParm: NULL");
888
889 for (i = 1; i < Cmd_Argc (); i++)
890 if (! Q_strcasecmp (parm, Cmd_Argv (i)))
891 return i;
892
893 return 0;
894 }
895
896 /*
897 ========
898 Cmd_List
899
900 Displays a list of all the commands similar to the Quake3 cmdlist - Eradicator
901 ========
902 */
Cmd_List_f(void)903 void Cmd_List_f (void)
904 {
905 cmd_function_t *cmd;
906 char *partial;
907 int len;
908 int count;
909
910 if (Cmd_Argc() > 1)
911 {
912 partial = Cmd_Argv (1);
913 len = Q_strlen(partial);
914 }
915 else
916 {
917 partial = NULL;
918 len = 0;
919 }
920
921 count=0;
922 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
923 {
924 if (partial && Q_strncmp (partial,cmd->name, len))
925 {
926 continue;
927 }
928 Con_Printf ("\"%s\"\n", cmd->name);
929 count++;
930 }
931
932 Con_Printf ("%i command(s)", count);
933 if (partial)
934 {
935 Con_Printf (" beginning with \"%s\"", partial);
936 }
937 Con_Printf ("\n");
938 }
939