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