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

/quakeforge-old/branches/modular_input/common/cmd.c

#
C | 772 lines | 681 code | 22 blank | 69 comment | 7 complexity | dcb5c8e62ff40cd588d076af06adf823 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-3.0, AGPL-1.0, Unlicense
  1. /*
  2. cmd.c - Quake script command processing module
  3. Copyright (C) 1996-1997 Id Software, Inc.
  4. Copyright (C) 1999,2000 contributors of the QuakeForge project
  5. Please see the file "AUTHORS" for a list of contributors
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. See the GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. */
  18. #include <common.h>
  19. #include <cmd.h>
  20. #include <console.h>
  21. #include <cvar.h>
  22. #include <sys.h>
  23. #include <client.h>
  24. #include <lib_replace.h>
  25. #include <zone.h>
  26. #include <string.h>
  27. #include <net.h>
  28. #include <common_quakedef.h>
  29. void Cmd_ForwardToServer (void);
  30. #define MAX_ALIAS_NAME 32
  31. typedef struct cmdalias_s
  32. {
  33. struct cmdalias_s *next;
  34. char name[MAX_ALIAS_NAME];
  35. char *value;
  36. } cmdalias_t;
  37. cmdalias_t *cmd_alias;
  38. qboolean cmd_wait;
  39. cvar_t cl_warncmd = {"cl_warncmd", "0"};
  40. //=============================================================================
  41. /*
  42. ============
  43. Cmd_Wait_f
  44. Causes execution of the remainder of the command buffer to be delayed until
  45. next frame. This allows commands like:
  46. bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
  47. ============
  48. */
  49. void Cmd_Wait_f (void)
  50. {
  51. cmd_wait = true;
  52. }
  53. /*
  54. =============================================================================
  55. COMMAND BUFFER
  56. =============================================================================
  57. */
  58. sizebuf_t cmd_text;
  59. byte cmd_text_buf[8192];
  60. /*
  61. ============
  62. Cbuf_Init
  63. ============
  64. */
  65. void Cbuf_Init (void)
  66. {
  67. cmd_text.data = cmd_text_buf;
  68. cmd_text.maxsize = sizeof(cmd_text_buf);
  69. }
  70. /*
  71. ============
  72. Cbuf_AddText
  73. Adds command text at the end of the buffer
  74. ============
  75. */
  76. void Cbuf_AddText (char *text)
  77. {
  78. int l;
  79. l = Q_strlen (text);
  80. if (cmd_text.cursize + l >= cmd_text.maxsize)
  81. {
  82. Con_Printf ("Cbuf_AddText: overflow\n");
  83. return;
  84. }
  85. SZ_Write (&cmd_text, text, Q_strlen (text));
  86. }
  87. /*
  88. ============
  89. Cbuf_InsertText
  90. Adds command text immediately after the current command
  91. Adds a \n to the text
  92. FIXME: actually change the command buffer to do less copying
  93. ============
  94. */
  95. void Cbuf_InsertText (char *text)
  96. {
  97. char *temp;
  98. int templen;
  99. // copy off any commands still remaining in the exec buffer
  100. templen = cmd_text.cursize;
  101. if (templen)
  102. {
  103. temp = Z_Malloc (templen);
  104. Q_memcpy (temp, cmd_text.data, templen);
  105. SZ_Clear (&cmd_text);
  106. }
  107. else
  108. temp = NULL; // shut up compiler
  109. // add the entire text of the file
  110. Cbuf_AddText (text);
  111. SZ_Write (&cmd_text, "\n", 1);
  112. // add the copied off data
  113. if (templen)
  114. {
  115. SZ_Write (&cmd_text, temp, templen);
  116. Z_Free (temp);
  117. }
  118. }
  119. /*
  120. ============
  121. Cbuf_Execute
  122. ============
  123. */
  124. void Cbuf_Execute (void)
  125. {
  126. int i;
  127. char *text;
  128. char line[1024];
  129. int quotes;
  130. while (cmd_text.cursize)
  131. {
  132. // find a \n or ; line break
  133. text = (char *)cmd_text.data;
  134. quotes = 0;
  135. for (i=0 ; i< cmd_text.cursize ; i++)
  136. {
  137. if (text[i] == '"')
  138. quotes++;
  139. if ( !(quotes&1) && text[i] == ';')
  140. break; // don't break if inside a quoted string
  141. if (text[i] == '\n')
  142. break;
  143. }
  144. memcpy (line, text, i);
  145. line[i] = 0;
  146. // delete the text from the command buffer and move remaining commands down
  147. // this is necessary because commands (exec, alias) can insert data at the
  148. // beginning of the text buffer
  149. if (i == cmd_text.cursize)
  150. cmd_text.cursize = 0;
  151. else
  152. {
  153. i++;
  154. cmd_text.cursize -= i;
  155. Q_memcpy (text, text+i, cmd_text.cursize);
  156. }
  157. // execute the command line
  158. Cmd_ExecuteString (line, src_command);
  159. if (cmd_wait)
  160. { // skip out while text still remains in buffer, leaving it
  161. // for next frame
  162. cmd_wait = false;
  163. break;
  164. }
  165. }
  166. }
  167. /*
  168. ==============================================================================
  169. SCRIPT COMMANDS
  170. ==============================================================================
  171. */
  172. /*
  173. ===============
  174. Cmd_StuffCmds_f
  175. Adds command line parameters as script statements
  176. Commands lead with a +, and continue until a - or another +
  177. quake +prog jctest.qp +cmd amlev1
  178. quake -nosound +cmd amlev1
  179. ===============
  180. */
  181. void Cmd_StuffCmds_f (void)
  182. {
  183. int i, j;
  184. int s;
  185. char *text, *build, c;
  186. // build the combined string to parse from
  187. s = 0;
  188. for (i=1 ; i<com_argc ; i++)
  189. {
  190. if (!com_argv[i])
  191. continue; // NEXTSTEP nulls out -NXHost
  192. s += Q_strlen (com_argv[i]) + 1;
  193. }
  194. if (!s)
  195. return;
  196. text = Z_Malloc (s+1);
  197. text[0] = 0;
  198. for (i=1 ; i<com_argc ; i++)
  199. {
  200. if (!com_argv[i])
  201. continue; // NEXTSTEP nulls out -NXHost
  202. Q_strcat (text,com_argv[i]);
  203. if (i != com_argc-1)
  204. Q_strcat (text, " ");
  205. }
  206. // pull out the commands
  207. build = Z_Malloc (s+1);
  208. build[0] = 0;
  209. for (i=0 ; i<s-1 ; i++)
  210. {
  211. if (text[i] == '+')
  212. {
  213. i++;
  214. for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
  215. ;
  216. c = text[j];
  217. text[j] = 0;
  218. Q_strcat (build, text+i);
  219. Q_strcat (build, "\n");
  220. text[j] = c;
  221. i = j-1;
  222. }
  223. }
  224. if (build[0])
  225. Cbuf_InsertText (build);
  226. Z_Free (text);
  227. Z_Free (build);
  228. }
  229. /*
  230. ===============
  231. Cmd_Exec_f
  232. ===============
  233. */
  234. void Cmd_Exec_f (void)
  235. {
  236. char *f;
  237. int mark;
  238. if (Cmd_Argc () != 2)
  239. {
  240. Con_Printf ("exec <filename> : execute a script file\n");
  241. return;
  242. }
  243. // FIXME: is this safe freeing the hunk here???
  244. mark = Hunk_LowMark ();
  245. f = (char *)COM_LoadHunkFile (Cmd_Argv(1));
  246. if (!f)
  247. {
  248. Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
  249. return;
  250. }
  251. if (!Cvar_Command () && (cl_warncmd.value || developer.value))
  252. Con_Printf ("execing %s\n",Cmd_Argv(1));
  253. Cbuf_InsertText (f);
  254. Hunk_FreeToLowMark (mark);
  255. }
  256. /*
  257. ===============
  258. Cmd_Echo_f
  259. Just prints the rest of the line to the console
  260. ===============
  261. */
  262. void Cmd_Echo_f (void)
  263. {
  264. int i;
  265. for (i=1 ; i<Cmd_Argc() ; i++)
  266. Con_Printf ("%s ",Cmd_Argv(i));
  267. Con_Printf ("\n");
  268. }
  269. /*
  270. ===============
  271. Cmd_Alias_f
  272. Creates a new command that executes a command string (possibly ; seperated)
  273. ===============
  274. */
  275. char *CopyString (char *in)
  276. {
  277. char *out;
  278. out = Z_Malloc (strlen(in)+1);
  279. strcpy (out, in);
  280. return out;
  281. }
  282. void Cmd_Alias_f (void)
  283. {
  284. cmdalias_t *a;
  285. char cmd[1024];
  286. int i, c;
  287. char *s;
  288. if (Cmd_Argc() == 1)
  289. {
  290. Con_Printf ("Current alias commands:\n");
  291. for (a = cmd_alias ; a ; a=a->next)
  292. Con_Printf ("%s : %s\n", a->name, a->value);
  293. return;
  294. }
  295. s = Cmd_Argv(1);
  296. if (strlen(s) >= MAX_ALIAS_NAME)
  297. {
  298. Con_Printf ("Alias name is too long\n");
  299. return;
  300. }
  301. // if the alias allready exists, reuse it
  302. for (a = cmd_alias ; a ; a=a->next)
  303. {
  304. if (!strcmp(s, a->name))
  305. {
  306. Z_Free (a->value);
  307. break;
  308. }
  309. }
  310. if (!a)
  311. {
  312. a = Z_Malloc (sizeof(cmdalias_t));
  313. a->next = cmd_alias;
  314. cmd_alias = a;
  315. }
  316. strcpy (a->name, s);
  317. // copy the rest of the command line
  318. cmd[0] = 0; // start out with a null string
  319. c = Cmd_Argc();
  320. for (i=2 ; i< c ; i++)
  321. {
  322. strcat (cmd, Cmd_Argv(i));
  323. if (i != c)
  324. strcat (cmd, " ");
  325. }
  326. strcat (cmd, "\n");
  327. a->value = CopyString (cmd);
  328. }
  329. /*
  330. =============================================================================
  331. COMMAND EXECUTION
  332. =============================================================================
  333. */
  334. typedef struct cmd_function_s
  335. {
  336. struct cmd_function_s *next;
  337. char *name;
  338. xcommand_t function;
  339. } cmd_function_t;
  340. #define MAX_ARGS 80
  341. static int cmd_argc;
  342. static char *cmd_argv[MAX_ARGS];
  343. static char *cmd_null_string = "";
  344. static char *cmd_args = NULL;
  345. cmd_source_t cmd_source;
  346. static cmd_function_t *cmd_functions; // possible commands to execute
  347. /*
  348. ============
  349. Cmd_Argc
  350. ============
  351. */
  352. int Cmd_Argc (void)
  353. {
  354. return cmd_argc;
  355. }
  356. /*
  357. ============
  358. Cmd_Argv
  359. ============
  360. */
  361. char *Cmd_Argv (int arg)
  362. {
  363. if ( arg >= cmd_argc )
  364. return cmd_null_string;
  365. return cmd_argv[arg];
  366. }
  367. /*
  368. ============
  369. Cmd_Args
  370. Returns a single string containing argv(1) to argv(argc()-1)
  371. ============
  372. */
  373. char *Cmd_Args (void)
  374. {
  375. if (!cmd_args)
  376. return "";
  377. return cmd_args;
  378. }
  379. /*
  380. ============
  381. Cmd_TokenizeString
  382. Parses the given string into command line tokens.
  383. ============
  384. */
  385. void Cmd_TokenizeString (char *text)
  386. {
  387. int i;
  388. // clear the args from the last string
  389. for (i=0 ; i<cmd_argc ; i++)
  390. Z_Free (cmd_argv[i]);
  391. cmd_argc = 0;
  392. cmd_args = NULL;
  393. while (1)
  394. {
  395. // skip whitespace up to a /n
  396. while (*text && *text <= ' ' && *text != '\n')
  397. {
  398. text++;
  399. }
  400. if (*text == '\n')
  401. { // a newline seperates commands in the buffer
  402. text++;
  403. break;
  404. }
  405. if (!*text)
  406. return;
  407. if (cmd_argc == 1)
  408. cmd_args = text;
  409. text = COM_Parse (text);
  410. if (!text)
  411. return;
  412. if (cmd_argc < MAX_ARGS)
  413. {
  414. cmd_argv[cmd_argc] = Z_Malloc (Q_strlen(com_token)+1);
  415. Q_strcpy (cmd_argv[cmd_argc], com_token);
  416. cmd_argc++;
  417. }
  418. }
  419. }
  420. /*
  421. ============
  422. Cmd_AddCommand
  423. ============
  424. */
  425. void Cmd_AddCommand (char *cmd_name, xcommand_t function)
  426. {
  427. cmd_function_t *cmd;
  428. if (host_initialized) // because hunk allocation would get stomped
  429. Sys_Error ("Cmd_AddCommand after host_initialized");
  430. // fail if the command is a variable name
  431. if (Cvar_VariableString(cmd_name)[0])
  432. {
  433. Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
  434. return;
  435. }
  436. // fail if the command already exists
  437. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  438. {
  439. if (!Q_strcmp (cmd_name, cmd->name))
  440. {
  441. Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
  442. return;
  443. }
  444. }
  445. cmd = Hunk_Alloc (sizeof(cmd_function_t));
  446. cmd->name = cmd_name;
  447. cmd->function = function;
  448. cmd->next = cmd_functions;
  449. cmd_functions = cmd;
  450. }
  451. /*
  452. ============
  453. Cmd_Exists
  454. ============
  455. */
  456. qboolean Cmd_Exists (char *cmd_name)
  457. {
  458. cmd_function_t *cmd;
  459. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  460. {
  461. if (!Q_strcmp (cmd_name,cmd->name))
  462. return true;
  463. }
  464. return false;
  465. }
  466. /*
  467. ============
  468. Cmd_CompleteCommand
  469. ============
  470. */
  471. char *Cmd_CompleteCommand (char *partial)
  472. {
  473. cmd_function_t *cmd;
  474. int len;
  475. cmdalias_t *a;
  476. len = Q_strlen(partial);
  477. if (!len)
  478. return NULL;
  479. // check for exact match
  480. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  481. if (!strcmp (partial,cmd->name))
  482. return cmd->name;
  483. for (a=cmd_alias ; a ; a=a->next)
  484. if (!strcmp (partial, a->name))
  485. return a->name;
  486. // check for partial match
  487. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  488. if (!strncmp (partial,cmd->name, len))
  489. return cmd->name;
  490. for (a=cmd_alias ; a ; a=a->next)
  491. if (!strncmp (partial, a->name, len))
  492. return a->name;
  493. return NULL;
  494. }
  495. #ifndef SERVERONLY // FIXME
  496. /*
  497. ===================
  498. Cmd_ForwardToServer
  499. adds the current command line as a clc_stringcmd to the client message.
  500. things like godmode, noclip, etc, are commands directed to the server,
  501. so when they are typed in at the console, they will need to be forwarded.
  502. ===================
  503. */
  504. void Cmd_ForwardToServer (void)
  505. {
  506. if (cls.state == ca_disconnected)
  507. {
  508. Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
  509. return;
  510. }
  511. if (cls.demoplayback)
  512. return; // not really connected
  513. MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
  514. SZ_Print (&cls.netchan.message, Cmd_Argv(0));
  515. if (Cmd_Argc() > 1) {
  516. SZ_Print (&cls.netchan.message, " ");
  517. SZ_Print (&cls.netchan.message, Cmd_Args());
  518. }
  519. #if 0
  520. MSG_WriteByte (&cls.message, clc_stringcmd);
  521. SZ_Print (&cls.message, Cmd_Argv(0));
  522. if (Cmd_Argc() > 1) {
  523. SZ_Print (&cls.message, " ");
  524. SZ_Print (&cls.message, Cmd_Args());
  525. }
  526. #endif
  527. }
  528. // don't forward the first argument
  529. void Cmd_ForwardToServer_f (void)
  530. {
  531. if (cls.state == ca_disconnected)
  532. {
  533. Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
  534. return;
  535. }
  536. if (Q_strcasecmp(Cmd_Argv(1), "snap") == 0) {
  537. Cbuf_InsertText ("snap\n");
  538. return;
  539. }
  540. if (cls.demoplayback)
  541. return; // not really connected
  542. if (Cmd_Argc() > 1) {
  543. MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
  544. SZ_Print (&cls.netchan.message, Cmd_Args());
  545. #if 0
  546. MSG_WriteByte (&cls.message, clc_stringcmd);
  547. SZ_Print (&cls.message, Cmd_Args());
  548. #endif
  549. }
  550. }
  551. #else
  552. void Cmd_ForwardToServer (void)
  553. {
  554. }
  555. #endif
  556. /*
  557. ============
  558. Cmd_ExecuteString
  559. A complete command line has been parsed, so try to execute it
  560. FIXME: lookupnoadd the token to speed search?
  561. ============
  562. */
  563. void Cmd_ExecuteString (char *text, cmd_source_t src)
  564. {
  565. cmd_function_t *cmd;
  566. cmdalias_t *a;
  567. cmd_source = src;
  568. Cmd_TokenizeString (text);
  569. // execute the command line
  570. if (!Cmd_Argc())
  571. return; // no tokens
  572. // check functions
  573. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  574. {
  575. if (!Q_strcasecmp (cmd_argv[0],cmd->name))
  576. {
  577. if (!cmd->function)
  578. Cmd_ForwardToServer ();
  579. else
  580. cmd->function ();
  581. return;
  582. }
  583. }
  584. // check alias
  585. for (a=cmd_alias ; a ; a=a->next)
  586. {
  587. if (!Q_strcasecmp (cmd_argv[0], a->name))
  588. {
  589. Cbuf_InsertText (a->value);
  590. return;
  591. }
  592. }
  593. // check cvars
  594. if (!Cvar_Command () && (cl_warncmd.value || developer.value))
  595. Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
  596. }
  597. /*
  598. ================
  599. Cmd_CheckParm
  600. Returns the position (1 to argc-1) in the command's argument list
  601. where the given parameter apears, or 0 if not present
  602. ================
  603. */
  604. int Cmd_CheckParm (char *parm)
  605. {
  606. int i;
  607. if (!parm)
  608. Sys_Error ("Cmd_CheckParm: NULL");
  609. for (i = 1; i < Cmd_Argc (); i++)
  610. if (! Q_strcasecmp (parm, Cmd_Argv (i)))
  611. return i;
  612. return 0;
  613. }
  614. /*
  615. ============
  616. Cmd_Init
  617. ============
  618. */
  619. void Cmd_Init (void)
  620. {
  621. //
  622. // register our commands
  623. //
  624. Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
  625. Cmd_AddCommand ("exec",Cmd_Exec_f);
  626. Cmd_AddCommand ("echo",Cmd_Echo_f);
  627. Cmd_AddCommand ("alias",Cmd_Alias_f);
  628. Cmd_AddCommand ("wait", Cmd_Wait_f);
  629. #ifndef SERVERONLY
  630. Cmd_AddCommand ("cmd", Cmd_ForwardToServer_f);
  631. #endif
  632. }