|
|
1.1 root 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: qboolean cmd_wait;
38:
39: cvar_t cl_warncmd = {"cl_warncmd", "0"};
40:
41: //=============================================================================
42:
43: /*
44: ============
45: Cmd_Wait_f
46:
47: Causes execution of the remainder of the command buffer to be delayed until
48: next frame. This allows commands like:
49: bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
50: ============
51: */
52: void Cmd_Wait_f (void)
53: {
54: cmd_wait = true;
55: }
56:
57: /*
58: =============================================================================
59:
60: COMMAND BUFFER
61:
62: =============================================================================
63: */
64:
65: sizebuf_t cmd_text;
66: byte cmd_text_buf[8192];
67:
68: /*
69: ============
70: Cbuf_Init
71: ============
72: */
73: void Cbuf_Init (void)
74: {
75: cmd_text.data = cmd_text_buf;
76: cmd_text.maxsize = sizeof(cmd_text_buf);
77: }
78:
79: /*
80: ============
81: Cbuf_AddText
82:
83: Adds command text at the end of the buffer
84: ============
85: */
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: SZ_Write (&cmd_text, text, Q_strlen (text));
98: }
99:
100:
101: /*
102: ============
103: Cbuf_InsertText
104:
105: Adds command text immediately after the current command
106: Adds a \n to the text
107: FIXME: actually change the command buffer to do less copying
108: ============
109: */
110: void Cbuf_InsertText (char *text)
111: {
112: char *temp;
113: int templen;
114:
115: // copy off any commands still remaining in the exec buffer
116: templen = cmd_text.cursize;
117: if (templen)
118: {
119: temp = Z_Malloc (templen);
120: Q_memcpy (temp, cmd_text.data, templen);
121: SZ_Clear (&cmd_text);
122: }
123: else
124: temp = NULL; // shut up compiler
125:
126: // add the entire text of the file
127: Cbuf_AddText (text);
128: SZ_Write (&cmd_text, "\n", 1);
129: // add the copied off data
130: if (templen)
131: {
132: SZ_Write (&cmd_text, temp, templen);
133: Z_Free (temp);
134: }
135: }
136:
137: /*
138: ============
139: Cbuf_Execute
140: ============
141: */
142: void Cbuf_Execute (void)
143: {
144: int i;
145: char *text;
146: char line[1024];
147: int quotes;
148:
149: while (cmd_text.cursize)
150: {
151: // find a \n or ; line break
152: text = (char *)cmd_text.data;
153:
154: quotes = 0;
155: for (i=0 ; i< cmd_text.cursize ; i++)
156: {
157: if (text[i] == '"')
158: quotes++;
159: if ( !(quotes&1) && text[i] == ';')
160: break; // don't break if inside a quoted string
161: if (text[i] == '\n')
162: break;
163: }
164:
165:
166: memcpy (line, text, i);
167: line[i] = 0;
168:
169: // delete the text from the command buffer and move remaining commands down
170: // this is necessary because commands (exec, alias) can insert data at the
171: // beginning of the text buffer
172:
173: if (i == cmd_text.cursize)
174: cmd_text.cursize = 0;
175: else
176: {
177: i++;
178: cmd_text.cursize -= i;
179: Q_memcpy (text, text+i, cmd_text.cursize);
180: }
181:
182: // execute the command line
183: Cmd_ExecuteString (line);
184:
185: if (cmd_wait)
186: { // skip out while text still remains in buffer, leaving it
187: // for next frame
188: cmd_wait = false;
189: break;
190: }
191: }
192: }
193:
194: /*
195: ==============================================================================
196:
197: SCRIPT COMMANDS
198:
199: ==============================================================================
200: */
201:
202: /*
203: ===============
204: Cmd_StuffCmds_f
205:
206: Adds command line parameters as script statements
207: Commands lead with a +, and continue until a - or another +
208: quake +prog jctest.qp +cmd amlev1
209: quake -nosound +cmd amlev1
210: ===============
211: */
212: void Cmd_StuffCmds_f (void)
213: {
214: int i, j;
215: int s;
216: char *text, *build, c;
217:
218: // build the combined string to parse from
219: s = 0;
220: for (i=1 ; i<com_argc ; i++)
221: {
222: if (!com_argv[i])
223: continue; // NEXTSTEP nulls out -NXHost
224: s += Q_strlen (com_argv[i]) + 1;
225: }
226: if (!s)
227: return;
228:
229: text = Z_Malloc (s+1);
230: text[0] = 0;
231: for (i=1 ; i<com_argc ; i++)
232: {
233: if (!com_argv[i])
234: continue; // NEXTSTEP nulls out -NXHost
235: Q_strcat (text,com_argv[i]);
236: if (i != com_argc-1)
237: Q_strcat (text, " ");
238: }
239:
240: // pull out the commands
241: build = Z_Malloc (s+1);
242: build[0] = 0;
243:
244: for (i=0 ; i<s-1 ; i++)
245: {
246: if (text[i] == '+')
247: {
248: i++;
249:
250: for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
251: ;
252:
253: c = text[j];
254: text[j] = 0;
255:
256: Q_strcat (build, text+i);
257: Q_strcat (build, "\n");
258: text[j] = c;
259: i = j-1;
260: }
261: }
262:
263: if (build[0])
264: Cbuf_InsertText (build);
265:
266: Z_Free (text);
267: Z_Free (build);
268: }
269:
270:
271: /*
272: ===============
273: Cmd_Exec_f
274: ===============
275: */
276: void Cmd_Exec_f (void)
277: {
278: char *f;
279: int mark;
280:
281: if (Cmd_Argc () != 2)
282: {
283: Con_Printf ("exec <filename> : execute a script file\n");
284: return;
285: }
286:
287: // FIXME: is this safe freeing the hunk here???
288: mark = Hunk_LowMark ();
289: f = (char *)COM_LoadHunkFile (Cmd_Argv(1));
290: if (!f)
291: {
292: Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
293: return;
294: }
295: if (!Cvar_Command () && (cl_warncmd.value || developer.value))
296: Con_Printf ("execing %s\n",Cmd_Argv(1));
297:
298: Cbuf_InsertText (f);
299: Hunk_FreeToLowMark (mark);
300: }
301:
302:
303: /*
304: ===============
305: Cmd_Echo_f
306:
307: Just prints the rest of the line to the console
308: ===============
309: */
310: void Cmd_Echo_f (void)
311: {
312: int i;
313:
314: for (i=1 ; i<Cmd_Argc() ; i++)
315: Con_Printf ("%s ",Cmd_Argv(i));
316: Con_Printf ("\n");
317: }
318:
319: /*
320: ===============
321: Cmd_Alias_f
322:
323: Creates a new command that executes a command string (possibly ; seperated)
324: ===============
325: */
326:
327: char *CopyString (char *in)
328: {
329: char *out;
330:
331: out = Z_Malloc (strlen(in)+1);
332: strcpy (out, in);
333: return out;
334: }
335:
336: void Cmd_Alias_f (void)
337: {
338: cmdalias_t *a;
339: char cmd[1024];
340: int i, c;
341: char *s;
342:
343: if (Cmd_Argc() == 1)
344: {
345: Con_Printf ("Current alias commands:\n");
346: for (a = cmd_alias ; a ; a=a->next)
347: Con_Printf ("%s : %s\n", a->name, a->value);
348: return;
349: }
350:
351: s = Cmd_Argv(1);
352: if (strlen(s) >= MAX_ALIAS_NAME)
353: {
354: Con_Printf ("Alias name is too long\n");
355: return;
356: }
357:
358: // if the alias allready exists, reuse it
359: for (a = cmd_alias ; a ; a=a->next)
360: {
361: if (!strcmp(s, a->name))
362: {
363: Z_Free (a->value);
364: break;
365: }
366: }
367:
368: if (!a)
369: {
370: a = Z_Malloc (sizeof(cmdalias_t));
371: a->next = cmd_alias;
372: cmd_alias = a;
373: }
374: strcpy (a->name, s);
375:
376: // copy the rest of the command line
377: cmd[0] = 0; // start out with a null string
378: c = Cmd_Argc();
379: for (i=2 ; i< c ; i++)
380: {
381: strcat (cmd, Cmd_Argv(i));
382: if (i != c)
383: strcat (cmd, " ");
384: }
385: strcat (cmd, "\n");
386:
387: a->value = CopyString (cmd);
388: }
389:
390: /*
391: =============================================================================
392:
393: COMMAND EXECUTION
394:
395: =============================================================================
396: */
397:
398: typedef struct cmd_function_s
399: {
400: struct cmd_function_s *next;
401: char *name;
402: xcommand_t function;
403: } cmd_function_t;
404:
405:
406: #define MAX_ARGS 80
407:
408: static int cmd_argc;
409: static char *cmd_argv[MAX_ARGS];
410: static char *cmd_null_string = "";
411: static char *cmd_args = NULL;
412:
413:
414:
415: static cmd_function_t *cmd_functions; // possible commands to execute
416:
417: /*
418: ============
419: Cmd_Argc
420: ============
421: */
422: int Cmd_Argc (void)
423: {
424: return cmd_argc;
425: }
426:
427: /*
428: ============
429: Cmd_Argv
430: ============
431: */
432: char *Cmd_Argv (int arg)
433: {
434: if ( arg >= cmd_argc )
435: return cmd_null_string;
436: return cmd_argv[arg];
437: }
438:
439: /*
440: ============
441: Cmd_Args
442:
443: Returns a single string containing argv(1) to argv(argc()-1)
444: ============
445: */
446: char *Cmd_Args (void)
447: {
448: if (!cmd_args)
449: return "";
450: return cmd_args;
451: }
452:
453:
454: /*
455: ============
456: Cmd_TokenizeString
457:
458: Parses the given string into command line tokens.
459: ============
460: */
461: void Cmd_TokenizeString (char *text)
462: {
463: int i;
464:
465: // clear the args from the last string
466: for (i=0 ; i<cmd_argc ; i++)
467: Z_Free (cmd_argv[i]);
468:
469: cmd_argc = 0;
470: cmd_args = NULL;
471:
472: while (1)
473: {
474: // skip whitespace up to a /n
475: while (*text && *text <= ' ' && *text != '\n')
476: {
477: text++;
478: }
479:
480: if (*text == '\n')
481: { // a newline seperates commands in the buffer
482: text++;
483: break;
484: }
485:
486: if (!*text)
487: return;
488:
489: if (cmd_argc == 1)
490: cmd_args = text;
491:
492: text = COM_Parse (text);
493: if (!text)
494: return;
495:
496: if (cmd_argc < MAX_ARGS)
497: {
498: cmd_argv[cmd_argc] = Z_Malloc (Q_strlen(com_token)+1);
499: Q_strcpy (cmd_argv[cmd_argc], com_token);
500: cmd_argc++;
501: }
502: }
503:
504: }
505:
506:
507: /*
508: ============
509: Cmd_AddCommand
510: ============
511: */
512: void Cmd_AddCommand (char *cmd_name, xcommand_t function)
513: {
514: cmd_function_t *cmd;
515:
516: if (host_initialized) // because hunk allocation would get stomped
517: Sys_Error ("Cmd_AddCommand after host_initialized");
518:
519: // fail if the command is a variable name
520: if (Cvar_VariableString(cmd_name)[0])
521: {
522: Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
523: return;
524: }
525:
526: // fail if the command already exists
527: for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
528: {
529: if (!Q_strcmp (cmd_name, cmd->name))
530: {
531: Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
532: return;
533: }
534: }
535:
536: cmd = Hunk_Alloc (sizeof(cmd_function_t));
537: cmd->name = cmd_name;
538: cmd->function = function;
539: cmd->next = cmd_functions;
540: cmd_functions = cmd;
541: }
542:
543: /*
544: ============
545: Cmd_Exists
546: ============
547: */
548: qboolean Cmd_Exists (char *cmd_name)
549: {
550: cmd_function_t *cmd;
551:
552: for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
553: {
554: if (!Q_strcmp (cmd_name,cmd->name))
555: return true;
556: }
557:
558: return false;
559: }
560:
561:
562:
563: /*
564: ============
565: Cmd_CompleteCommand
566: ============
567: */
568: char *Cmd_CompleteCommand (char *partial)
569: {
570: cmd_function_t *cmd;
571: int len;
572: cmdalias_t *a;
573:
574: len = Q_strlen(partial);
575:
576: if (!len)
577: return NULL;
578:
579: // check for exact match
580: for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
581: if (!strcmp (partial,cmd->name))
582: return cmd->name;
583: for (a=cmd_alias ; a ; a=a->next)
584: if (!strcmp (partial, a->name))
585: return a->name;
586:
587: // check for partial match
588: for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
589: if (!strncmp (partial,cmd->name, len))
590: return cmd->name;
591: for (a=cmd_alias ; a ; a=a->next)
592: if (!strncmp (partial, a->name, len))
593: return a->name;
594:
595: return NULL;
596: }
597:
598: #ifndef SERVERONLY // FIXME
599: /*
600: ===================
601: Cmd_ForwardToServer
602:
603: adds the current command line as a clc_stringcmd to the client message.
604: things like godmode, noclip, etc, are commands directed to the server,
605: so when they are typed in at the console, they will need to be forwarded.
606: ===================
607: */
608: void Cmd_ForwardToServer (void)
609: {
610: if (cls.state == ca_disconnected)
611: {
612: Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
613: return;
614: }
615:
616: if (cls.demoplayback)
617: return; // not really connected
618:
619: MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
620: SZ_Print (&cls.netchan.message, Cmd_Argv(0));
621: if (Cmd_Argc() > 1)
622: {
623: SZ_Print (&cls.netchan.message, " ");
624: SZ_Print (&cls.netchan.message, Cmd_Args());
625: }
626: }
627:
628: // don't forward the first argument
629: void Cmd_ForwardToServer_f (void)
630: {
631: if (cls.state == ca_disconnected)
632: {
633: Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
634: return;
635: }
636:
637: if (Q_strcasecmp(Cmd_Argv(1), "snap") == 0) {
638: Cbuf_InsertText ("snap\n");
639: return;
640: }
641:
642: if (cls.demoplayback)
643: return; // not really connected
644:
645: if (Cmd_Argc() > 1)
646: {
647: MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
648: SZ_Print (&cls.netchan.message, Cmd_Args());
649: }
650: }
651: #else
652: void Cmd_ForwardToServer (void)
653: {
654: }
655: #endif
656:
657: /*
658: ============
659: Cmd_ExecuteString
660:
661: A complete command line has been parsed, so try to execute it
662: FIXME: lookupnoadd the token to speed search?
663: ============
664: */
665: void Cmd_ExecuteString (char *text)
666: {
667: cmd_function_t *cmd;
668: cmdalias_t *a;
669:
670: Cmd_TokenizeString (text);
671:
672: // execute the command line
673: if (!Cmd_Argc())
674: return; // no tokens
675:
676: // check functions
677: for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
678: {
679: if (!Q_strcasecmp (cmd_argv[0],cmd->name))
680: {
681: if (!cmd->function)
682: Cmd_ForwardToServer ();
683: else
684: cmd->function ();
685: return;
686: }
687: }
688:
689: // check alias
690: for (a=cmd_alias ; a ; a=a->next)
691: {
692: if (!Q_strcasecmp (cmd_argv[0], a->name))
693: {
694: Cbuf_InsertText (a->value);
695: return;
696: }
697: }
698:
699: // check cvars
700: if (!Cvar_Command () && (cl_warncmd.value || developer.value))
701: Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
702:
703: }
704:
705:
706:
707: /*
708: ================
709: Cmd_CheckParm
710:
711: Returns the position (1 to argc-1) in the command's argument list
712: where the given parameter apears, or 0 if not present
713: ================
714: */
715: int Cmd_CheckParm (char *parm)
716: {
717: int i;
718:
719: if (!parm)
720: Sys_Error ("Cmd_CheckParm: NULL");
721:
722: for (i = 1; i < Cmd_Argc (); i++)
723: if (! Q_strcasecmp (parm, Cmd_Argv (i)))
724: return i;
725:
726: return 0;
727: }
728:
729: /*
730: ============
731: Cmd_Init
732: ============
733: */
734: void Cmd_Init (void)
735: {
736: //
737: // register our commands
738: //
739: Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
740: Cmd_AddCommand ("exec",Cmd_Exec_f);
741: Cmd_AddCommand ("echo",Cmd_Echo_f);
742: Cmd_AddCommand ("alias",Cmd_Alias_f);
743: Cmd_AddCommand ("wait", Cmd_Wait_f);
744: #ifndef SERVERONLY
745: Cmd_AddCommand ("cmd", Cmd_ForwardToServer_f);
746: #endif
747: }
748:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.