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