PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/cmd.c

https://gitlab.com/fzwoch/fodquake
C | 1787 lines | 1608 code | 132 blank | 47 comment | 190 complexity | 549d7d305f675ff7caa10c2b29ed38e4 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3. Copyright (C) 2005-2010 Mark Olsen
  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. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. See the GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. */
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #include "quakedef.h"
  19. #include "common.h"
  20. #include "filesystem.h"
  21. #include "strl.h"
  22. #include "utils.h"
  23. #include "context_sensitive_tab.h"
  24. #include "tokenize_string.h"
  25. extern cvar_t vid_fullscreen;
  26. extern cvar_t vid_width;
  27. extern cvar_t vid_height;
  28. #ifdef GLQUAKE
  29. extern cvar_t vid_conwidth;
  30. extern cvar_t vid_conheight;
  31. #endif
  32. extern cvar_t s_nosound;
  33. #ifndef SERVERONLY
  34. qboolean CL_CheckServerCommand (void);
  35. #endif
  36. static void Cmd_ExecuteStringEx (cbuf_t *context, char *text);
  37. static qboolean can_execute_functions;
  38. cvar_t cl_warncmd = {"cl_warncmd", "0"};
  39. cbuf_t *cbuf_main;
  40. #ifndef SERVERONLY
  41. cbuf_t *cbuf_svc;
  42. cbuf_t *cbuf_safe, *cbuf_formatted_comms;
  43. #endif
  44. cbuf_t *cbuf_cmdsave;
  45. cbuf_t *cbuf_current = NULL;
  46. //=============================================================================
  47. //Causes execution of the remainder of the command buffer to be delayed until next frame.
  48. //This allows commands like: bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
  49. void Cmd_Wait_f (void)
  50. {
  51. if (cbuf_current)
  52. cbuf_current->wait = true;
  53. }
  54. /*
  55. =============================================================================
  56. COMMAND BUFFER
  57. =============================================================================
  58. */
  59. void Cbuf_AddText (char *text)
  60. {
  61. Cbuf_AddTextEx (cbuf_main, text);
  62. }
  63. void Cbuf_InsertText (char *text)
  64. {
  65. Cbuf_InsertTextEx (cbuf_main, text);
  66. }
  67. void Cbuf_Execute (void)
  68. {
  69. Cbuf_ExecuteEx (cbuf_main);
  70. #ifndef SERVERONLY
  71. Cbuf_ExecuteEx (cbuf_safe);
  72. Cbuf_ExecuteEx (cbuf_formatted_comms);
  73. #endif
  74. }
  75. void Cbuf_Init (void)
  76. {
  77. cbuf_main = malloc(sizeof(*cbuf_main));
  78. #ifndef SERVERONLY
  79. cbuf_svc = malloc(sizeof(*cbuf_svc));
  80. cbuf_safe = malloc(sizeof(*cbuf_safe));
  81. cbuf_formatted_comms = malloc(sizeof(*cbuf_formatted_comms));
  82. #endif
  83. cbuf_cmdsave = malloc(sizeof(*cbuf_cmdsave));
  84. if (cbuf_main == 0
  85. #ifndef SERVERONLY
  86. || cbuf_svc == 0
  87. || cbuf_safe == 0
  88. || cbuf_formatted_comms == 0
  89. #endif
  90. || cbuf_cmdsave == 0)
  91. {
  92. Sys_Error("Cmd_Init: Out of memory\n");
  93. }
  94. cbuf_main->text_start = cbuf_main->text_end = MAXCMDBUF >> 1;
  95. cbuf_main->wait = false;
  96. cbuf_main->runAwayLoop = 0;
  97. #ifndef SERVERONLY
  98. cbuf_safe->text_start = cbuf_safe->text_end = (MAXCMDBUF >> 1);
  99. cbuf_safe->wait = false;
  100. cbuf_safe->runAwayLoop = 0;
  101. cbuf_formatted_comms->text_start = cbuf_formatted_comms->text_end = (MAXCMDBUF >> 1);
  102. cbuf_formatted_comms->wait = false;
  103. cbuf_formatted_comms->runAwayLoop = 0;
  104. cbuf_svc->text_start = cbuf_svc->text_end = (MAXCMDBUF >> 1);
  105. cbuf_svc->wait = false;
  106. cbuf_svc->runAwayLoop = 0;
  107. #endif
  108. cbuf_cmdsave->text_start = cbuf_cmdsave->text_end = (MAXCMDBUF >> 1);
  109. cbuf_cmdsave->wait = false;
  110. cbuf_cmdsave->runAwayLoop = 0;
  111. }
  112. void Cbuf_Shutdown()
  113. {
  114. if (cbuf_main)
  115. {
  116. free(cbuf_main);
  117. cbuf_main = 0;
  118. }
  119. #ifndef SERVERONLY
  120. if (cbuf_svc)
  121. {
  122. free(cbuf_svc);
  123. cbuf_svc = 0;
  124. }
  125. if (cbuf_safe)
  126. {
  127. free(cbuf_safe);
  128. cbuf_safe = 0;
  129. }
  130. if (cbuf_formatted_comms)
  131. {
  132. free(cbuf_formatted_comms);
  133. cbuf_formatted_comms = 0;
  134. }
  135. #endif
  136. if (cbuf_cmdsave)
  137. {
  138. free(cbuf_cmdsave);
  139. cbuf_cmdsave = 0;
  140. }
  141. }
  142. //Adds command text at the end of the buffer
  143. void Cbuf_AddTextEx (cbuf_t *cbuf, char *text)
  144. {
  145. int len, new_start, new_bufsize;
  146. len = strlen (text);
  147. if (cbuf->text_end + len <= MAXCMDBUF)
  148. {
  149. memcpy (cbuf->text_buf + cbuf->text_end, text, len);
  150. cbuf->text_end += len;
  151. return;
  152. }
  153. new_bufsize = cbuf->text_end-cbuf->text_start+len;
  154. if (new_bufsize > MAXCMDBUF)
  155. {
  156. Com_Printf ("Cbuf_AddText: overflow\n");
  157. return;
  158. }
  159. // Calculate optimal position of text in buffer
  160. new_start = ((MAXCMDBUF - new_bufsize) >> 1);
  161. memcpy (cbuf->text_buf + new_start, cbuf->text_buf + cbuf->text_start, cbuf->text_end-cbuf->text_start);
  162. memcpy (cbuf->text_buf + new_start + cbuf->text_end-cbuf->text_start, text, len);
  163. cbuf->text_start = new_start;
  164. cbuf->text_end = cbuf->text_start + new_bufsize;
  165. }
  166. //Adds command text at the beginning of the buffer
  167. void Cbuf_InsertTextEx (cbuf_t *cbuf, char *text)
  168. {
  169. int len, new_start, new_bufsize;
  170. len = strlen(text);
  171. if (len <= cbuf->text_start)
  172. {
  173. memcpy (cbuf->text_buf + (cbuf->text_start - len), text, len);
  174. cbuf->text_start -= len;
  175. return;
  176. }
  177. new_bufsize = cbuf->text_end - cbuf->text_start + len;
  178. if (new_bufsize > MAXCMDBUF)
  179. {
  180. Com_Printf ("Cbuf_InsertText: overflow\n");
  181. return;
  182. }
  183. // Calculate optimal position of text in buffer
  184. new_start = ((MAXCMDBUF - new_bufsize) >> 1);
  185. memmove (cbuf->text_buf + (new_start + len), cbuf->text_buf + cbuf->text_start,
  186. cbuf->text_end - cbuf->text_start);
  187. memcpy (cbuf->text_buf + new_start, text, len);
  188. cbuf->text_start = new_start;
  189. cbuf->text_end = cbuf->text_start + new_bufsize;
  190. }
  191. #define MAX_RUNAWAYLOOP 1000
  192. void Cbuf_ExecuteEx (cbuf_t *cbuf)
  193. {
  194. int i, j, cursize;
  195. char *text, line[1400], *src, *dest;
  196. qboolean comment, quotes;
  197. while (cbuf->text_end > cbuf->text_start)
  198. {
  199. // find a \n or ; line break
  200. text = (char *) cbuf->text_buf + cbuf->text_start;
  201. cursize = cbuf->text_end - cbuf->text_start;
  202. comment = quotes = false;
  203. for (i = 0; i < cursize; i++)
  204. {
  205. if (text[i] == '\n')
  206. break;
  207. if (text[i] == '"')
  208. {
  209. quotes = !quotes;
  210. continue;
  211. }
  212. if (comment || quotes)
  213. continue;
  214. if (text[i] == '/' && i + 1 < cursize && text[i + 1] == '/')
  215. comment = true;
  216. else if (text[i] == ';')
  217. break;
  218. }
  219. // don't execute lines without ending \n; this fixes problems with
  220. // partially stuffed aliases not being executed properly
  221. #ifndef SERVERONLY
  222. if (cbuf == cbuf_svc && i == cursize)
  223. break;
  224. #endif
  225. // Copy text to line, skipping carriage return chars
  226. src = text;
  227. dest = line;
  228. j = min (i, sizeof(line) - 1);
  229. for ( ; j; j--, src++)
  230. {
  231. if (*src != '\r')
  232. *dest++ = *src;
  233. }
  234. *dest = 0;
  235. // delete the text from the command buffer and move remaining commands down This is necessary
  236. // because commands (exec, alias) can insert data at the beginning of the text buffer
  237. if (i == cursize)
  238. {
  239. cbuf->text_start = cbuf->text_end = (MAXCMDBUF >> 1);
  240. }
  241. else
  242. {
  243. i++;
  244. cbuf->text_start += i;
  245. }
  246. cursize = cbuf->text_end - cbuf->text_start;
  247. Cmd_ExecuteStringEx (cbuf, line); // execute the command line
  248. if (cbuf->text_end - cbuf->text_start > cursize)
  249. cbuf->runAwayLoop++;
  250. if (cbuf->runAwayLoop > MAX_RUNAWAYLOOP)
  251. {
  252. Com_Printf("\x02" "A recursive alias has caused an infinite loop.");
  253. Com_Printf("\x02" " Clearing execution buffer to prevent lockup.\n");
  254. cbuf->text_start = cbuf->text_end = (MAXCMDBUF >> 1);
  255. cbuf->runAwayLoop = 0;
  256. }
  257. if (cbuf->wait)
  258. {
  259. // skip out while text still remains in buffer, leaving it for next frame
  260. cbuf->wait = false;
  261. #ifndef SERVERONLY
  262. cbuf->runAwayLoop += Q_rint(0.5 * cls.frametime * MAX_RUNAWAYLOOP);
  263. #endif
  264. return;
  265. }
  266. }
  267. cbuf->runAwayLoop = 0;
  268. }
  269. /*
  270. ==============================================================================
  271. SCRIPT COMMANDS
  272. ==============================================================================
  273. */
  274. void Cmd_ParseLegacyCmdLineCmds()
  275. {
  276. int i;
  277. for(i=1;i<com_argc;i++)
  278. {
  279. if (strcmp(com_argv[i], "-width") == 0 && i+1 < com_argc)
  280. Cvar_Set(&vid_width, com_argv[i+1]);
  281. else if (strcmp(com_argv[i], "-height") == 0 && i+1 < com_argc)
  282. Cvar_Set(&vid_height, com_argv[i+1]);
  283. #ifdef GLQUAKE
  284. else if (strcmp(com_argv[i], "-conwidth") == 0 && i+1 < com_argc)
  285. Cvar_Set(&vid_conwidth, com_argv[i+1]);
  286. else if (strcmp(com_argv[i], "-conheight") == 0 && i+1 < com_argc)
  287. Cvar_Set(&vid_conheight, com_argv[i+1]);
  288. #endif
  289. else if (strcmp(com_argv[i], "-fullscreen") == 0)
  290. Cvar_Set(&vid_fullscreen, "1");
  291. else if (strcmp(com_argv[i], "-window") == 0)
  292. Cvar_Set(&vid_fullscreen, "0");
  293. else if (strcmp(com_argv[i], "-nosound") == 0)
  294. Cvar_Set(&s_nosound, "1");
  295. }
  296. }
  297. /*
  298. Adds command line parameters as script statements
  299. Commands lead with a +, and continue until a - or another +
  300. quake +prog jctest.qp +cmd amlev1
  301. quake -nosound +cmd amlev1
  302. */
  303. void Cmd_StuffCmds_f(void)
  304. {
  305. int k, len;
  306. char *s, *text, *token;
  307. // build the combined string to parse from
  308. len = 0;
  309. for (k = 1; k < com_argc; k++)
  310. len += strlen (com_argv[k]) + 1;
  311. if (!len)
  312. return;
  313. text = Z_Malloc (len + 1);
  314. for (k = 1; k < com_argc; k++)
  315. {
  316. strcat (text, com_argv[k]);
  317. if (k != com_argc - 1)
  318. strcat (text, " ");
  319. }
  320. // pull out the commands
  321. token = Z_Malloc (len + 1);
  322. s = text;
  323. while (*s)
  324. {
  325. if (*s == '+')
  326. {
  327. k = 0;
  328. for (s = s + 1; s[0] && (s[0] != ' ' || (s[1] != '-' && s[1] != '+')); s++)
  329. token[k++] = s[0];
  330. token[k++] = '\n';
  331. token[k] = 0;
  332. Cbuf_AddText (token);
  333. }
  334. else if (*s == '-')
  335. {
  336. for (s = s + 1; s[0] && s[0] != ' '; s++)
  337. ;
  338. }
  339. else
  340. {
  341. s++;
  342. }
  343. }
  344. Z_Free (text);
  345. Z_Free (token);
  346. }
  347. void Cmd_Exec_f(void)
  348. {
  349. char *f, name[MAX_OSPATH];
  350. if (Cmd_Argc() != 2)
  351. {
  352. Com_Printf("exec <filename> : execute a script file\n");
  353. return;
  354. }
  355. Q_strncpyz(name, Cmd_Argv(1), sizeof(name) - 4);
  356. if (!(f = (char *)FS_LoadMallocFile(name)))
  357. {
  358. char *p;
  359. p = COM_SkipPath(name);
  360. if (!strchr (p, '.'))
  361. {
  362. // no extension, so try the default (.cfg)
  363. strcat (name, ".cfg");
  364. f = (char *) FS_LoadMallocFile(name);
  365. }
  366. if (!f)
  367. {
  368. Com_Printf("couldn't exec %s\n", Cmd_Argv(1));
  369. return;
  370. }
  371. }
  372. if (cl_warncmd.value || developer.value)
  373. Com_Printf("execing %s\n", name);
  374. #ifndef SERVERONLY
  375. if (cbuf_current == cbuf_svc)
  376. {
  377. Cbuf_AddText("weight_disable\n");
  378. Cbuf_AddText(f);
  379. Cbuf_AddText("\n");
  380. Cbuf_AddText("weight_enable\n");
  381. }
  382. else
  383. #endif
  384. {
  385. Cbuf_InsertText("weight_enable\n");
  386. Cbuf_InsertText("\n");
  387. Cbuf_InsertText(f);
  388. Cbuf_InsertText("weight_disable\n");
  389. }
  390. free(f);
  391. }
  392. //Just prints the rest of the line to the console
  393. void Cmd_Echo_f (void)
  394. {
  395. int i;
  396. for (i = 1; i < Cmd_Argc(); i++)
  397. Com_Printf ("%s ", Cmd_Argv(i));
  398. Com_Printf ("\n");
  399. }
  400. /*
  401. =============================================================================
  402. ALIASES
  403. =============================================================================
  404. */
  405. static cmd_alias_t *cmd_alias_hash[32];
  406. cmd_alias_t *cmd_alias;
  407. cmd_alias_t *Cmd_FindAlias (char *name)
  408. {
  409. int key;
  410. cmd_alias_t *alias;
  411. key = Com_HashKey (name);
  412. for (alias = cmd_alias_hash[key]; alias; alias = alias->hash_next)
  413. {
  414. if (!Q_strcasecmp(name, alias->name))
  415. return alias;
  416. }
  417. return NULL;
  418. }
  419. char *Cmd_AliasString (char *name)
  420. {
  421. int key;
  422. cmd_alias_t *alias;
  423. key = Com_HashKey (name);
  424. for (alias = cmd_alias_hash[key]; alias; alias = alias->hash_next)
  425. {
  426. if (!Q_strcasecmp(name, alias->name))
  427. return alias->value;
  428. }
  429. return NULL;
  430. }
  431. void Cmd_Viewalias_f (void)
  432. {
  433. cmd_alias_t *alias;
  434. if (Cmd_Argc() < 2)
  435. {
  436. Com_Printf ("viewalias <aliasname> : view body of alias\n");
  437. return;
  438. }
  439. alias = Cmd_FindAlias(Cmd_Argv(1));
  440. if (alias)
  441. Com_Printf ("%s : \"%s\"\n", Cmd_Argv(1), alias->value);
  442. else
  443. Com_Printf ("No such alias: %s\n", Cmd_Argv(1));
  444. }
  445. int Cmd_AliasCompare (const void *p1, const void *p2)
  446. {
  447. cmd_alias_t *a1, *a2;
  448. a1 = *((cmd_alias_t **) p1);
  449. a2 = *((cmd_alias_t **) p2);
  450. if (a1->name[0] == '+')
  451. {
  452. if (a2->name[0] == '+')
  453. return Q_strcasecmp(a1->name + 1, a2->name + 1);
  454. else
  455. return -1;
  456. }
  457. else if (a1->name[0] == '-')
  458. {
  459. if (a2->name[0] == '+')
  460. return 1;
  461. else if (a2->name[0] == '-')
  462. return Q_strcasecmp(a1->name + 1, a2->name + 1);
  463. else
  464. return -1;
  465. }
  466. else if (a2->name[0] == '+' || a2->name[0] == '-')
  467. {
  468. return 1;
  469. }
  470. else
  471. {
  472. return Q_strcasecmp(a1->name, a2->name);
  473. }
  474. }
  475. void Cmd_AliasList_f (void)
  476. {
  477. cmd_alias_t *a;
  478. int i, count;
  479. cmd_alias_t *sorted_aliases[512];
  480. #define MAX_SORTED_ALIASES (sizeof(sorted_aliases) / sizeof(sorted_aliases[0]))
  481. for (a = cmd_alias, count = 0; a && count < MAX_SORTED_ALIASES; a = a->next, count++)
  482. sorted_aliases[count] = a;
  483. qsort(sorted_aliases, count, sizeof (cmd_alias_t *), Cmd_AliasCompare);
  484. for (i = 0; i < count; i++)
  485. {
  486. Com_Printf ("\x02%s :", sorted_aliases[i]->name);
  487. Com_Printf (" %s\n\n", sorted_aliases[i]->value);
  488. }
  489. Com_Printf ("----------\n%d aliases\n", count);
  490. }
  491. //Creates a new command that executes a command string (possibly ; separated)
  492. void Cmd_Alias_f (void)
  493. {
  494. cmd_alias_t *a;
  495. char *s;
  496. int c, key;
  497. c = Cmd_Argc();
  498. if (c == 1) {
  499. Com_Printf ("%s <name> <command> : create or modify an alias\n", Cmd_Argv(0));
  500. Com_Printf ("aliaslist : list all aliases\n");
  501. return;
  502. }
  503. s = Cmd_Argv(1);
  504. if (strlen(s) >= MAX_ALIAS_NAME)
  505. {
  506. Com_Printf ("Alias name is too long\n");
  507. return;
  508. }
  509. key = Com_HashKey(s);
  510. // if the alias already exists, reuse it
  511. for (a = cmd_alias_hash[key]; a; a = a->hash_next)
  512. {
  513. if (!Q_strcasecmp(a->name, s))
  514. {
  515. Z_Free (a->value);
  516. break;
  517. }
  518. }
  519. if (!a) {
  520. a = Z_Malloc (sizeof(cmd_alias_t));
  521. a->next = cmd_alias;
  522. cmd_alias = a;
  523. a->hash_next = cmd_alias_hash[key];
  524. cmd_alias_hash[key] = a;
  525. }
  526. strcpy (a->name, s);
  527. a->flags = 0;
  528. if (!Q_strcasecmp(Cmd_Argv(0), "aliasa"))
  529. a->flags |= ALIAS_ARCHIVE;
  530. #ifndef SERVERONLY
  531. if (cbuf_current == cbuf_svc)
  532. a->flags |= ALIAS_SERVER;
  533. if (!Q_strcasecmp(Cmd_Argv(0), "tempalias"))
  534. a->flags |= ALIAS_TEMP;
  535. #endif
  536. // copy the rest of the command line
  537. a->value = CopyString (Cmd_MakeArgs(2));
  538. }
  539. qboolean Cmd_DeleteAlias (char *name)
  540. {
  541. cmd_alias_t *a, *prev;
  542. int key;
  543. key = Com_HashKey (name);
  544. prev = NULL;
  545. for (a = cmd_alias_hash[key]; a; a = a->hash_next)
  546. {
  547. if (!Q_strcasecmp(a->name, name))
  548. {
  549. // unlink from hash
  550. if (prev)
  551. prev->hash_next = a->hash_next;
  552. else
  553. cmd_alias_hash[key] = a->hash_next;
  554. break;
  555. }
  556. prev = a;
  557. }
  558. if (!a)
  559. return false; // not found
  560. prev = NULL;
  561. for (a = cmd_alias; a; a = a->next)
  562. {
  563. if (!Q_strcasecmp(a->name, name))
  564. {
  565. // unlink from alias list
  566. if (prev)
  567. prev->next = a->next;
  568. else
  569. cmd_alias = a->next;
  570. // free
  571. Z_Free (a->value);
  572. Z_Free (a);
  573. return true;
  574. }
  575. prev = a;
  576. }
  577. Sys_Error("Cmd_DeleteAlias: alias list broken");
  578. return false; // shut up compiler
  579. }
  580. void Cmd_UnAlias_f (void)
  581. {
  582. char*s;
  583. if (Cmd_Argc() != 2)
  584. {
  585. Com_Printf ("unalias <alias>: erase an existing alias\n");
  586. return;
  587. }
  588. s = Cmd_Argv(1);
  589. if (strlen(s) >= MAX_ALIAS_NAME)
  590. {
  591. Com_Printf ("Alias name is too long\n");
  592. return;
  593. }
  594. Cmd_DeleteAlias (s);
  595. }
  596. // remove all aliases
  597. void Cmd_UnAliasAll_f (void)
  598. {
  599. cmd_alias_t *a, *next;
  600. int i;
  601. for (a = cmd_alias; a ; a = next)
  602. {
  603. next = a->next;
  604. Z_Free (a->value);
  605. Z_Free (a);
  606. }
  607. cmd_alias = NULL;
  608. // clear hash
  609. for (i = 0; i < 32; i++)
  610. cmd_alias_hash[i] = NULL;
  611. }
  612. void DeleteServerAliases(void)
  613. {
  614. extern cmd_alias_t *cmd_alias;
  615. cmd_alias_t *a;
  616. for (a = cmd_alias; a; a = a->next)
  617. {
  618. if (a->flags & ALIAS_SERVER)
  619. Cmd_DeleteAlias(a->name);
  620. }
  621. }
  622. void Cmd_WriteAliases (FILE *f)
  623. {
  624. cmd_alias_t *a;
  625. for (a = cmd_alias ; a ; a=a->next)
  626. if (a->flags & ALIAS_ARCHIVE)
  627. fprintf (f, "aliasa %s \"%s\"\n", a->name, a->value);
  628. }
  629. /*
  630. =============================================================================
  631. LEGACY COMMANDS
  632. =============================================================================
  633. */
  634. typedef struct legacycmd_s
  635. {
  636. char *oldname, *newname;
  637. struct legacycmd_s *next;
  638. } legacycmd_t;
  639. static legacycmd_t *legacycmds = NULL;
  640. void Cmd_AddLegacyCommand (char *oldname, char *newname)
  641. {
  642. legacycmd_t *cmd;
  643. cmd = (legacycmd_t *) Q_Malloc (sizeof(legacycmd_t));
  644. cmd->next = legacycmds;
  645. legacycmds = cmd;
  646. cmd->oldname = CopyString(oldname);
  647. cmd->newname = CopyString(newname);
  648. }
  649. qboolean Cmd_IsLegacyCommand (char *oldname)
  650. {
  651. legacycmd_t *cmd;
  652. for (cmd = legacycmds; cmd; cmd = cmd->next)
  653. {
  654. if (!Q_strcasecmp(cmd->oldname, oldname))
  655. return true;
  656. }
  657. return false;
  658. }
  659. static qboolean Cmd_LegacyCommand (void)
  660. {
  661. static qboolean recursive = false;
  662. legacycmd_t *cmd;
  663. char text[1024];
  664. for (cmd = legacycmds; cmd; cmd = cmd->next)
  665. {
  666. if (!Q_strcasecmp(cmd->oldname, Cmd_Argv(0)))
  667. break;
  668. }
  669. if (!cmd)
  670. return false;
  671. if (!cmd->newname[0])
  672. return true; // just ignore this command
  673. // build new command string
  674. snprintf(text, sizeof(text), "%s %s", cmd->newname, Cmd_Args());
  675. if (recursive)
  676. Sys_Error("Cmd_LegacyCommand: Called recursively");
  677. recursive = true;
  678. Cmd_ExecuteString (text);
  679. recursive = false;
  680. return true;
  681. }
  682. /*
  683. =============================================================================
  684. COMMAND EXECUTION
  685. =============================================================================
  686. */
  687. #define MAX_ARGS 80
  688. static int cmd_argc;
  689. static char *cmd_argv[MAX_ARGS];
  690. static char *cmd_null_string = "";
  691. static char *cmd_args = NULL;
  692. cmd_function_t *cmd_hash_array[32];
  693. /*static*/ cmd_function_t *cmd_functions; // possible commands to execute
  694. int Cmd_Argc (void)
  695. {
  696. return cmd_argc;
  697. }
  698. char *Cmd_Argv (int arg)
  699. {
  700. if (arg >= cmd_argc)
  701. return cmd_null_string;
  702. return cmd_argv[arg];
  703. }
  704. //Returns a single string containing argv(1) to argv(argc() - 1)
  705. char *Cmd_Args (void)
  706. {
  707. if (!cmd_args)
  708. return "";
  709. return cmd_args;
  710. }
  711. //Returns a single string containing argv(start) to argv(argc() - 1)
  712. //Unlike Cmd_Args, shrinks spaces between argvs
  713. char *Cmd_MakeArgs (int start)
  714. {
  715. int i, c;
  716. static char text[1024];
  717. text[0] = 0;
  718. c = Cmd_Argc();
  719. for (i = start; i < c; i++)
  720. {
  721. if (i > start)
  722. strlcat(text, " ", sizeof(text));
  723. strlcat(text, Cmd_Argv(i), sizeof(text));
  724. }
  725. return text;
  726. }
  727. //Parses the given string into command line tokens.
  728. void Cmd_TokenizeString (char *text)
  729. {
  730. int idx;
  731. static char argv_buf[1024];
  732. idx = 0;
  733. cmd_argc = 0;
  734. cmd_args = NULL;
  735. while (1)
  736. {
  737. // skip whitespace
  738. while (*text == ' ' || *text == '\t' || *text == '\r')
  739. text++;
  740. if (*text == '\n')
  741. {
  742. /* a newline separates commands in the buffer */
  743. break;
  744. }
  745. if (!*text)
  746. return;
  747. if (cmd_argc == 1)
  748. cmd_args = text;
  749. text = COM_Parse (text);
  750. if (!text)
  751. return;
  752. if (cmd_argc < MAX_ARGS)
  753. {
  754. if (idx+strlen(com_token) >= sizeof(argv_buf))
  755. break;
  756. cmd_argv[cmd_argc] = argv_buf + idx;
  757. strcpy(cmd_argv[cmd_argc], com_token);
  758. idx += strlen(com_token) + 1;
  759. cmd_argc++;
  760. }
  761. }
  762. }
  763. extern int cvarsregged;
  764. void Cmd_AddCommand (char *cmd_name, xcommand_t function)
  765. {
  766. cmd_function_t *cmd;
  767. int key;
  768. if (cvarsregged)
  769. {
  770. printf("Command \"%s\" registered too late\n", cmd_name);
  771. }
  772. // fail if the command is a variable name
  773. if (Cvar_FindVar(cmd_name))
  774. {
  775. Com_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
  776. return;
  777. }
  778. key = Com_HashKey (cmd_name);
  779. // fail if the command already exists
  780. for (cmd = cmd_hash_array[key]; cmd; cmd=cmd->hash_next)
  781. {
  782. if (!Q_strcasecmp (cmd_name, cmd->name))
  783. {
  784. Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
  785. return;
  786. }
  787. }
  788. cmd = malloc(sizeof(*cmd));
  789. if (cmd == 0)
  790. Sys_Error("Cmd_AddCommand: Unable to allocate memory for command\n");
  791. cmd->name = cmd_name;
  792. cmd->function = function;
  793. cmd->next = cmd_functions;
  794. cmd_functions = cmd;
  795. cmd->hash_next = cmd_hash_array[key];
  796. cmd_hash_array[key] = cmd;
  797. cmd->weight = 0;
  798. }
  799. qboolean Cmd_Exists (char *cmd_name)
  800. {
  801. int key;
  802. cmd_function_t *cmd;
  803. key = Com_HashKey (cmd_name);
  804. for (cmd=cmd_hash_array[key]; cmd; cmd = cmd->hash_next)
  805. {
  806. if (!Q_strcasecmp (cmd_name, cmd->name))
  807. return true;
  808. }
  809. return false;
  810. }
  811. cmd_function_t *Cmd_FindCommand (char *cmd_name)
  812. {
  813. int key;
  814. cmd_function_t *cmd;
  815. key = Com_HashKey (cmd_name);
  816. for (cmd = cmd_hash_array[key]; cmd; cmd = cmd->hash_next)
  817. {
  818. if (!Q_strcasecmp (cmd_name, cmd->name))
  819. return cmd;
  820. }
  821. return NULL;
  822. }
  823. char *Cmd_CompleteCommand (char *partial)
  824. {
  825. cmd_function_t *cmd;
  826. int len;
  827. cmd_alias_t *alias;
  828. len = strlen(partial);
  829. if (!len)
  830. return NULL;
  831. // check for exact match
  832. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  833. if (!Q_strcasecmp (partial, cmd->name))
  834. return cmd->name;
  835. for (alias = cmd_alias; alias; alias = alias->next)
  836. if (!Q_strcasecmp (partial, alias->name))
  837. return alias->name;
  838. // check for partial match
  839. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  840. if (!Q_strncasecmp (partial, cmd->name, len))
  841. return cmd->name;
  842. for (alias = cmd_alias; alias; alias = alias->next)
  843. if (!Q_strncasecmp (partial, alias->name, len))
  844. return alias->name;
  845. return NULL;
  846. }
  847. int Cmd_CompleteCountPossible (char *partial)
  848. {
  849. cmd_function_t *cmd;
  850. int len, c = 0;
  851. len = strlen(partial);
  852. if (!len)
  853. return 0;
  854. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  855. if (!Q_strncasecmp (partial, cmd->name, len))
  856. c++;
  857. return c;
  858. }
  859. int Cmd_AliasCompleteCountPossible (char *partial)
  860. {
  861. cmd_alias_t *alias;
  862. int len, c = 0;
  863. len = strlen(partial);
  864. if (!len)
  865. return 0;
  866. for (alias = cmd_alias; alias; alias = alias->next)
  867. if (!Q_strncasecmp (partial, alias->name, len))
  868. c++;
  869. return c;
  870. }
  871. int Cmd_CommandCompare (const void *p1, const void *p2)
  872. {
  873. return strcmp((*((cmd_function_t **) p1))->name, (*((cmd_function_t **) p2))->name);
  874. }
  875. void Cmd_CmdList_f (void)
  876. {
  877. cmd_function_t *cmd;
  878. int i;
  879. int count;
  880. cmd_function_t **sorted_cmds;
  881. for (cmd = cmd_functions, count = 0; cmd; cmd = cmd->next, count++);
  882. sorted_cmds = malloc(count * sizeof(*sorted_cmds));
  883. if (sorted_cmds == 0)
  884. {
  885. Com_ErrorPrintf("cmdlist: out of memory\n");
  886. return;
  887. }
  888. for (cmd = cmd_functions, count = 0; cmd; cmd = cmd->next, count++)
  889. sorted_cmds[count] = cmd;
  890. qsort(sorted_cmds, count, sizeof (cmd_function_t *), Cmd_CommandCompare);
  891. for (i = 0; i < count; i++)
  892. Com_Printf ("%s\n", sorted_cmds[i]->name);
  893. Com_Printf ("------------\n%d commands\n", count);
  894. free(sorted_cmds);
  895. }
  896. #define MAX_MACROS 64
  897. typedef struct
  898. {
  899. char name[32];
  900. const char *(*func) (void);
  901. qboolean teamplay;
  902. } macro_command_t;
  903. static macro_command_t macro_commands[MAX_MACROS];
  904. static int macro_count = 0;
  905. void Cmd_AddMacroEx(char *s, const char *(*f)(void), qboolean teamplay)
  906. {
  907. if (macro_count == MAX_MACROS)
  908. Sys_Error("Cmd_AddMacro: macro_count == MAX_MACROS");
  909. Q_strncpyz(macro_commands[macro_count].name, s, sizeof(macro_commands[macro_count].name));
  910. macro_commands[macro_count].func = f;
  911. macro_commands[macro_count].teamplay = teamplay;
  912. macro_count++;
  913. }
  914. void Cmd_AddMacro(char *s, const char *(*f)(void))
  915. {
  916. Cmd_AddMacroEx(s, f, false);
  917. }
  918. char *Cmd_MacroString (char *s, int *macro_length)
  919. {
  920. int i;
  921. macro_command_t *macro;
  922. for (i = 0; i < macro_count; i++)
  923. {
  924. macro = &macro_commands[i];
  925. if (!Q_strncasecmp(s, macro->name, strlen(macro->name)))
  926. {
  927. #ifndef SERVERONLY
  928. if (cbuf_current == cbuf_main && macro->teamplay)
  929. cbuf_current = cbuf_formatted_comms;
  930. #endif
  931. *macro_length = strlen(macro->name);
  932. return macro->func();
  933. }
  934. }
  935. *macro_length = 0;
  936. return NULL;
  937. }
  938. int Cmd_MacroCompare (const void *p1, const void *p2)
  939. {
  940. return strcmp((*((macro_command_t **) p1))->name, (*((macro_command_t **) p2))->name);
  941. }
  942. void Cmd_MacroList_f (void)
  943. {
  944. int i;
  945. static qboolean sorted = false;
  946. static macro_command_t *sorted_macros[MAX_MACROS];
  947. if (!macro_count)
  948. {
  949. Com_Printf("No macros!");
  950. return;
  951. }
  952. if (!sorted)
  953. {
  954. for (i = 0; i < macro_count; i++)
  955. sorted_macros[i] = &macro_commands[i];
  956. sorted = true;
  957. qsort(sorted_macros, macro_count, sizeof (macro_command_t *), Cmd_MacroCompare);
  958. }
  959. for (i = 0; i < macro_count; i++)
  960. Com_Printf ("$%s\n", sorted_macros[i]->name);
  961. Com_Printf ("---------\n%d macros\n", macro_count);
  962. }
  963. //Expands all $cvar expressions to cvar values
  964. //If not SERVERONLY, also expands $macro expressions
  965. void Cmd_ExpandString(char *data, char *dest, unsigned int maxlen)
  966. {
  967. unsigned int c;
  968. char buf[255], *str;
  969. int i, len, quotes = 0, name_length;
  970. cvar_t *var, *bestvar;
  971. #ifndef SERVERONLY
  972. int macro_length;
  973. #endif
  974. len = 0;
  975. while ((c = *data))
  976. {
  977. if (c == '"')
  978. quotes++;
  979. if (c == '$' && !(quotes&1))
  980. {
  981. data++;
  982. /* Copy the texter after '$' to a temporary buffer and
  983. * look for the longest cvar match in the process */
  984. i = 0;
  985. buf[0] = 0;
  986. bestvar = NULL;
  987. while ((c = *data) > 32 && i < sizeof(buf)-1)
  988. {
  989. if (c == '$')
  990. break;
  991. data++;
  992. buf[i++] = c;
  993. buf[i] = 0;
  994. if ((var = Cvar_FindVar(buf)))
  995. {
  996. bestvar = var;
  997. }
  998. }
  999. #ifndef SERVERONLY
  1000. if (!dedicated)
  1001. {
  1002. str = Cmd_MacroString (buf, &macro_length);
  1003. name_length = macro_length;
  1004. if (bestvar && (!str || (strlen(bestvar->name) > macro_length)))
  1005. {
  1006. str = bestvar->string;
  1007. name_length = strlen(bestvar->name);
  1008. }
  1009. }
  1010. else
  1011. #endif
  1012. {
  1013. if (bestvar)
  1014. {
  1015. str = bestvar->string;
  1016. name_length = strlen(bestvar->name);
  1017. }
  1018. else
  1019. {
  1020. str = NULL;
  1021. }
  1022. }
  1023. if (str)
  1024. {
  1025. // check buffer size
  1026. if (len + strlen(str) + strlen(buf+name_length) >= maxlen - 1)
  1027. break;
  1028. strcpy(&dest[len], str);
  1029. len += strlen(str);
  1030. i = name_length;
  1031. while (buf[i])
  1032. dest[len++] = buf[i++];
  1033. }
  1034. else
  1035. {
  1036. // no matching cvar or macro
  1037. dest[len++] = '$';
  1038. if (len + strlen(buf) >= maxlen - 1)
  1039. break;
  1040. strcpy (&dest[len], buf);
  1041. len += strlen(buf);
  1042. }
  1043. }
  1044. else
  1045. {
  1046. dest[len] = c;
  1047. data++;
  1048. len++;
  1049. if (len >= maxlen - 1)
  1050. break;
  1051. }
  1052. }
  1053. dest[len] = 0;
  1054. }
  1055. char *msgtrigger_commands[] =
  1056. {
  1057. "play", "playvol", "stopsound", "set", "echo", "say", "say_team",
  1058. "alias", "unalias", "msg_trigger", "inc", "bind", "unbind", "record",
  1059. "easyrecord", "stop", "if", "wait", "log", "match_forcestart",
  1060. NULL
  1061. };
  1062. char *formatted_comms_commands[] =
  1063. {
  1064. "if", "wait", "echo", "say", "say_team",
  1065. "tp_point", "tp_pickup", "tp_took",
  1066. NULL
  1067. };
  1068. //A complete command line has been parsed, so try to execute it
  1069. static void Cmd_ExecuteStringEx (cbuf_t *context, char *text)
  1070. {
  1071. cvar_t *v;
  1072. cmd_function_t *cmd;
  1073. cmd_alias_t *a;
  1074. static char buf[2048];
  1075. cbuf_t *inserttarget, *oldcontext;
  1076. extern int weight_disable;
  1077. oldcontext = cbuf_current;
  1078. cbuf_current = context;
  1079. Cmd_ExpandString(text, buf, sizeof(buf));
  1080. Cmd_TokenizeString (buf);
  1081. if (!Cmd_Argc())
  1082. goto done; // no tokens
  1083. #ifndef SERVERONLY
  1084. if (cbuf_current == cbuf_svc)
  1085. {
  1086. if (CL_CheckServerCommand())
  1087. goto done;
  1088. }
  1089. #endif
  1090. // check functions
  1091. if ((cmd = Cmd_FindCommand(cmd_argv[0])))
  1092. {
  1093. #ifndef SERVERONLY
  1094. if (can_execute_functions || strcmp(cmd_argv[0], "cfg_load") == 0 || strcmp(cmd_argv[0], "exec") == 0 || strcmp(cmd_argv[0], "alias") == 0)
  1095. {
  1096. char **s;
  1097. if (cbuf_current == cbuf_safe)
  1098. {
  1099. for (s = msgtrigger_commands; *s; s++)
  1100. {
  1101. if (!Q_strcasecmp(cmd_argv[0], *s))
  1102. break;
  1103. }
  1104. if (!*s)
  1105. {
  1106. Com_Printf ("\"%s\" cannot be used in message triggers\n", cmd_argv[0]);
  1107. goto done;
  1108. }
  1109. }
  1110. else if (cbuf_current == cbuf_formatted_comms)
  1111. {
  1112. for (s = formatted_comms_commands; *s; s++)
  1113. {
  1114. if (!Q_strcasecmp(cmd_argv[0], *s))
  1115. break;
  1116. }
  1117. if (!*s)
  1118. {
  1119. Com_Printf("\"%s\" cannot be used in combination with teamplay $macros\n", cmd_argv[0]);
  1120. goto done;
  1121. }
  1122. }
  1123. #endif
  1124. if (weight_disable == 0)
  1125. cmd->weight++;
  1126. if (cmd->function)
  1127. cmd->function();
  1128. else
  1129. Cmd_ForwardToServer ();
  1130. goto done;
  1131. }
  1132. else
  1133. {
  1134. Cbuf_AddTextEx(cbuf_cmdsave, text);
  1135. Cbuf_AddTextEx(cbuf_cmdsave, "\n");
  1136. goto done;
  1137. }
  1138. }
  1139. // some bright guy decided to use "skill" as a mod command in Custom TF, sigh
  1140. if (!strcmp(Cmd_Argv(0), "skill") && cmd_argc == 1 && Cmd_FindAlias("skill"))
  1141. goto checkaliases;
  1142. // check cvars
  1143. if ((v = Cvar_FindVar (Cmd_Argv(0))))
  1144. {
  1145. if (weight_disable == 0)
  1146. v->weight++;
  1147. #ifndef SERVERONLY
  1148. if (cbuf_current == cbuf_formatted_comms)
  1149. {
  1150. Com_Printf("\"%s\" cannot be used in combination with teamplay $macros\n", cmd_argv[0]);
  1151. goto done;
  1152. }
  1153. #endif
  1154. if (Cvar_Command())
  1155. goto done;
  1156. }
  1157. // check aliases
  1158. checkaliases:
  1159. if ((a = Cmd_FindAlias(cmd_argv[0])))
  1160. {
  1161. if (weight_disable == 0)
  1162. a->weight++;
  1163. #ifndef SERVERONLY
  1164. if (cbuf_current == cbuf_svc)
  1165. {
  1166. Cbuf_AddText (a->value);
  1167. Cbuf_AddText ("\n");
  1168. }
  1169. else
  1170. #endif
  1171. {
  1172. #ifdef SERVERONLY
  1173. inserttarget = cbuf_main;
  1174. #else
  1175. inserttarget = cbuf_current ? cbuf_current : cbuf_main;
  1176. #endif
  1177. Cbuf_InsertTextEx (inserttarget, "\n");
  1178. // if the alias value is a command or cvar and
  1179. // the alias is called with parameters, add them
  1180. if (Cmd_Argc() > 1 && !strchr(a->value, ' ') && !strchr(a->value, '\t') &&
  1181. (Cvar_FindVar(a->value) || (Cmd_FindCommand(a->value) && a->value[0] != '+' && a->value[0] != '-'))
  1182. )
  1183. {
  1184. Cbuf_InsertTextEx (inserttarget, Cmd_Args());
  1185. Cbuf_InsertTextEx (inserttarget, " ");
  1186. }
  1187. Cbuf_InsertTextEx (inserttarget, a->value);
  1188. }
  1189. goto done;
  1190. }
  1191. #ifndef SERVERONLY
  1192. if (Cmd_LegacyCommand())
  1193. goto done;
  1194. #endif
  1195. #ifndef SERVERONLY
  1196. if (cbuf_current != cbuf_svc)
  1197. #endif
  1198. {
  1199. if (cl_warncmd.value || developer.value)
  1200. Com_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
  1201. }
  1202. done:
  1203. cbuf_current = oldcontext;
  1204. }
  1205. void Cmd_ExecuteString (char *text)
  1206. {
  1207. Cmd_ExecuteStringEx (NULL, text);
  1208. }
  1209. static qboolean is_numeric (char *c)
  1210. {
  1211. return (*c >= '0' && *c <= '9') ||
  1212. ((*c == '-' || *c == '+') && (c[1] == '.' || (c[1]>='0' && c[1]<='9'))) ||
  1213. (*c == '.' && (c[1]>='0' && c[1]<='9'));
  1214. }
  1215. void Cmd_If_f (void)
  1216. {
  1217. int i, c;
  1218. char *op, buf[1024] = {0};
  1219. qboolean result;
  1220. if ((c = Cmd_Argc()) < 5)
  1221. {
  1222. Com_Printf ("Usage: if <expr1> <op> <expr2> <command> [else <command>]\n");
  1223. return;
  1224. }
  1225. op = Cmd_Argv(2);
  1226. if (!strcmp(op, "==") || !strcmp(op, "=") || !strcmp(op, "!=") || !strcmp(op, "<>"))
  1227. {
  1228. if (is_numeric(Cmd_Argv(1)) && is_numeric(Cmd_Argv(3)))
  1229. result = Q_atof(Cmd_Argv(1)) == Q_atof(Cmd_Argv(3));
  1230. else
  1231. result = !strcmp(Cmd_Argv(1), Cmd_Argv(3));
  1232. if (op[0] != '=')
  1233. result = !result;
  1234. }
  1235. else if (!strcmp(op, ">"))
  1236. {
  1237. result = Q_atof(Cmd_Argv(1)) > Q_atof(Cmd_Argv(3));
  1238. }
  1239. else if (!strcmp(op, "<"))
  1240. {
  1241. result = Q_atof(Cmd_Argv(1)) < Q_atof(Cmd_Argv(3));
  1242. }
  1243. else if (!strcmp(op, ">="))
  1244. {
  1245. result = Q_atof(Cmd_Argv(1)) >= Q_atof(Cmd_Argv(3));
  1246. }
  1247. else if (!strcmp(op, "<="))
  1248. {
  1249. result = Q_atof(Cmd_Argv(1)) <= Q_atof(Cmd_Argv(3));
  1250. }
  1251. else if (!strcmp(op, "isin"))
  1252. {
  1253. result = (strstr(Cmd_Argv(3), Cmd_Argv(1)) ? 1 : 0);
  1254. }
  1255. else if (!strcmp(op, "!isin"))
  1256. {
  1257. result = (strstr(Cmd_Argv(3), Cmd_Argv(1)) ? 0 : 1);
  1258. }
  1259. else
  1260. {
  1261. Com_Printf ("unknown operator: %s\n", op);
  1262. Com_Printf ("valid operators are ==, =, !=, <>, >, <, >=, <=, isin, !isin\n");
  1263. return;
  1264. }
  1265. if (result)
  1266. {
  1267. for (i = 4; i < c; i++)
  1268. {
  1269. if ((i == 4) && !Q_strcasecmp(Cmd_Argv(i), "then"))
  1270. continue;
  1271. if (!Q_strcasecmp(Cmd_Argv(i), "else"))
  1272. break;
  1273. if (buf[0])
  1274. strlcat(buf, " ", sizeof(buf));
  1275. strlcat(buf, Cmd_Argv(i), sizeof(buf));
  1276. }
  1277. }
  1278. else
  1279. {
  1280. for (i = 4; i < c ; i++)
  1281. {
  1282. if (!Q_strcasecmp(Cmd_Argv(i), "else"))
  1283. break;
  1284. }
  1285. if (i == c)
  1286. return;
  1287. for (i++; i < c; i++)
  1288. {
  1289. if (buf[0])
  1290. strlcat(buf, " ", sizeof(buf));
  1291. strlcat(buf, Cmd_Argv(i), sizeof(buf));
  1292. }
  1293. }
  1294. strlcat(buf, "\n", sizeof(buf));
  1295. Cbuf_InsertTextEx (cbuf_current ? cbuf_current : cbuf_main, buf);
  1296. }
  1297. //Returns the position (1 to argc - 1) in the command's argument list where the given parameter apears, or 0 if not present
  1298. int Cmd_CheckParm (char *parm)
  1299. {
  1300. int i, c;
  1301. c = Cmd_Argc();
  1302. for (i = 1; i < c; i++)
  1303. if (! Q_strcasecmp (parm, Cmd_Argv (i)))
  1304. return i;
  1305. return 0;
  1306. }
  1307. void Cmd_EnableFunctionExecution()
  1308. {
  1309. can_execute_functions = 1;
  1310. Cbuf_ExecuteEx(cbuf_cmdsave);
  1311. }
  1312. static int cstc_alias_check(char *string, struct tokenized_string *ts)
  1313. {
  1314. int i;
  1315. for (i=0; i<ts->count; i++)
  1316. {
  1317. if (strstr(string, ts->tokens[i]) == NULL)
  1318. return 0;
  1319. }
  1320. return 1;
  1321. }
  1322. static int cstc_alias_conditions(void)
  1323. {
  1324. if (cmd_alias == NULL)
  1325. return 0;
  1326. return 1;
  1327. }
  1328. static int cstc_alias_get_results(struct cst_info *self, int *results, int get_result, int result_type, char **result)
  1329. {
  1330. int count;
  1331. cmd_alias_t *a;
  1332. extern cmd_alias_t *cmd_alias;
  1333. count = 0;
  1334. if (results)
  1335. {
  1336. a = cmd_alias;
  1337. while (a)
  1338. {
  1339. if (cstc_alias_check(a->name, self->tokenized_input))
  1340. count++;
  1341. a = a->next;
  1342. }
  1343. *results = count;
  1344. return 0;
  1345. }
  1346. if (result == NULL)
  1347. return 0;
  1348. count = -1;
  1349. a = cmd_alias;
  1350. while (a)
  1351. {
  1352. if (cstc_alias_check(a->name, self->tokenized_input))
  1353. count++;
  1354. if (count == get_result)
  1355. {
  1356. if (a->value)
  1357. *result = va("%s \"%s\"", a->name, a->value);
  1358. else
  1359. *result = va("%s \"\"", a->name);
  1360. return 0;
  1361. }
  1362. a = a->next;
  1363. }
  1364. return 1;
  1365. }
  1366. struct cstc_cfginfo
  1367. {
  1368. struct directory_list *dl;
  1369. qboolean *checked;
  1370. qboolean initialized;
  1371. };
  1372. static int cstc_exec_get_data(struct cst_info *self, int remove)
  1373. {
  1374. struct cstc_cfginfo *data;
  1375. const char * const cfg_endings[] = { ".cfg", NULL};
  1376. if (!self)
  1377. return 1;
  1378. if (self->data)
  1379. {
  1380. data = (struct cstc_cfginfo *)self->data;
  1381. Util_Dir_Delete(data->dl);
  1382. free(data->checked);
  1383. free(data);
  1384. self->data = NULL;
  1385. }
  1386. if (remove)
  1387. return 0;
  1388. if ((data = calloc(1, sizeof(*data))))
  1389. {
  1390. if ((data->dl = Util_Dir_Read(va("%s/qw/", com_basedir), 1, 1, cfg_endings)))
  1391. {
  1392. if (data->dl->entries == 0)
  1393. {
  1394. cstc_exec_get_data(self, 1);
  1395. return 1;
  1396. }
  1397. self->data = data;
  1398. return 0;
  1399. }
  1400. free(data);
  1401. }
  1402. return 1;
  1403. }
  1404. static int cstc_exec_check(char *entry, struct tokenized_string *ts)
  1405. {
  1406. int i;
  1407. for (i=0; i<ts->count; i++)
  1408. {
  1409. if (Util_strcasestr(entry, ts->tokens[i]) == NULL)
  1410. return 0;
  1411. }
  1412. return 1;
  1413. }
  1414. static int cstc_exec_get_results(struct cst_info *self, int *results, int get_result, int result_type, char **result)
  1415. {
  1416. struct cstc_cfginfo *data;
  1417. int count, i;
  1418. if (self->data == NULL)
  1419. return 1;
  1420. data = (struct cstc_cfginfo *)self->data;
  1421. if (results || data->initialized == false)
  1422. {
  1423. if (data->checked)
  1424. free(data->checked);
  1425. if (!(data->checked= calloc(data->dl->entry_count, sizeof(qboolean))))
  1426. return 1;
  1427. for (i=0, count=0; i<data->dl->entry_count; i++)
  1428. {
  1429. if (cstc_exec_check(data->dl->entries[i].name, self->tokenized_input))
  1430. {
  1431. data->checked[i] = true;
  1432. count++;
  1433. }
  1434. }
  1435. if (results)
  1436. *results = count;
  1437. data->initialized = true;
  1438. return 0;
  1439. }
  1440. if (result == NULL)
  1441. return 0;
  1442. for (i=0, count=-1; i<data->dl->entry_count; i++)
  1443. {
  1444. if (data->checked[i] == true)
  1445. count++;
  1446. if (count == get_result)
  1447. {
  1448. *result = data->dl->entries[i].name;
  1449. return 0;
  1450. }
  1451. }
  1452. return 1;
  1453. }
  1454. void Cmd_Init (void)
  1455. {
  1456. // register our commands
  1457. Cmd_AddCommand("exec", Cmd_Exec_f);
  1458. Cmd_AddCommand("echo", Cmd_Echo_f);
  1459. Cmd_AddCommand("aliaslist", Cmd_AliasList_f);
  1460. //Cmd_AddCommand("aliasa", Cmd_Alias_f);
  1461. Cmd_AddCommand("alias", Cmd_Alias_f);
  1462. Cmd_AddCommand("tempalias", Cmd_Alias_f);
  1463. Cmd_AddCommand("viewalias", Cmd_Viewalias_f);
  1464. Cmd_AddCommand("unaliasall", Cmd_UnAliasAll_f);
  1465. Cmd_AddCommand("unalias", Cmd_UnAlias_f);
  1466. Cmd_AddCommand("wait", Cmd_Wait_f);
  1467. Cmd_AddCommand("cmdlist", Cmd_CmdList_f);
  1468. Cmd_AddCommand("if", Cmd_If_f);
  1469. Cmd_AddCommand("macrolist", Cmd_MacroList_f);
  1470. CSTC_Add("alias", &cstc_alias_conditions, &cstc_alias_get_results, NULL, NULL, 0, "arrow up/down to navigate");
  1471. CSTC_Add("exec", NULL, &cstc_exec_get_results, &cstc_exec_get_data, NULL, CSTC_EXECUTE, "arrow up/down to navigate");
  1472. }
  1473. void Cmd_Shutdown()
  1474. {
  1475. cmd_function_t *func;
  1476. while((func = cmd_functions))
  1477. {
  1478. cmd_functions = func->next;
  1479. free(func);
  1480. }
  1481. }