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 */
Cmd_Wait_f(void)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
70 byte cmd_text_buf[32768]; //8192
71 byte defer_text_buf[32768]; //8192
72
73 /*
74 ============
75 Cbuf_Init
76 ============
77 */
Cbuf_Init(void)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 */
Cbuf_AddText(char * text)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 */
Cbuf_InsertText(char * text)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 */
Cbuf_CopyToDefer(void)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 */
Cbuf_InsertFromDefer(void)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 */
Cbuf_ExecuteText(int exec_when,char * text)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 */
Cbuf_Execute(void)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 */
Cbuf_AddEarlyCommands(qboolean clear)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 */
Cbuf_AddLateCommands(void)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 */
Cmd_Exec_f(void)371 void Cmd_Exec_f (void)
372 {
373 char *scriptname;
374 char *f, *f2;
375 int len;
376
377 if (Cmd_Argc () != 2)
378 {
379 Com_Printf ("exec <filename> : execute a script file\n");
380 return;
381 }
382
383 scriptname = Cmd_Argv(1);
384
385 //AutoFind if now suffix
386 if (!strstr (scriptname, ".")) //add .cfg for easy use
387 {
388 scriptname = va("%s.cfg", Cmd_Argv(1));
389 if (FS_LoadFile (scriptname, NULL) == -1)
390 {
391 scriptname = va("%s.rc", Cmd_Argv(1));
392 if (FS_LoadFile (scriptname, NULL) == -1)
393 {
394 scriptname = va("%s.c", Cmd_Argv(1));
395 if (FS_LoadFile (scriptname, NULL) == -1)
396 {
397 Com_Printf ("couldn't exec %s\n", Cmd_Argv(1));
398 return;
399 }
400 }
401 }
402 }
403
404 len = FS_LoadFile (scriptname, (void **)&f);
405 if (!f)
406 {
407 Com_Printf ("couldn't exec %s\n",scriptname);
408 return;
409 }
410 Com_Printf ("execing %s\n",scriptname);
411
412 // the file doesn't have a trailing 0, so we need to copy it off
413 f2 = Z_Malloc(len+1);
414 memcpy (f2, f, len);
415 f2[len] = 0;
416
417 Cbuf_InsertText (f2);
418
419 Z_Free (f2);
420 FS_FreeFile (f);
421 }
422
423
424 /*
425 ===============
426 Cmd_Echo_f
427
428 Just prints the rest of the line to the console
429 ===============
430 */
Cmd_Echo_f(void)431 void Cmd_Echo_f (void)
432 {
433 int i;
434
435 for (i=1 ; i<Cmd_Argc() ; i++)
436 Com_Printf ("%s ",Cmd_Argv(i));
437 Com_Printf ("\n");
438 }
439
440 /*
441 ===============
442 Cmd_Alias_f
443
444 Creates a new command that executes a command string (possibly ; seperated)
445 ===============
446 */
Cmd_Alias_f(void)447 void Cmd_Alias_f (void)
448 {
449 cmdalias_t *a;
450 char cmd[1024];
451 int i, c;
452 char *s;
453
454 if (Cmd_Argc() == 1)
455 {
456 Com_Printf ("Current alias commands:\n");
457 for (a = cmd_alias ; a ; a=a->next)
458 Com_Printf ("%s : %s\n", a->name, a->value);
459 return;
460 }
461
462 s = Cmd_Argv(1);
463 if (strlen(s) >= MAX_ALIAS_NAME)
464 {
465 Com_Printf ("Alias name is too long\n");
466 return;
467 }
468
469 // if the alias already exists, reuse it
470 for (a = cmd_alias ; a ; a=a->next)
471 {
472 if (!strcmp(s, a->name))
473 {
474 Z_Free (a->value);
475 break;
476 }
477 }
478
479 if (!a)
480 {
481 a = Z_Malloc (sizeof(cmdalias_t));
482 a->next = cmd_alias;
483 cmd_alias = a;
484 }
485 strcpy (a->name, s);
486
487 // copy the rest of the command line
488 cmd[0] = 0; // start out with a null string
489 c = Cmd_Argc();
490 for (i=2 ; i< c ; i++)
491 {
492 strcat (cmd, Cmd_Argv(i));
493 if (i != (c - 1))
494 strcat (cmd, " ");
495 }
496 strcat (cmd, "\n");
497
498 a->value = CopyString (cmd);
499 }
500
501 /*
502 =============================================================================
503
504 COMMAND EXECUTION
505
506 =============================================================================
507 */
508
509 typedef struct cmd_function_s
510 {
511 struct cmd_function_s *next;
512 char *name;
513 xcommand_t function;
514 } cmd_function_t;
515
516
517 static int cmd_argc;
518 static char *cmd_argv[MAX_STRING_TOKENS];
519 static char *cmd_null_string = "";
520 static char cmd_args[MAX_STRING_CHARS];
521
522 static cmd_function_t *cmd_functions; // possible commands to execute
523
524 /*
525 ============
526 Cmd_Argc
527 ============
528 */
Cmd_Argc(void)529 int Cmd_Argc (void)
530 {
531 return cmd_argc;
532 }
533
534 /*
535 ============
536 Cmd_Argv
537 ============
538 */
Cmd_Argv(int arg)539 char *Cmd_Argv (int arg)
540 {
541 if ( (unsigned)arg >= cmd_argc )
542 return cmd_null_string;
543 return cmd_argv[arg];
544 }
545
546 /*
547 ============
548 Cmd_Args
549
550 Returns a single string containing argv(1) to argv(argc()-1)
551 ============
552 */
Cmd_Args(void)553 char *Cmd_Args (void)
554 {
555 return cmd_args;
556 }
557
558
559 /*
560 ======================
561 Cmd_MacroExpandString
562 ======================
563 */
Cmd_MacroExpandString(char * text)564 char *Cmd_MacroExpandString (char *text)
565 {
566 int i, j, count, len;
567 qboolean inquote;
568 char *scan;
569 static char expanded[MAX_STRING_CHARS];
570 char temporary[MAX_STRING_CHARS];
571 char *token, *start;
572
573 inquote = false;
574 scan = text;
575
576 len = strlen (scan);
577 if (len >= MAX_STRING_CHARS)
578 {
579 Com_Printf ("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
580 return NULL;
581 }
582
583 count = 0;
584
585 for (i=0 ; i<len ; i++)
586 {
587 if (scan[i] == '"')
588 inquote ^= 1;
589 if (inquote)
590 continue; // don't expand inside quotes
591 if (scan[i] != '$')
592 continue;
593 // scan out the complete macro
594 start = scan+i+1;
595 token = COM_Parse (&start);
596 if (!start)
597 continue;
598
599 token = Cvar_VariableString (token);
600
601 j = strlen(token);
602 len += j;
603 if (len >= MAX_STRING_CHARS)
604 {
605 Com_Printf ("Expanded line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
606 return NULL;
607 }
608
609 strncpy (temporary, scan, i);
610 strcpy (temporary+i, token);
611 strcpy (temporary+i+j, start);
612
613 strcpy (expanded, temporary);
614 scan = expanded;
615 i--;
616
617 if (++count == 100)
618 {
619 Com_Printf ("Macro expansion loop, discarded.\n");
620 return NULL;
621 }
622 }
623
624 if (inquote)
625 {
626 Com_Printf ("Line has unmatched quote, discarded.\n");
627 return NULL;
628 }
629
630 return scan;
631 }
632
633
634 /*
635 ============
636 Cmd_TokenizeString
637
638 Parses the given string into command line tokens.
639 $Cvars will be expanded unless they are in a quoted token
640 ============
641 */
Cmd_TokenizeString(char * text,qboolean macroExpand)642 void Cmd_TokenizeString (char *text, qboolean macroExpand)
643 {
644 int i;
645 char *com_token;
646
647 // clear the args from the last string
648 for (i=0 ; i<cmd_argc ; i++)
649 Z_Free (cmd_argv[i]);
650
651 cmd_argc = 0;
652 cmd_args[0] = 0;
653
654 // macro expand the text
655 if (macroExpand)
656 text = Cmd_MacroExpandString (text);
657 if (!text)
658 return;
659
660 while (1)
661 {
662 // skip whitespace up to a /n
663 while (*text && *text <= ' ' && *text != '\n')
664 {
665 text++;
666 }
667
668 if (*text == '\n')
669 { // a newline seperates commands in the buffer
670 text++;
671 break;
672 }
673
674 if (!*text)
675 return;
676
677 // set cmd_args to everything after the first arg
678 if (cmd_argc == 1)
679 {
680 int l;
681
682 strcpy (cmd_args, text);
683
684 // strip off any trailing whitespace
685 l = strlen(cmd_args) - 1;
686 for ( ; l >= 0 ; l--)
687 if (cmd_args[l] <= ' ')
688 cmd_args[l] = 0;
689 else
690 break;
691 }
692
693 com_token = COM_Parse (&text);
694 if (!text)
695 return;
696
697 if (cmd_argc < MAX_STRING_TOKENS)
698 {
699 cmd_argv[cmd_argc] = Z_Malloc (strlen(com_token)+1);
700 strcpy (cmd_argv[cmd_argc], com_token);
701 cmd_argc++;
702 }
703 }
704
705 }
706
707
708 /*
709 ============
710 Cmd_AddCommand
711 ============
712 */
Cmd_AddCommand(char * cmd_name,xcommand_t function)713 void Cmd_AddCommand (char *cmd_name, xcommand_t function)
714 {
715 cmd_function_t *cmd;
716
717 // fail if the command is a variable name
718 if (Cvar_VariableString(cmd_name)[0])
719 {
720 Com_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
721 return;
722 }
723
724 // fail if the command already exists
725 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
726 {
727 if (!strcmp (cmd_name, cmd->name))
728 {
729 Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
730 return;
731 }
732 }
733
734 cmd = Z_Malloc (sizeof(cmd_function_t));
735 cmd->name = cmd_name;
736 cmd->function = function;
737 cmd->next = cmd_functions;
738 cmd_functions = cmd;
739 }
740
741 /*
742 ============
743 Cmd_RemoveCommand
744 ============
745 */
Cmd_RemoveCommand(char * cmd_name)746 void Cmd_RemoveCommand (char *cmd_name)
747 {
748 cmd_function_t *cmd, **back;
749
750 back = &cmd_functions;
751 while (1)
752 {
753 cmd = *back;
754 if (!cmd)
755 {
756 Com_Printf ("Cmd_RemoveCommand: %s not added\n", cmd_name);
757 return;
758 }
759 if (!strcmp (cmd_name, cmd->name))
760 {
761 *back = cmd->next;
762 Z_Free (cmd);
763 return;
764 }
765 back = &cmd->next;
766 }
767 }
768
769 /*
770 ============
771 Cmd_Exists
772 ============
773 */
Cmd_Exists(char * cmd_name)774 qboolean Cmd_Exists (char *cmd_name)
775 {
776 cmd_function_t *cmd;
777
778 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
779 {
780 if (!strcmp (cmd_name,cmd->name))
781 return true;
782 }
783
784 return false;
785 }
786
787
788
789 /*
790 ============
791 Cmd_CompleteCommand
792 ============
793 */
794 char retval[256];
Cmd_CompleteCommand(char * partial)795 char *Cmd_CompleteCommand (char *partial)
796 {
797 cmd_function_t *cmd;
798 int len,i,o,p;
799 cmdalias_t *a;
800 cvar_t *cvar;
801 char *pmatch[1024];
802 qboolean diff = false;
803
804 len = strlen(partial);
805
806 if (!len)
807 return NULL;
808
809 // check for exact match
810 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
811 if (!_stricmp (partial,cmd->name))
812 return cmd->name;
813 for (a=cmd_alias ; a ; a=a->next)
814 if (!_stricmp (partial, a->name))
815 return a->name;
816 for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
817 if (!_stricmp (partial,cvar->name))
818 return cvar->name;
819
820 for (i=0; i<1024; i++)
821 pmatch[i]=NULL;
822 i=0;
823
824 // check for partial match
825 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
826 if (!_strnicmp (partial,cmd->name, len)) {
827 pmatch[i]=cmd->name;
828 i++;
829 }
830 for (a=cmd_alias ; a ; a=a->next)
831 if (!_strnicmp (partial, a->name, len)) {
832 pmatch[i]=a->name;
833 i++;
834 }
835 for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
836 if (!_strnicmp (partial,cvar->name, len)) {
837 pmatch[i]=cvar->name;
838 i++;
839 }
840
841 if (i) {
842 if (i == 1)
843 return pmatch[0];
844
845 Com_Printf("]/%s\n",partial);
846 for (o=0; o<i; o++)
847 Com_Printf(" %s\n",pmatch[o]);
848
849 strcpy(retval,""); p=0;
850 while (!diff && p < 256) {
851 retval[p]=pmatch[0][p];
852 for (o=0; o<i; o++) {
853 if (p > strlen(pmatch[o]))
854 continue;
855 if (retval[p] != pmatch[o][p]) {
856 retval[p] = 0;
857 diff=false;
858 }
859 }
860 p++;
861 }
862 return retval;
863 }
864
865 return NULL;
866 }
867
Cmd_IsComplete(char * command)868 qboolean Cmd_IsComplete (char *command)
869 {
870 cmd_function_t *cmd;
871 cmdalias_t *a;
872 cvar_t *cvar;
873
874 // check for exact match
875 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
876 if (!_stricmp (command,cmd->name))
877 return true;
878 for (a=cmd_alias ; a ; a=a->next)
879 if (!_stricmp (command, a->name))
880 return true;
881 for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
882 if (!_stricmp (command,cvar->name))
883 return true;
884
885 return false;
886 }
887
888 /*
889 ============
890 Cmd_ExecuteString
891
892 A complete command line has been parsed, so try to execute it
893 FIXME: lookupnoadd the token to speed search?
894 ============
895 */
Cmd_ExecuteString(char * text)896 void Cmd_ExecuteString (char *text)
897 {
898 cmd_function_t *cmd;
899 cmdalias_t *a;
900
901 Cmd_TokenizeString (text, true);
902
903 // execute the command line
904 if (!Cmd_Argc())
905 return; // no tokens
906
907 // check functions
908 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
909 {
910 if (!Q_strcasecmp (cmd_argv[0],cmd->name))
911 {
912 if (!cmd->function)
913 { // forward to server command
914 Cmd_ExecuteString (va("cmd %s", text));
915 }
916 else
917 cmd->function ();
918 return;
919 }
920 }
921
922 // check alias
923 for (a=cmd_alias ; a ; a=a->next)
924 {
925 if (!Q_strcasecmp (cmd_argv[0], a->name))
926 {
927 if (++alias_count == ALIAS_LOOP_COUNT)
928 {
929 Com_Printf ("ALIAS_LOOP_COUNT\n");
930 return;
931 }
932 Cbuf_InsertText (a->value);
933 return;
934 }
935 }
936
937 // check cvars
938 if (Cvar_Command ())
939 return;
940
941 // send it as a server command if we are connected
942 Cmd_ForwardToServer ();
943 }
944
945 /*
946 ============
947 Cmd_List_f
948 ============
949 */
Cmd_List_f(void)950 void Cmd_List_f (void)
951 {
952 cmd_function_t *cmd;
953 int i;
954
955 i = 0;
956 for (cmd=cmd_functions ; cmd ; cmd=cmd->next, i++)
957 Com_Printf ("%s\n", cmd->name);
958 Com_Printf ("%i commands\n", i);
959 }
960
961 /*
962 ============
963 Cmd_Init
964 ============
965 */
Cmd_Init(void)966 void Cmd_Init (void)
967 {
968 //
969 // register our commands
970 //
971 Cmd_AddCommand ("cmdlist",Cmd_List_f);
972 Cmd_AddCommand ("exec",Cmd_Exec_f);
973 Cmd_AddCommand ("echo",Cmd_Echo_f);
974 Cmd_AddCommand ("alias",Cmd_Alias_f);
975 Cmd_AddCommand ("wait", Cmd_Wait_f);
976 }
977
978