PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/external/quake/quake/src/QW/client/cmd.c

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