|
|
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.