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