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