File: qcommon\cmd.c
1 /*
2 Copyright (C) 1997-2001 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 "qcommon.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 qboolean cmd_wait;
38
39 #define ALIAS_LOOP_COUNT 16
40 int alias_count; // for detecting runaway loops
41
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 void Cmd_Wait_f (void)
55 {
56 cmd_wait = true;
57 }
58
59
60 /*
61 =============================================================================
62
63 COMMAND BUFFER
64
65 =============================================================================
66 */
67
68 sizebuf_t cmd_text;
69 byte cmd_text_buf[8192];
70
71 byte defer_text_buf[8192];
72
73 /*
74 ============
75 Cbuf_Init
76 ============
77 */
78 void Cbuf_Init (void)
79 {
80 SZ_Init (&cmd_text, cmd_text_buf, sizeof(cmd_text_buf));
81 }
82
83 /*
84 ============
85 Cbuf_AddText
86
87 Adds command text at the end of the buffer
88 ============
89 */
90 void Cbuf_AddText (char *text)
91 {
92 int l;
93
94 l = strlen (text);
95
96 if (cmd_text.cursize + l >= cmd_text.maxsize)
97 {
98 Com_Printf ("Cbuf_AddText: overflow\n");
99 return;
100 }
101 SZ_Write (&cmd_text, text, strlen (text));
102 }
103
104
105 /*
106 ============
107 Cbuf_InsertText
108
109 Adds command text immediately after the current command
110 Adds a \n to the text
111 FIXME: actually change the command buffer to do less copying
112 ============
113 */
114 void Cbuf_InsertText (char *text)
115 {
116 char *temp;
117 int templen;
118
119 // copy off any commands still remaining in the exec buffer
120 templen = cmd_text.cursize;
121 if (templen)
122 {
123 temp = Z_Malloc (templen);
124 memcpy (temp, cmd_text.data, templen);
125 SZ_Clear (&cmd_text);
126 }
127 else
128 temp = NULL; // shut up compiler
129
130 // add the entire text of the file
131 Cbuf_AddText (text);
132
133 // add the copied off data
134 if (templen)
135 {
136 SZ_Write (&cmd_text, temp, templen);
137 Z_Free (temp);
138 }
139 }
140
141
142 /*
143 ============
144 Cbuf_CopyToDefer
145 ============
146 */
147 void Cbuf_CopyToDefer (void)
148 {
149 memcpy(defer_text_buf, cmd_text_buf, cmd_text.cursize);
150 defer_text_buf[cmd_text.cursize] = 0;
151 cmd_text.cursize = 0;
152 }
153
154 /*
155 ============
156 Cbuf_InsertFromDefer
157 ============
158 */
159 void Cbuf_InsertFromDefer (void)
160 {
161 Cbuf_InsertText (defer_text_buf);
162 defer_text_buf[0] = 0;
163 }
164
165
166 /*
167 ============
168 Cbuf_ExecuteText
169 ============
170 */
171 void Cbuf_ExecuteText (int exec_when, char *text)
172 {
173 switch (exec_when)
174 {
175 case EXEC_NOW:
176 Cmd_ExecuteString (text);
177 break;
178 case EXEC_INSERT:
179 Cbuf_InsertText (text);
180 break;
181 case EXEC_APPEND:
182 Cbuf_AddText (text);
183 break;
184 default:
185 Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
186 }
187 }
188
189 /*
190 ============
191 Cbuf_Execute
192 ============
193 */
194 void Cbuf_Execute (void)
195 {
196 int i;
197 char *text;
198 char line[1024];
199 int quotes;
200
201 alias_count = 0; // don't allow infinite alias loops
202
203 while (cmd_text.cursize)
204 {
205 // find a \n or ; line break
206 text = (char *)cmd_text.data;
207
208 quotes = 0;
209 for (i=0 ; i< cmd_text.cursize ; i++)
210 {
211 if (text[i] == '"')
212 quotes++;
213 if ( !(quotes&1) && text[i] == ';')
214 break; // don't break if inside a quoted string
215 if (text[i] == '\n')
216 break;
217 }
218
219
220 memcpy (line, text, i);
221 line[i] = 0;
222
223 // delete the text from the command buffer and move remaining commands down
224 // this is necessary because commands (exec, alias) can insert data at the
225 // beginning of the text buffer
226
227 if (i == cmd_text.cursize)
228 cmd_text.cursize = 0;
229 else
230 {
231 i++;
232 cmd_text.cursize -= i;
233 memmove (text, text+i, cmd_text.cursize);
234 }
235
236 // execute the command line
237 Cmd_ExecuteString (line);
238
239 if (cmd_wait)
240 {
241 // skip out while text still remains in buffer, leaving it
242 // for next frame
243 cmd_wait = false;
244 break;
245 }
246 }
247 }
248
249
250 /*
251 ===============
252 Cbuf_AddEarlyCommands
253
254 Adds command line parameters as script statements
255 Commands lead with a +, and continue until another +
256
257 Set commands are added early, so they are guaranteed to be set before
258 the client and server initialize for the first time.
259
260 Other commands are added late, after all initialization is complete.
261 ===============
262 */
263 void Cbuf_AddEarlyCommands (qboolean clear)
264 {
265 int i;
266 char *s;
267
268 for (i=0 ; i<COM_Argc() ; i++)
269 {
270 s = COM_Argv(i);
271 if (strcmp (s, "+set"))
272 continue;
273 Cbuf_AddText (va("set %s %s\n", COM_Argv(i+1), COM_Argv(i+2)));
274 if (clear)
275 {
276 COM_ClearArgv(i);
277 COM_ClearArgv(i+1);
278 COM_ClearArgv(i+2);
279 }
280 i+=2;
281 }
282 }
283
284 /*
285 =================
286 Cbuf_AddLateCommands
287
288 Adds command line parameters as script statements
289 Commands lead with a + and continue until another + or -
290 quake +vid_ref gl +map amlev1
291
292 Returns true if any late commands were added, which
293 will keep the demoloop from immediately starting
294 =================
295 */
296 qboolean Cbuf_AddLateCommands (void)
297 {
298 int i, j;
299 int s;
300 char *text, *build, c;
301 int argc;
302 qboolean ret;
303
304 // build the combined string to parse from
305 s = 0;
306 argc = COM_Argc();
307 for (i=1 ; i<argc ; i++)
308 {
309 s += strlen (COM_Argv(i)) + 1;
310 }
311 if (!s)
312 return false;
313
314 text = Z_Malloc (s+1);
315 text[0] = 0;
316 for (i=1 ; i<argc ; i++)
317 {
318 strcat (text,COM_Argv(i));
319 if (i != argc-1)
320 strcat (text, " ");
321 }
322
323 // pull out the commands
324 build = Z_Malloc (s+1);
325 build[0] = 0;
326
327 for (i=0 ; i<s-1 ; i++)
328 {
329 if (text[i] == '+')
330 {
331 i++;
332
333 for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
334 ;
335
336 c = text[j];
337 text[j] = 0;
338
339 strcat (build, text+i);
340 strcat (build, "\n");
341 text[j] = c;
342 i = j-1;
343 }
344 }
345
346 ret = (build[0] != 0);
347 if (ret)
348 Cbuf_AddText (build);
349
350 Z_Free (text);
351 Z_Free (build);
352
353 return ret;
354 }
355
356
357 /*
358 ==============================================================================
359
360 SCRIPT COMMANDS
361
362 ==============================================================================
363 */
364
365
366 /*
367 ===============
368 Cmd_Exec_f
369 ===============
370 */
371 void Cmd_Exec_f (void)
372 {
373 char *f, *f2;
374 int len;
375
376 if (Cmd_Argc () != 2)
377 {
378 Com_Printf ("exec <filename> : execute a script file\n");
379 return;
380 }
381
382 len = FS_LoadFile (Cmd_Argv(1), (void **)&f);
383 if (!f)
384 {
385 Com_Printf ("couldn't exec %s\n",Cmd_Argv(1));
386 return;
387 }
388 Com_Printf ("execing %s\n",Cmd_Argv(1));
389
390 // the file doesn't have a trailing 0, so we need to copy it off
391 f2 = Z_Malloc(len+1);
392 memcpy (f2, f, len);
393 f2[len] = 0;
394
395 Cbuf_InsertText (f2);
396
397 Z_Free (f2);
398 FS_FreeFile (f);
399 }
400
401
402 /*
403 ===============
404 Cmd_Echo_f
405
406 Just prints the rest of the line to the console
407 ===============
408 */
409 void Cmd_Echo_f (void)
410 {
411 int i;
412
413 for (i=1 ; i<Cmd_Argc() ; i++)
414 Com_Printf ("%s ",Cmd_Argv(i));
415 Com_Printf ("\n");
416 }
417
418 /*
419 ===============
420 Cmd_Alias_f
421
422 Creates a new command that executes a command string (possibly ; seperated)
423 ===============
424 */
425 void Cmd_Alias_f (void)
426 {
427 cmdalias_t *a;
428 char cmd[1024];
429 int i, c;
430 char *s;
431
432 if (Cmd_Argc() == 1)
433 {
434 Com_Printf ("Current alias commands:\n");
435 for (a = cmd_alias ; a ; a=a->next)
436 Com_Printf ("%s : %s\n", a->name, a->value);
437 return;
438 }
439
440 s = Cmd_Argv(1);
441 if (strlen(s) >= MAX_ALIAS_NAME)
442 {
443 Com_Printf ("Alias name is too long\n");
444 return;
445 }
446
447 // if the alias already exists, reuse it
448 for (a = cmd_alias ; a ; a=a->next)
449 {
450 if (!strcmp(s, a->name))
451 {
452 Z_Free (a->value);
453 break;
454 }
455 }
456
457 if (!a)
458 {
459 a = Z_Malloc (sizeof(cmdalias_t));
460 a->next = cmd_alias;
461 cmd_alias = a;
462 }
463 strcpy (a->name, s);
464
465 // copy the rest of the command line
466 cmd[0] = 0; // start out with a null string
467 c = Cmd_Argc();
468 for (i=2 ; i< c ; i++)
469 {
470 strcat (cmd, Cmd_Argv(i));
471 if (i != (c - 1))
472 strcat (cmd, " ");
473 }
474 strcat (cmd, "\n");
475
476 a->value = CopyString (cmd);
477 }
478
479 /*
480 =============================================================================
481
482 COMMAND EXECUTION
483
484 =============================================================================
485 */
486
487 typedef struct cmd_function_s
488 {
489 struct cmd_function_s *next;
490 char *name;
491 xcommand_t function;
492 } cmd_function_t;
493
494
495 static int cmd_argc;
496 static char *cmd_argv[MAX_STRING_TOKENS];
497 static char *cmd_null_string = "";
498 static char cmd_args[MAX_STRING_CHARS];
499
500 static cmd_function_t *cmd_functions; // possible commands to execute
501
502 /*
503 ============
504 Cmd_Argc
505 ============
506 */
507 int Cmd_Argc (void)
508 {
509 return cmd_argc;
510 }
511
512 /*
513 ============
514 Cmd_Argv
515 ============
516 */
517 char *Cmd_Argv (int arg)
518 {
519 if ( (unsigned)arg >= cmd_argc )
520 return cmd_null_string;
521 return cmd_argv[arg];
522 }
523
524 /*
525 ============
526 Cmd_Args
527
528 Returns a single string containing argv(1) to argv(argc()-1)
529 ============
530 */
531 char *Cmd_Args (void)
532 {
533 return cmd_args;
534 }
535
536
537 /*
538 ======================
539 Cmd_MacroExpandString
540 ======================
541 */
542 char *Cmd_MacroExpandString (char *text)
543 {
544 int i, j, count, len;
545 qboolean inquote;
546 char *scan;
547 static char expanded[MAX_STRING_CHARS];
548 char temporary[MAX_STRING_CHARS];
549 char *token, *start;
550
551 inquote = false;
552 scan = text;
553
554 len = strlen (scan);
555 if (len >= MAX_STRING_CHARS)
556 {
557 Com_Printf ("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
558 return NULL;
559 }
560
561 count = 0;
562
563 for (i=0 ; i<len ; i++)
564 {
565 if (scan[i] == '"')
566 inquote ^= 1;
567 if (inquote)
568 continue; // don't expand inside quotes
569 if (scan[i] != '$')
570 continue;
571 // scan out the complete macro
572 start = scan+i+1;
573 token = COM_Parse (&start);
574 if (!start)
575 continue;
576
577 token = Cvar_VariableString (token);
578
579 j = strlen(token);
580 len += j;
581 if (len >= MAX_STRING_CHARS)
582 {
583 Com_Printf ("Expanded line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
584 return NULL;
585 }
586
587 strncpy (temporary, scan, i);
588 strcpy (temporary+i, token);
589 strcpy (temporary+i+j, start);
590
591 strcpy (expanded, temporary);
592 scan = expanded;
593 i--;
594
595 if (++count == 100)
596 {
597 Com_Printf ("Macro expansion loop, discarded.\n");
598 return NULL;
599 }
600 }
601
602 if (inquote)
603 {
604 Com_Printf ("Line has unmatched quote, discarded.\n");
605 return NULL;
606 }
607
608 return scan;
609 }
610
611
612 /*
613 ============
614 Cmd_TokenizeString
615
616 Parses the given string into command line tokens.
617 $Cvars will be expanded unless they are in a quoted token
618 ============
619 */
620 void Cmd_TokenizeString (char *text, qboolean macroExpand)
621 {
622 int i;
623 char *com_token;
624
625 // clear the args from the last string
626 for (i=0 ; i<cmd_argc ; i++)
627 Z_Free (cmd_argv[i]);
628
629 cmd_argc = 0;
630 cmd_args[0] = 0;
631
632 // macro expand the text
633 if (macroExpand)
634 text = Cmd_MacroExpandString (text);
635 if (!text)
636 return;
637
638 while (1)
639 {
640 // skip whitespace up to a /n
641 while (*text && *text <= ' ' && *text != '\n')
642 {
643 text++;
644 }
645
646 if (*text == '\n')
647 { // a newline seperates commands in the buffer
648 text++;
649 break;
650 }
651
652 if (!*text)
653 return;
654
655 // set cmd_args to everything after the first arg
656 if (cmd_argc == 1)
657 {
658 int l;
659
660 strcpy (cmd_args, text);
661
662 // strip off any trailing whitespace
663 l = strlen(cmd_args) - 1;
664 for ( ; l >= 0 ; l--)
665 if (cmd_args[l] <= ' ')
666 cmd_args[l] = 0;
667 else
668 break;
669 }
670
671 com_token = COM_Parse (&text);
672 if (!text)
673 return;
674
675 if (cmd_argc < MAX_STRING_TOKENS)
676 {
677 cmd_argv[cmd_argc] = Z_Malloc (strlen(com_token)+1);
678 strcpy (cmd_argv[cmd_argc], com_token);
679 cmd_argc++;
680 }
681 }
682
683 }
684
685
686 /*
687 ============
688 Cmd_AddCommand
689 ============
690 */
691 void Cmd_AddCommand (char *cmd_name, xcommand_t function)
692 {
693 cmd_function_t *cmd;
694
695 // fail if the command is a variable name
696 if (Cvar_VariableString(cmd_name)[0])
697 {
698 Com_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
699 return;
700 }
701
702 // fail if the command already exists
703 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
704 {
705 if (!strcmp (cmd_name, cmd->name))
706 {
707 Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
708 return;
709 }
710 }
711
712 cmd = Z_Malloc (sizeof(cmd_function_t));
713 cmd->name = cmd_name;
714 cmd->function = function;
715 cmd->next = cmd_functions;
716 cmd_functions = cmd;
717 }
718
719 /*
720 ============
721 Cmd_RemoveCommand
722 ============
723 */
724 void Cmd_RemoveCommand (char *cmd_name)
725 {
726 cmd_function_t *cmd, **back;
727
728 back = &cmd_functions;
729 while (1)
730 {
731 cmd = *back;
732 if (!cmd)
733 {
734 Com_Printf ("Cmd_RemoveCommand: %s not added\n", cmd_name);
735 return;
736 }
737 if (!strcmp (cmd_name, cmd->name))
738 {
739 *back = cmd->next;
740 Z_Free (cmd);
741 return;
742 }
743 back = &cmd->next;
744 }
745 }
746
747 /*
748 ============
749 Cmd_Exists
750 ============
751 */
752 qboolean Cmd_Exists (char *cmd_name)
753 {
754 cmd_function_t *cmd;
755
756 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
757 {
758 if (!strcmp (cmd_name,cmd->name))
759 return true;
760 }
761
762 return false;
763 }
764
765
766
767 /*
768 ============
769 Cmd_CompleteCommand
770 ============
771 */
772 char *Cmd_CompleteCommand (char *partial)
773 {
774 cmd_function_t *cmd;
775 int len;
776 cmdalias_t *a;
777
778 len = strlen(partial);
779
780 if (!len)
781 return NULL;
782
783 // check for exact match
784 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
785 if (!strcmp (partial,cmd->name))
786 return cmd->name;
787 for (a=cmd_alias ; a ; a=a->next)
788 if (!strcmp (partial, a->name))
789 return a->name;
790
791 // check for partial match
792 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
793 if (!strncmp (partial,cmd->name, len))
794 return cmd->name;
795 for (a=cmd_alias ; a ; a=a->next)
796 if (!strncmp (partial, a->name, len))
797 return a->name;
798
799 return NULL;
800 }
801
802
803 /*
804 ============
805 Cmd_ExecuteString
806
807 A complete command line has been parsed, so try to execute it
808 FIXME: lookupnoadd the token to speed search?
809 ============
810 */
811 void Cmd_ExecuteString (char *text)
812 {
813 cmd_function_t *cmd;
814 cmdalias_t *a;
815
816 Cmd_TokenizeString (text, true);
817
818 // execute the command line
819 if (!Cmd_Argc())
820 return; // no tokens
821
822 // check functions
823 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
824 {
825 if (!Q_strcasecmp (cmd_argv[0],cmd->name))
826 {
827 if (!cmd->function)
828 { // forward to server command
829 Cmd_ExecuteString (va("cmd %s", text));
830 }
831 else
832 cmd->function ();
833 return;
834 }
835 }
836
837 // check alias
838 for (a=cmd_alias ; a ; a=a->next)
839 {
840 if (!Q_strcasecmp (cmd_argv[0], a->name))
841 {
842 if (++alias_count == ALIAS_LOOP_COUNT)
843 {
844 Com_Printf ("ALIAS_LOOP_COUNT\n");
845 return;
846 }
847 Cbuf_InsertText (a->value);
848 return;
849 }
850 }
851
852 // check cvars
853 if (Cvar_Command ())
854 return;
855
856 // send it as a server command if we are connected
857 Cmd_ForwardToServer ();
858 }
859
860 /*
861 ============
862 Cmd_List_f
863 ============
864 */
865 void Cmd_List_f (void)
866 {
867 cmd_function_t *cmd;
868 int i;
869
870 i = 0;
871 for (cmd=cmd_functions ; cmd ; cmd=cmd->next, i++)
872 Com_Printf ("%s\n", cmd->name);
873 Com_Printf ("%i commands\n", i);
874 }
875
876 /*
877 ============
878 Cmd_Init
879 ============
880 */
881 void Cmd_Init (void)
882 {
883 //
884 // register our commands
885 //
886 Cmd_AddCommand ("cmdlist",Cmd_List_f);
887 Cmd_AddCommand ("exec",Cmd_Exec_f);
888 Cmd_AddCommand ("echo",Cmd_Echo_f);
889 Cmd_AddCommand ("alias",Cmd_Alias_f);
890 Cmd_AddCommand ("wait", Cmd_Wait_f);
891 }
892
893