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