Quake II RTX doxygen  1.0 dev
cmd.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 // cmd.c -- Quake script command processing module
19 
20 #include "shared/shared.h"
21 #include "shared/list.h"
22 #include "common/cmd.h"
23 #include "common/common.h"
24 #include "common/cvar.h"
25 #include "common/error.h"
26 #include "common/files.h"
27 #include "common/prompt.h"
28 #include "common/utils.h"
29 #include "client/client.h"
30 
31 #ifdef _WINDOWS
32 #include <Windows.h>
33 #endif
34 
35 #define Cmd_Malloc(size) Z_TagMalloc(size, TAG_CMD)
36 #define Cmd_CopyString(string) Z_TagCopyString(string, TAG_CMD)
37 
38 static char *Cmd_ArgsRange(int from, int to);
39 
40 /*
41 =============================================================================
42 
43  COMMAND BUFFER
44 
45 =============================================================================
46 */
47 
48 char cmd_buffer_text[CMD_BUFFER_SIZE];
49 cmdbuf_t cmd_buffer;
50 
51 // points to the buffer current command is executed from
52 cmdbuf_t *cmd_current;
53 
54 /*
55 ============
56 Cmd_Wait_f
57 
58 Causes execution of the remainder of the command buffer to be delayed until
59 next frame. This allows commands like:
60 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
61 ============
62 */
63 static void Cmd_Wait_f(void)
64 {
65  int count;
66 
67  count = atoi(Cmd_Argv(1));
68  if (count < 1) {
69  count = 1;
70  }
71  cmd_current->waitCount += count;
72 }
73 
74 /*
75 ============
76 Cbuf_Init
77 ============
78 */
79 void Cbuf_Init(void)
80 {
81  memset(&cmd_buffer, 0, sizeof(cmd_buffer));
82  cmd_buffer.from = FROM_CONSOLE;
84  cmd_buffer.maxsize = sizeof(cmd_buffer_text);
86 }
87 
88 /*
89 ============
90 Cbuf_AddText
91 
92 Adds command text at the end of the buffer
93 ============
94 */
95 void Cbuf_AddText(cmdbuf_t *buf, const char *text)
96 {
97  size_t l = strlen(text);
98 
99  if (buf->cursize + l > buf->maxsize) {
100  Com_WPrintf("%s: overflow\n", __func__);
101  return;
102  }
103  memcpy(buf->text + buf->cursize, text, l);
104  buf->cursize += l;
105 }
106 
107 char *Cbuf_Alloc(cmdbuf_t *buf, size_t len)
108 {
109  char *text;
110 
111  if (buf->cursize + len > buf->maxsize) {
112  return NULL;
113  }
114  text = buf->text + buf->cursize;
115  buf->cursize += len;
116 
117  return text;
118 }
119 
120 /*
121 ============
122 Cbuf_InsertText
123 
124 Adds command text at the beginning of command buffer.
125 Adds a \n to the text.
126 ============
127 */
128 void Cbuf_InsertText(cmdbuf_t *buf, const char *text)
129 {
130  size_t l = strlen(text);
131 
132 // add the entire text of the file
133  if (!l) {
134  return;
135  }
136  if (buf->cursize + l + 1 > buf->maxsize) {
137  Com_WPrintf("%s: overflow\n", __func__);
138  return;
139  }
140 
141  memmove(buf->text + l + 1, buf->text, buf->cursize);
142  memcpy(buf->text, text, l);
143  buf->text[l] = '\n';
144  buf->cursize += l + 1;
145 }
146 
147 /*
148 ============
149 Cbuf_Execute
150 ============
151 */
152 void Cbuf_Execute(cmdbuf_t *buf)
153 {
154  int i;
155  char *text;
156  char line[MAX_STRING_CHARS];
157  int quotes;
158 
159  while (buf->cursize) {
160  if (buf->waitCount > 0) {
161  // skip out while text still remains in buffer, leaving it
162  // for next frame (counter is decremented externally now)
163  return;
164  }
165 
166 // find a \n or ; line break
167  text = buf->text;
168 
169  quotes = 0;
170  for (i = 0; i < buf->cursize; i++) {
171  if (text[i] == '"')
172  quotes++;
173  if (!(quotes & 1) && text[i] == ';')
174  break; // don't break if inside a quoted string
175  if (text[i] == '\n')
176  break;
177  }
178 
179  // check for overflow
180  if (i > sizeof(line) - 1) {
181  i = sizeof(line) - 1;
182  }
183 
184  memcpy(line, text, i);
185  line[i] = 0;
186 
187 // delete the text from the command buffer and move remaining commands down
188 // this is necessary because commands (exec, alias) can insert data at the
189 // beginning of the text buffer
190  if (i == buf->cursize) {
191  buf->cursize = 0;
192  } else {
193  i++;
194  buf->cursize -= i;
195  memmove(text, text + i, buf->cursize);
196  }
197 
198 // execute the command line
199  cmd_current = buf;
200  buf->exec(buf, line);
201  }
202 
203  buf->aliasCount = 0; // don't allow infinite alias loops
204 }
205 
206 /*
207 ==============================================================================
208 
209  SCRIPT COMMANDS
210 
211 ==============================================================================
212 */
213 
214 #define ALIAS_HASH_SIZE 64
215 
216 #define FOR_EACH_ALIAS_HASH(alias, hash) \
217  LIST_FOR_EACH(cmdalias_t, alias, &cmd_aliasHash[hash], hashEntry)
218 #define FOR_EACH_ALIAS(alias) \
219  LIST_FOR_EACH(cmdalias_t, alias, &cmd_alias, listEntry)
220 
221 typedef struct cmdalias_s {
222  list_t hashEntry;
223  list_t listEntry;
224  char *value;
225  char name[1];
226 } cmdalias_t;
227 
228 static list_t cmd_alias;
230 
231 /*
232 ===============
233 Cmd_AliasFind
234 ===============
235 */
236 static cmdalias_t *Cmd_AliasFind(const char *name)
237 {
238  unsigned hash;
239  cmdalias_t *alias;
240 
241  hash = Com_HashString(name, ALIAS_HASH_SIZE);
242  FOR_EACH_ALIAS_HASH(alias, hash) {
243  if (!strcmp(name, alias->name)) {
244  return alias;
245  }
246  }
247 
248  return NULL;
249 }
250 
251 char *Cmd_AliasCommand(const char *name)
252 {
253  cmdalias_t *a;
254 
255  a = Cmd_AliasFind(name);
256  if (!a) {
257  return NULL;
258  }
259 
260  return a->value;
261 }
262 
263 void Cmd_AliasSet(const char *name, const char *cmd)
264 {
265  cmdalias_t *a;
266  unsigned hash;
267  size_t len;
268 
269  // if the alias already exists, reuse it
270  a = Cmd_AliasFind(name);
271  if (a) {
272  Z_Free(a->value);
273  a->value = Cmd_CopyString(cmd);
274  return;
275  }
276 
277  len = strlen(name);
278  a = Cmd_Malloc(sizeof(cmdalias_t) + len);
279  memcpy(a->name, name, len + 1);
280  a->value = Cmd_CopyString(cmd);
281 
282  List_Append(&cmd_alias, &a->listEntry);
283 
284  hash = Com_HashString(name, ALIAS_HASH_SIZE);
285  List_Append(&cmd_aliasHash[hash], &a->hashEntry);
286 }
287 
288 void Cmd_Alias_g(genctx_t *ctx)
289 {
290  cmdalias_t *a;
291 
292  FOR_EACH_ALIAS(a) {
293  if (!Prompt_AddMatch(ctx, a->name)) {
294  break;
295  }
296  }
297 }
298 
299 
300 /*
301 ===============
302 Cmd_Alias_f
303 
304 Creates a new command that executes a command string (possibly ; seperated)
305 ===============
306 */
307 void Cmd_Alias_f(void)
308 {
309  cmdalias_t *a;
310  char *s, *cmd;
311 
312  if (Cmd_Argc() < 2) {
313  if (LIST_EMPTY(&cmd_alias)) {
314  Com_Printf("No alias commands registered.\n");
315  return;
316  }
317  Com_Printf("Registered alias commands:\n");
318  FOR_EACH_ALIAS(a) {
319  Com_Printf("\"%s\" = \"%s\"\n", a->name, a->value);
320  }
321  return;
322  }
323 
324  s = Cmd_Argv(1);
325  if (Cmd_Exists(s)) {
326  Com_Printf("\"%s\" already defined as a command\n", s);
327  return;
328  }
329 
330  if (Cvar_Exists(s, qtrue)) {
331  Com_Printf("\"%s\" already defined as a cvar\n", s);
332  return;
333  }
334 
335  if (Cmd_Argc() < 3) {
336  a = Cmd_AliasFind(s);
337  if (a) {
338  Com_Printf("\"%s\" = \"%s\"\n", a->name, a->value);
339  } else {
340  Com_Printf("\"%s\" is undefined\n", s);
341  }
342  return;
343  }
344 
345  // copy the rest of the command line
346  cmd = Cmd_ArgsFrom(2);
347  Cmd_AliasSet(s, cmd);
348 }
349 
350 static void Cmd_UnAlias_f(void)
351 {
352  static const cmd_option_t options[] = {
353  { "h", "help", "display this message" },
354  { "a", "all", "delete everything" },
355  { 0 }
356  };
357  char *s;
358  cmdalias_t *a, *n;
359  unsigned hash;
360  int c;
361 
362  while ((c = Cmd_ParseOptions(options)) != -1) {
363  switch (c) {
364  case 'h':
365  Com_Printf("Usage: %s [-ha] [name]\n", Cmd_Argv(0));
366  Cmd_PrintHelp(options);
367  return;
368  case 'a':
369  LIST_FOR_EACH_SAFE(cmdalias_t, a, n, &cmd_alias, listEntry) {
370  Z_Free(a->value);
371  Z_Free(a);
372  }
373  for (hash = 0; hash < ALIAS_HASH_SIZE; hash++) {
374  List_Init(&cmd_aliasHash[hash]);
375  }
376  List_Init(&cmd_alias);
377  Com_Printf("Removed all alias commands.\n");
378  return;
379  default:
380  return;
381  }
382  }
383 
384  if (!cmd_optarg[0]) {
385  Com_Printf("Missing alias name.\n"
386  "Try %s --help for more information.\n",
387  Cmd_Argv(0));
388  return;
389  }
390 
391  s = Cmd_Argv(1);
392  a = Cmd_AliasFind(s);
393  if (!a) {
394  Com_Printf("\"%s\" is undefined.\n", s);
395  return;
396  }
397 
398  List_Delete(&a->listEntry);
399  List_Delete(&a->hashEntry);
400 
401  Z_Free(a->value);
402  Z_Free(a);
403 }
404 
405 #if USE_CLIENT
406 void Cmd_WriteAliases(qhandle_t f)
407 {
408  cmdalias_t *a;
409 
410  FOR_EACH_ALIAS(a) {
411  FS_FPrintf(f, "alias \"%s\" \"%s\"\n", a->name, a->value);
412  }
413 }
414 #endif
415 
416 static void Cmd_Alias_c(genctx_t *ctx, int argnum)
417 {
418  if (argnum == 1) {
419  Cmd_Alias_g(ctx);
420  } else {
421  Com_Generic_c(ctx, argnum - 2);
422  }
423 }
424 
425 static void Cmd_UnAlias_c(genctx_t *ctx, int argnum)
426 {
427  if (argnum == 1) {
428  Cmd_Alias_g(ctx);
429  }
430 }
431 
432 /*
433 =============================================================================
434 
435 MESSAGE TRIGGERS
436 
437 =============================================================================
438 */
439 
440 #define FOR_EACH_TRIGGER(trig) \
441  LIST_FOR_EACH(cmd_trigger_t, trig, &cmd_triggers, entry)
442 #define FOR_EACH_TRIGGER_SAFE(trig, next) \
443  LIST_FOR_EACH_SAFE(cmd_trigger_t, trig, next, &cmd_triggers, entry)
444 
445 typedef struct {
446  list_t entry;
447  char *match;
448  char *command;
449 } cmd_trigger_t;
450 
451 static list_t cmd_triggers;
452 
453 static cmd_trigger_t *find_trigger(const char *command, const char *match)
454 {
455  cmd_trigger_t *trigger;
456 
457  FOR_EACH_TRIGGER(trigger) {
458  if (!strcmp(trigger->command, command) && !strcmp(trigger->match, match)) {
459  return trigger;
460  }
461  }
462 
463  return NULL;
464 }
465 
466 static void list_triggers(void)
467 {
468  cmd_trigger_t *trigger;
469 
470  if (LIST_EMPTY(&cmd_triggers)) {
471  Com_Printf("No current message triggers\n");
472  return;
473  }
474 
475  Com_Printf("Current message triggers:\n");
476  FOR_EACH_TRIGGER(trigger) {
477  Com_Printf("\"%s\" = \"%s\"\n", trigger->command, trigger->match);
478  }
479 }
480 
481 /*
482 ============
483 Cmd_Trigger_f
484 ============
485 */
486 static void Cmd_Trigger_f(void)
487 {
488  cmd_trigger_t *trigger;
489  const char *command, *match;
490  size_t cmdlen, matchlen;
491 
492  if (Cmd_Argc() == 1) {
493  list_triggers();
494  return;
495  }
496 
497  if (Cmd_Argc() < 3) {
498  Com_Printf("Usage: %s <command> <match>\n", Cmd_Argv(0));
499  return;
500  }
501 
502  command = Cmd_Argv(1);
503  match = Cmd_ArgsFrom(2);
504 
505  // don't create the same trigger twice
506  if (find_trigger(command, match)) {
507  return;
508  }
509 
510  cmdlen = strlen(command) + 1;
511  matchlen = strlen(match) + 1;
512  if (matchlen < 4) {
513  Com_Printf("Match string is too short\n");
514  return;
515  }
516 
517  trigger = Z_Malloc(sizeof(*trigger) + cmdlen + matchlen);
518  trigger->command = (char *)(trigger + 1);
519  trigger->match = trigger->command + cmdlen;
520  memcpy(trigger->command, command, cmdlen);
521  memcpy(trigger->match, match, matchlen);
522  List_Append(&cmd_triggers, &trigger->entry);
523 }
524 
525 static void Cmd_UnTrigger_f(void)
526 {
527  cmd_trigger_t *trigger, *next;
528  const char *command, *match;
529 
530  if (Cmd_Argc() == 1) {
531  list_triggers();
532  return;
533  }
534 
535  if (LIST_EMPTY(&cmd_triggers)) {
536  Com_Printf("No current message triggers\n");
537  return;
538  }
539 
540  if (Cmd_Argc() == 2) {
541  if (!Q_stricmp(Cmd_Argv(1), "all")) {
542  int count = 0;
543 
544  FOR_EACH_TRIGGER_SAFE(trigger, next) {
545  Z_Free(trigger);
546  count++;
547  }
548 
549  Com_Printf("Removed %d trigger%s\n", count, count == 1 ? "" : "s");
550  List_Init(&cmd_triggers);
551  return;
552  }
553 
554  Com_Printf("Usage: %s <command> <match>\n", Cmd_Argv(0));
555  return;
556  }
557 
558  command = Cmd_Argv(1);
559  match = Cmd_ArgsFrom(2);
560 
561  trigger = find_trigger(command, match);
562  if (!trigger) {
563  Com_Printf("Can't find trigger \"%s\" = \"%s\"\n", command, match);
564  return;
565  }
566 
567  List_Remove(&trigger->entry);
568  Z_Free(trigger);
569 }
570 
571 /*
572 ============
573 Cmd_ExecTrigger
574 ============
575 */
576 void Cmd_ExecTrigger(const char *string)
577 {
578  cmd_trigger_t *trigger;
579  char *match;
580 
581  // execute matching triggers
582  FOR_EACH_TRIGGER(trigger) {
583  match = Cmd_MacroExpandString(trigger->match, qfalse);
584  if (match && Com_WildCmp(match, string)) {
585  Cbuf_AddText(&cmd_buffer, trigger->command);
586  Cbuf_AddText(&cmd_buffer, "\n");
587  }
588  }
589 }
590 
591 /*
592 =============================================================================
593 
594  BRANCHING
595 
596 =============================================================================
597 */
598 
599 /*
600 ============
601 Cmd_If_f
602 ============
603 */
604 static void Cmd_If_f(void)
605 {
606  char *a, *b, *op;
607  qboolean numeric;
608  qboolean matched;
609  int i, j;
610 
611  if (Cmd_Argc() < 5) {
612  Com_Printf("Usage: if <expr> <op> <expr> [then] <command> [else <command>]\n");
613  return;
614  }
615 
616  a = Cmd_Argv(1);
617  op = Cmd_Argv(2);
618  b = Cmd_Argv(3);
619 
620  numeric = COM_IsFloat(a) && COM_IsFloat(b);
621  if (!strcmp(op, "==")) {
622  matched = numeric ? atof(a) == atof(b) : !strcmp(a, b);
623  } else if (!strcmp(op, "!=") || !strcmp(op, "<>")) {
624  matched = numeric ? atof(a) != atof(b) : strcmp(a, b);
625  } else if (!strcmp(op, "<")) {
626  if (!numeric) {
627 error:
628  Com_Printf("Can't use '%s' with non-numeric expression(s)\n", op);
629  return;
630  }
631  matched = atof(a) < atof(b);
632  } else if (!strcmp(op, "<=")) {
633  if (!numeric)
634  goto error;
635  matched = atof(a) <= atof(b);
636  } else if (!strcmp(op, ">")) {
637  if (!numeric)
638  goto error;
639  matched = atof(a) > atof(b);
640  } else if (!strcmp(op, ">=")) {
641  if (!numeric)
642  goto error;
643  matched = atof(a) >= atof(b);
644  } else if (!Q_stricmp(op, "isin")) {
645  matched = strstr(b, a) != NULL;
646  } else if (!Q_stricmp(op, "!isin")) {
647  matched = strstr(b, a) == NULL;
648  } else if (!Q_stricmp(op, "isini")) {
649  matched = Q_stristr(b, a) != NULL;
650  } else if (!Q_stricmp(op, "!isini")) {
651  matched = Q_stristr(b, a) == NULL;
652  } else if (!Q_stricmp(op, "eq")) {
653  matched = !Q_stricmp(a, b);
654  } else if (!Q_stricmp(op, "ne")) {
655  matched = Q_stricmp(a, b);
656  } else {
657  Com_Printf("Unknown operator '%s'\n", op);
658  Com_Printf("Valid are: ==, != or <>, <, <=, >, >=, [!]isin[i], eq, ne\n");
659  return;
660  }
661 
662  // skip over optional 'then'
663  i = 4;
664  if (!Q_stricmp(Cmd_Argv(i), "then")) {
665  i++;
666  }
667 
668  // scan out branch 1 argument range
669  for (j = i; i < Cmd_Argc(); i++) {
670  if (!Q_stricmp(Cmd_Argv(i), "else")) {
671  break;
672  }
673  }
674 
675  if (matched) {
676  // execute branch 1
677  if (i > j) {
679  }
680  } else {
681  // execute branch 2
682  if (++i < Cmd_Argc()) {
684  }
685  }
686 }
687 
688 /*
689 ============
690 Cmd_OpenURL_f
691 ============
692 */
693 static void Cmd_OpenURL_f(void)
694 {
695  if (Cmd_Argc() != 2)
696  {
697  Com_Printf("openurl expects a single argument that is the URL to open");
698  return;
699  }
700 
701  const char* url = Cmd_Argv(1);
702  if (Q_stricmpn(url, "http://", 7) && Q_stricmpn(url, "https://", 8))
703  {
704  Com_Printf("the URL must start with http:// or https://");
705  return;
706  }
707 
708 
709 #ifdef __linux__
710  pid_t pid = fork();
711  if (pid == 0) {
712  char * args[] = { "xdg-open", url, NULL};
713  execv("/usr/bin/xdg-open", args);
714  exit(0);
715  }
716 #elif _WINDOWS
717  ShellExecuteA(0, 0, url, 0, 0, SW_SHOW);
718 #endif
719 }
720 
721 /*
722 =============================================================================
723 
724  MACRO EXECUTION
725 
726 =============================================================================
727 */
728 
729 #define MACRO_HASH_SIZE 64
730 
731 static cmd_macro_t *cmd_macros;
732 static cmd_macro_t *cmd_macroHash[MACRO_HASH_SIZE];
733 
734 /*
735 ============
736 Cmd_FindMacro
737 ============
738 */
739 cmd_macro_t *Cmd_FindMacro(const char *name)
740 {
741  cmd_macro_t *macro;
742  unsigned hash;
743 
744  hash = Com_HashString(name, MACRO_HASH_SIZE);
745  for (macro = cmd_macroHash[hash]; macro; macro = macro->hashNext) {
746  if (!strcmp(macro->name, name)) {
747  return macro;
748  }
749  }
750 
751  return NULL;
752 }
753 
754 void Cmd_Macro_g(genctx_t *ctx)
755 {
756  cmd_macro_t *m;
757 
758  for (m = cmd_macros; m; m = m->next) {
759  if (!Prompt_AddMatch(ctx, m->name)) {
760  break;
761  }
762  }
763 }
764 
765 /*
766 ============
767 Cmd_AddMacro
768 ============
769 */
770 void Cmd_AddMacro(const char *name, xmacro_t function)
771 {
772  cmd_macro_t *macro;
773  unsigned hash;
774 
775 // fail if the macro is a variable name
776  if (Cvar_Exists(name, qfalse)) {
777  Com_WPrintf("%s: %s already defined as a cvar\n", __func__, name);
778  return;
779  }
780 
781 // fail if the macro already exists
782  macro = Cmd_FindMacro(name);
783  if (macro) {
784  if (macro->function != function) {
785  Com_WPrintf("%s: %s already defined\n", __func__, name);
786  }
787  return;
788  }
789 
790  macro = Cmd_Malloc(sizeof(cmd_macro_t));
791  macro->name = name;
792  macro->function = function;
793  macro->next = cmd_macros;
794  cmd_macros = macro;
795 
796  hash = Com_HashString(name, MACRO_HASH_SIZE);
797  macro->hashNext = cmd_macroHash[hash];
798  cmd_macroHash[hash] = macro;
799 }
800 
801 
802 /*
803 =============================================================================
804 
805  COMMAND EXECUTION
806 
807 =============================================================================
808 */
809 
810 #define CMD_HASH_SIZE 128
811 
812 #define FOR_EACH_CMD_HASH(cmd, hash) \
813  LIST_FOR_EACH(cmd_function_t, cmd, &cmd_hash[hash], hashEntry)
814 #define FOR_EACH_CMD(cmd) \
815  LIST_FOR_EACH(cmd_function_t, cmd, &cmd_functions, listEntry)
816 
817 typedef struct cmd_function_s {
818  list_t hashEntry;
819  list_t listEntry;
820 
821  xcommand_t function;
822  xcompleter_t completer;
823  char *name;
825 
826 static list_t cmd_functions; // possible commands to execute
827 static list_t cmd_hash[CMD_HASH_SIZE];
828 
829 static int cmd_argc;
830 static char *cmd_argv[MAX_STRING_TOKENS]; // pointers to cmd_data[]
831 static char *cmd_null_string = "";
832 
833 // complete command string, left untouched
834 static char cmd_string[MAX_STRING_CHARS];
835 static size_t cmd_string_len;
836 static size_t cmd_string_tail;
837 
838 // offsets of individual tokens into cmd_string
839 static size_t cmd_offsets[MAX_STRING_TOKENS];
840 
841 // sequence of NULL-terminated, normalized tokens
842 static char cmd_data[MAX_STRING_CHARS];
843 
844 // normalized command arguments
845 static char cmd_args[MAX_STRING_CHARS];
846 
850 
851 from_t Cmd_From(void)
852 {
853  return cmd_current->from;
854 }
855 
856 size_t Cmd_ArgOffset(int arg)
857 {
858  if (arg < 0) {
859  return 0;
860  }
861  if (arg >= cmd_argc) {
862  return cmd_string_len;
863  }
864  return cmd_offsets[arg];
865 }
866 
867 int Cmd_FindArgForOffset(size_t offset)
868 {
869  int i;
870 
871  for (i = 1; i < cmd_argc; i++) {
872  if (offset < cmd_offsets[i]) {
873  break;
874  }
875  }
876  return i - 1;
877 }
878 
879 size_t Cmd_WhiteSpaceTail(void)
880 {
881  return cmd_string_tail;
882 }
883 
884 /*
885 ============
886 Cmd_Argc
887 ============
888 */
889 int Cmd_Argc(void)
890 {
891  return cmd_argc;
892 }
893 
894 /*
895 ============
896 Cmd_Argv
897 ============
898 */
899 char *Cmd_Argv(int arg)
900 {
901  if (arg < 0 || arg >= cmd_argc) {
902  return cmd_null_string;
903  }
904  return cmd_argv[arg];
905 }
906 
907 /*
908 ============
909 Cmd_ArgvBuffer
910 ============
911 */
912 size_t Cmd_ArgvBuffer(int arg, char *buffer, size_t size)
913 {
914  char *s;
915 
916  if (arg < 0 || arg >= cmd_argc) {
917  s = cmd_null_string;
918  } else {
919  s = cmd_argv[arg];
920  }
921 
922  return Q_strlcpy(buffer, s, size);
923 }
924 
925 
926 /*
927 ============
928 Cmd_Args
929 
930 Returns a single string containing argv(1) to argv(argc()-1)
931 ============
932 */
933 char *Cmd_Args(void)
934 {
935  int i;
936 
937  if (cmd_argc < 2) {
938  return cmd_null_string;
939  }
940 
941  cmd_args[0] = 0;
942  for (i = 1; i < cmd_argc - 1; i++) {
943  strcat(cmd_args, cmd_argv[i]);
944  strcat(cmd_args, " ");
945  }
946  strcat(cmd_args, cmd_argv[i]);
947 
948  return cmd_args;
949 }
950 
951 char *Cmd_RawArgs(void)
952 {
953  if (cmd_argc < 2) {
954  return cmd_null_string;
955  }
956  return cmd_string + cmd_offsets[1];
957 }
958 
959 char *Cmd_RawString(void)
960 {
961  return cmd_string;
962 }
963 
964 /*
965 ============
966 Cmd_ArgsBuffer
967 ============
968 */
969 size_t Cmd_ArgsBuffer(char *buffer, size_t size)
970 {
971  return Q_strlcpy(buffer, Cmd_Args(), size);
972 }
973 
974 /*
975 ============
976 Cmd_ArgsFrom
977 
978 Returns a single string containing argv(1) to argv(from-1)
979 ============
980 */
981 char *Cmd_ArgsFrom(int from)
982 {
983  int i;
984 
985  if (from < 0 || from >= cmd_argc) {
986  return cmd_null_string;
987  }
988 
989  cmd_args[0] = 0;
990  for (i = from; i < cmd_argc - 1; i++) {
991  strcat(cmd_args, cmd_argv[i]);
992  strcat(cmd_args, " ");
993  }
994  strcat(cmd_args, cmd_argv[i]);
995 
996  return cmd_args;
997 }
998 
999 static char *Cmd_ArgsRange(int from, int to)
1000 {
1001  int i;
1002 
1003  if (from < 0 || from >= cmd_argc) {
1004  return cmd_null_string;
1005  }
1006 
1007  if (to > cmd_argc - 1) {
1008  to = cmd_argc - 1;
1009  }
1010 
1011  cmd_args[0] = 0;
1012  for (i = from; i < to; i++) {
1013  strcat(cmd_args, cmd_argv[i]);
1014  strcat(cmd_args, " ");
1015  }
1016  strcat(cmd_args, cmd_argv[i]);
1017 
1018  return cmd_args;
1019 }
1020 
1021 char *Cmd_RawArgsFrom(int from)
1022 {
1023  size_t offset;
1024 
1025  if (from < 0 || from >= cmd_argc) {
1026  return cmd_null_string;
1027  }
1028 
1029  offset = cmd_offsets[from];
1030 
1031  return cmd_string + offset;
1032 }
1033 
1034 void Cmd_Shift(void)
1035 {
1036  int i;
1037 
1038  if (!cmd_argc) {
1039  return;
1040  }
1041 
1042  if (cmd_argc == 1) {
1043  cmd_string[0] = 0;
1044  return;
1045  }
1046 
1047  cmd_argc--;
1048  for (i = 0; i < cmd_argc; i++) {
1049  cmd_offsets[i] = cmd_offsets[i + 1];
1050  cmd_argv[i] = cmd_argv[i + 1];
1051  }
1052 
1053  memmove(cmd_string, cmd_string + cmd_offsets[1],
1054  MAX_STRING_CHARS - cmd_offsets[1]);
1055 }
1056 
1057 int Cmd_ParseOptions(const cmd_option_t *opt)
1058 {
1059  const cmd_option_t *o;
1060  char *s, *p;
1061 
1063 
1064  if (cmd_optind == cmd_argc) {
1066  return -1; // no more arguments
1067  }
1068 
1069  s = cmd_argv[cmd_optind];
1070  if (*s != '-') {
1071  cmd_optarg = s;
1072  return -1; // non-option argument
1073  }
1074  cmd_optopt = s++;
1075 
1076  if (*s == '-') {
1077  s++;
1078  if (*s == 0) {
1079  if (++cmd_optind < cmd_argc) {
1081  } else {
1083  }
1084  return -1; // special terminator
1085  }
1086 
1087  // check for long option argument
1088  if ((p = strchr(s, '=')) != NULL) {
1089  *p = 0;
1090  }
1091 
1092  // parse long option
1093  for (o = opt; o->sh; o++) {
1094  if (!strcmp(o->lo, s)) {
1095  break;
1096  }
1097  }
1098  if (!o->sh) {
1099  goto unknown;
1100  }
1101 
1102  // parse long option argument
1103  if (p) {
1104  if (o->sh[1] != ':') {
1105  Com_Printf("%s does not take an argument.\n", cmd_argv[cmd_optind]);
1106  Cmd_PrintHint();
1107  return '!';
1108  }
1109  cmd_optarg = p + 1;
1110  }
1111  } else {
1112  // parse short option
1113  for (o = opt; o->sh; o++) {
1114  if (o->sh[0] == *s) {
1115  break;
1116  }
1117  }
1118  if (!o->sh || s[1]) {
1119  goto unknown;
1120  }
1121  p = NULL;
1122  }
1123 
1124  // parse option argument
1125  if (!p && o->sh[1] == ':') {
1126  if (cmd_optind + 1 == cmd_argc) {
1127  Com_Printf("Missing argument to %s.\n", cmd_argv[cmd_optind]);
1128  Cmd_PrintHint();
1129  return ':';
1130  }
1132  }
1133 
1134  cmd_optind++;
1135  return o->sh[0];
1136 
1137 unknown:
1138  Com_Printf("Unknown option: %s.\n", cmd_argv[cmd_optind]);
1139  Cmd_PrintHint();
1140  return '?';
1141 }
1142 
1143 void Cmd_PrintUsage(const cmd_option_t *opt, const char *suffix)
1144 {
1145  Com_Printf("Usage: %s [-", cmd_argv[0]);
1146  while (opt->sh) {
1147  Com_Printf("%c", opt->sh[0]);
1148  if (opt->sh[1] == ':') {
1149  Com_Printf(":");
1150  }
1151  opt++;
1152  }
1153  if (suffix) {
1154  Com_Printf("] %s\n", suffix);
1155  } else {
1156  Com_Printf("]\n");
1157  }
1158 }
1159 
1160 void Cmd_PrintHelp(const cmd_option_t *opt)
1161 {
1162  char buffer[32];
1163 
1164  Com_Printf("\nAvailable options:\n");
1165  while (opt->sh) {
1166  if (opt->sh[1] == ':') {
1167  Q_concat(buffer, sizeof(buffer),
1168  opt->lo, "=<", opt->sh + 2, ">", NULL);
1169  } else {
1170  Q_strlcpy(buffer, opt->lo, sizeof(buffer));
1171  }
1172  Com_Printf("-%c | --%-16.16s | %s\n", opt->sh[0], buffer, opt->help);
1173  opt++;
1174  }
1175  Com_Printf("\n");
1176 }
1177 
1178 void Cmd_PrintHint(void)
1179 {
1180  Com_Printf("Try '%s --help' for more information.\n", cmd_argv[0]);
1181 }
1182 
1183 void Cmd_Option_c(const cmd_option_t *opt, xgenerator_t g, genctx_t *ctx, int argnum)
1184 {
1185  if (ctx->partial[0] == '-') {
1186  for (; opt->sh; opt++) {
1187  if (ctx->count >= ctx->size) {
1188  break;
1189  }
1190  if (ctx->partial[1] == '-') {
1191  if (!strncmp(opt->lo, ctx->partial + 2, ctx->length - 2)) {
1192  ctx->matches[ctx->count++] = Z_CopyString(va("--%s", opt->lo));
1193  }
1194  } else if (!ctx->partial[1] || opt->sh[0] == ctx->partial[1]) {
1195  ctx->matches[ctx->count++] = Z_CopyString(va("-%c", opt->sh[0]));
1196  }
1197  }
1198  } else {
1199 #if 0
1200  if (argnum > 1) {
1201  s = cmd_argv[argnum - 1];
1202  }
1203 #endif
1204  if (g) {
1205  g(ctx);
1206  } else if (!ctx->partial[0] && ctx->count < ctx->size) {
1207  ctx->matches[ctx->count++] = Z_CopyString("-");
1208  }
1209  }
1210 }
1211 
1212 
1213 /*
1214 ======================
1215 Cmd_MacroExpandString
1216 ======================
1217 */
1218 char *Cmd_MacroExpandString(const char *text, qboolean aliasHack)
1219 {
1220  size_t i, j, len;
1221  int count;
1222  qboolean inquote;
1223  char *scan, *start;
1224  static char expanded[MAX_STRING_CHARS];
1225  char temporary[MAX_STRING_CHARS];
1226  char buffer[MAX_STRING_CHARS];
1227  char *token;
1228  cmd_macro_t *macro;
1229  cvar_t *var;
1230  qboolean rescan;
1231 
1232  len = strlen(text);
1233  if (len >= MAX_STRING_CHARS) {
1234  Com_Printf("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
1235  return NULL;
1236  }
1237 
1238  scan = memcpy(expanded, text, len + 1);
1239 
1240  inquote = qfalse;
1241  count = 0;
1242 
1243  for (i = 0; i < len; i++) {
1244  if (!scan[i]) {
1245  break;
1246  }
1247  if (scan[i] == '"') {
1248  inquote ^= 1;
1249  }
1250  if (inquote) {
1251  continue; // don't expand inside quotes
1252  }
1253  if (scan[i] != '$') {
1254  continue;
1255  }
1256 
1257  // scan out the complete macro
1258  start = scan + i + 1;
1259 
1260  if (*start == 0) {
1261  break; // end of string
1262  }
1263 
1264  // allow $$ escape syntax
1265  if (*start == '$') {
1266  memmove(scan + i, start, len - i);
1267  continue;
1268  }
1269 
1270  // skip leading spaces
1271  while (*start && *start <= 32) {
1272  start++;
1273  }
1274 
1275  token = temporary;
1276 
1277  if (*start == '{') {
1278  // allow ${variable} syntax
1279  start++;
1280  if (*start == '$') { // allow ${$variable} syntax
1281  start++;
1282  }
1283  while (*start) {
1284  if (*start == '}') {
1285  start++;
1286  break;
1287  }
1288  *token++ = *start++;
1289  }
1290  } else {
1291  // parse single word
1292  while (*start > 32) {
1293  if (*start == '$') { // allow $var$ syntax
1294  start++;
1295  break;
1296  }
1297  *token++ = *start++;
1298  }
1299  }
1300 
1301  *token = 0;
1302 
1303  if (token == temporary) {
1304  continue;
1305  }
1306 
1307  rescan = qfalse;
1308 
1309  if (aliasHack) {
1310  // expand positional parameters only
1311  if (!strcmp(temporary, "@")) {
1312  token = Cmd_Args();
1313  } else {
1314  int arg1, arg2;
1315  char *s;
1316 
1317  // parse {arg1-arg2} format for ranges
1318  arg1 = strtoul(temporary, &s, 10);
1319  if (s[0] == '-') {
1320  if (s[1]) {
1321  arg2 = strtoul(s + 1, &s, 10);
1322  if (s[0]) {
1323  continue; // second part is not a number
1324  }
1325  } else {
1326  arg2 = cmd_argc - 1;
1327  }
1328  token = Cmd_ArgsRange(arg1, arg2);
1329  } else if (s[0] == 0) {
1330  token = Cmd_Argv(arg1);
1331  } else {
1332  continue; // first part is not a number
1333  }
1334  }
1335  } else {
1336  // check for macros first
1337  macro = Cmd_FindMacro(temporary);
1338  if (macro) {
1339  macro->function(buffer, MAX_STRING_CHARS - len);
1340  token = buffer;
1341  } else {
1342  // than variables
1343  var = Cvar_FindVar(temporary);
1344  if (var && !(var->flags & CVAR_PRIVATE)) {
1345  token = var->string;
1346  rescan = qtrue;
1347  } else if (!strcmp(temporary, "qt")) {
1348  token = "\"";
1349  } else if (!strcmp(temporary, "sc")) {
1350  token = ";";
1351  } else {
1352  token = "";
1353  }
1354  }
1355  }
1356 
1357  j = strlen(token);
1358  len += j;
1359  if (len >= MAX_STRING_CHARS) {
1360  Com_Printf("Expanded line exceeded %i chars, discarded.\n",
1361  MAX_STRING_CHARS);
1362  Com_Printf("was %s\n", text);
1363  return NULL;
1364  }
1365 
1366  strncpy(temporary, scan, i);
1367  strcpy(temporary + i, token);
1368  strcpy(temporary + i + j, start);
1369 
1370  strcpy(expanded, temporary);
1371  scan = expanded;
1372  if (!rescan) {
1373  i += j;
1374  }
1375  i--;
1376 
1377  if (++count == 100) {
1378  Com_Printf("Macro expansion loop, discarded.\n");
1379  return NULL;
1380  }
1381  }
1382 
1383  if (inquote) {
1384  Com_Printf("Line has unmatched quote, discarded.\n");
1385  return NULL;
1386  }
1387 
1388  return scan;
1389 }
1390 
1391 /*
1392 ============
1393 Cmd_TokenizeString
1394 
1395 Parses the given string into command line tokens.
1396 $Cvars will be expanded unless they are in a quoted token
1397 ============
1398 */
1399 void Cmd_TokenizeString(const char *text, qboolean macroExpand)
1400 {
1401  int i;
1402  char *data, *start, *dest;
1403 
1404 // clear the args from the last string
1405  for (i = 0; i < cmd_argc; i++) {
1406  cmd_argv[i] = NULL;
1407  cmd_offsets[i] = 0;
1408  }
1409 
1410  cmd_argc = 0;
1411  cmd_string[0] = 0;
1412  cmd_string_len = 0;
1413  cmd_string_tail = 0;
1414  cmd_optind = 1;
1416 
1417  if (!text[0]) {
1418  return;
1419  }
1420 
1421 // macro expand the text
1422  if (macroExpand) {
1423  text = Cmd_MacroExpandString(text, qfalse);
1424  if (!text) {
1425  return;
1426  }
1427  }
1428 
1429  cmd_string_len = Q_strlcpy(cmd_string, text, sizeof(cmd_string));
1430  if (cmd_string_len >= sizeof(cmd_string)) {
1431  Com_Printf("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
1432  return;
1433  }
1434 
1435 // strip off any trailing whitespace
1436  while (cmd_string_len) {
1437  if (cmd_string[cmd_string_len - 1] > ' ') {
1438  break;
1439  }
1440  cmd_string[cmd_string_len - 1] = 0;
1441  cmd_string_len--;
1442  cmd_string_tail++;
1443  }
1444 
1445  dest = cmd_data;
1446  start = data = cmd_string;
1447  while (cmd_argc < MAX_STRING_TOKENS) {
1448 // skip whitespace up to a /n
1449  while (*data <= ' ') {
1450  if (*data == 0) {
1451  return; // end of text
1452  }
1453  if (*data == '\n') {
1454  return; // a newline seperates commands in the buffer
1455  }
1456  data++;
1457  }
1458 
1459 // add new argument
1460  cmd_offsets[cmd_argc] = data - start;
1461  cmd_argv[cmd_argc] = dest;
1462  cmd_argc++;
1463 
1464  if (*data == ';') {
1465  data++;
1466  *dest++ = ';';
1467  *dest++ = 0;
1468  continue;
1469  }
1470 
1471 // parse quoted string
1472  if (*data == '\"') {
1473  data++;
1474  while (*data != '\"') {
1475  if (*data == 0) {
1476  return; // end of data
1477  }
1478  *dest++ = *data++;
1479  }
1480  data++;
1481  *dest++ = 0;
1482  continue;
1483  }
1484 
1485 // parse reqular token
1486  while (*data > ' ') {
1487  if (*data == '\"') {
1488  break;
1489  }
1490  if (*data == ';') {
1491  break;
1492  }
1493  *dest++ = *data++;
1494  }
1495  *dest++ = 0;
1496 
1497  if (*data == 0) {
1498  return; // end of text
1499  }
1500  }
1501 }
1502 
1503 /*
1504 ============
1505 Cmd_Find
1506 ============
1507 */
1508 static cmd_function_t *Cmd_Find(const char *name)
1509 {
1510  cmd_function_t *cmd;
1511  unsigned hash;
1512 
1513  hash = Com_HashString(name, CMD_HASH_SIZE);
1514  FOR_EACH_CMD_HASH(cmd, hash) {
1515  if (!strcmp(cmd->name, name)) {
1516  return cmd;
1517  }
1518  }
1519 
1520  return NULL;
1521 }
1522 
1523 static void Cmd_RegCommand(const cmdreg_t *reg)
1524 {
1525  cmd_function_t *cmd;
1526  unsigned hash;
1527 
1528 // fail if the command is a variable name
1529  if (Cvar_Exists(reg->name, qfalse)) {
1530  Com_WPrintf("%s: %s already defined as a cvar\n", __func__, reg->name);
1531  return;
1532  }
1533 
1534 // fail if the command already exists
1535  cmd = Cmd_Find(reg->name);
1536  if (cmd) {
1537  if (cmd->function) {
1538  Com_WPrintf("%s: %s already defined\n", __func__, reg->name);
1539  return;
1540  }
1541  cmd->function = reg->function;
1542  cmd->completer = reg->completer;
1543  return;
1544  }
1545 
1546  cmd = Cmd_Malloc(sizeof(*cmd));
1547  cmd->name = (char *)reg->name;
1548  cmd->function = reg->function;
1549  cmd->completer = reg->completer;
1550 
1551  List_Append(&cmd_functions, &cmd->listEntry);
1552 
1553  hash = Com_HashString(reg->name, CMD_HASH_SIZE);
1554  List_Append(&cmd_hash[hash], &cmd->hashEntry);
1555 }
1556 
1557 /*
1558 ============
1559 Cmd_AddCommand
1560 ============
1561 */
1562 void Cmd_AddCommand(const char *name, xcommand_t function)
1563 {
1564  cmdreg_t reg;
1565 
1566  reg.name = name;
1567  reg.function = function;
1568  reg.completer = NULL;
1569  Cmd_RegCommand(&reg);
1570 }
1571 
1572 void Cmd_Register(const cmdreg_t *reg)
1573 {
1574  while (reg->name) {
1575  Cmd_RegCommand(reg);
1576  reg++;
1577  }
1578 }
1579 
1580 void Cmd_Deregister(const cmdreg_t *reg)
1581 {
1582  while (reg->name) {
1583  Cmd_RemoveCommand(reg->name);
1584  reg++;
1585  }
1586 }
1587 
1588 /*
1589 ============
1590 Cmd_RemoveCommand
1591 ============
1592 */
1593 void Cmd_RemoveCommand(const char *name)
1594 {
1595  cmd_function_t *cmd;
1596 
1597  cmd = Cmd_Find(name);
1598  if (!cmd) {
1599  Com_DPrintf("%s: %s not added\n", __func__, name);
1600  return;
1601  }
1602 
1603  List_Delete(&cmd->listEntry);
1604  List_Delete(&cmd->hashEntry);
1605  Z_Free(cmd);
1606 }
1607 
1608 /*
1609 ============
1610 Cmd_Exists
1611 ============
1612 */
1613 qboolean Cmd_Exists(const char *name)
1614 {
1615  cmd_function_t *cmd = Cmd_Find(name);
1616 
1617  return cmd ? qtrue : qfalse;
1618 }
1619 
1620 xcommand_t Cmd_FindFunction(const char *name)
1621 {
1622  cmd_function_t *cmd = Cmd_Find(name);
1623 
1624  return cmd ? cmd->function : NULL;
1625 }
1626 
1627 xcompleter_t Cmd_FindCompleter(const char *name)
1628 {
1629  cmd_function_t *cmd = Cmd_Find(name);
1630 
1631  return cmd ? cmd->completer : NULL;
1632 }
1633 
1634 void Cmd_Command_g(genctx_t *ctx)
1635 {
1636  cmd_function_t *cmd;
1637 
1638  FOR_EACH_CMD(cmd) {
1639  if (!Prompt_AddMatch(ctx, cmd->name)) {
1640  break;
1641  }
1642  }
1643 }
1644 
1645 void Cmd_ExecuteCommand(cmdbuf_t *buf)
1646 {
1647  cmd_function_t *cmd;
1648  cmdalias_t *a;
1649  cvar_t *v;
1650  char *text;
1651 
1652  // check functions
1653  cmd = Cmd_Find(cmd_argv[0]);
1654  if (cmd) {
1655  if (cmd->function) {
1656  cmd->function();
1657  } else if (!CL_ForwardToServer()) {
1658  Com_Printf("Can't \"%s\", not connected\n", cmd_argv[0]);
1659  }
1660  return;
1661  }
1662 
1663  // check aliases
1664  a = Cmd_AliasFind(cmd_argv[0]);
1665  if (a) {
1666  if (buf->aliasCount >= ALIAS_LOOP_COUNT) {
1667  Com_WPrintf("Runaway alias loop\n");
1668  return;
1669  }
1670  text = Cmd_MacroExpandString(a->value, qtrue);
1671  if (text) {
1672  buf->aliasCount++;
1673  Cbuf_InsertText(buf, text);
1674  }
1675  return;
1676  }
1677 
1678  // check variables
1679  v = Cvar_FindVar(cmd_argv[0]);
1680  if (v) {
1681  Cvar_Command(v);
1682  return;
1683  }
1684 
1685  // send it as a server command if we are connected
1686  if (!CL_ForwardToServer()) {
1687  Com_Printf("Unknown command \"%s\"\n", cmd_argv[0]);
1688  }
1689 }
1690 
1691 /*
1692 ============
1693 Cmd_ExecuteString
1694 
1695 A complete command line has been parsed, so try to execute it
1696 ============
1697 */
1698 void Cmd_ExecuteString(cmdbuf_t *buf, const char *text)
1699 {
1700  Cmd_TokenizeString(text, qtrue);
1701 
1702  // execute the command line
1703  if (!cmd_argc) {
1704  return; // no tokens
1705  }
1706 
1707  Cmd_ExecuteCommand(buf);
1708 }
1709 
1710 qerror_t Cmd_ExecuteFile(const char *path, unsigned flags)
1711 {
1712  char *f;
1713  ssize_t len;
1714  qerror_t ret;
1715  cmdbuf_t *buf;
1716 
1717  len = FS_LoadFileEx(path, (void **)&f, flags, TAG_FILESYSTEM);
1718  if (!f) {
1719  return len;
1720  }
1721 
1722  // check for binary file
1723  if (memchr(f, 0, len)) {
1724  ret = Q_ERR_INVALID_FORMAT;
1725  goto finish;
1726  }
1727 
1728  // sanity check file size after stripping off comments
1729  len = COM_Compress(f);
1730  if (len > CMD_BUFFER_SIZE) {
1731  ret = Q_ERR_FBIG;
1732  goto finish;
1733  }
1734 
1735  // FIXME: always insert into main command buffer,
1736  // no matter where command came from?
1737  buf = &cmd_buffer;
1738 
1739  // check for exec loop
1740  if (++buf->aliasCount > ALIAS_LOOP_COUNT) {
1741  ret = Q_ERR_RUNAWAY_LOOP;
1742  goto finish;
1743  }
1744 
1745  // check for overflow
1746  if (buf->cursize + len + 1 > buf->maxsize) {
1747  ret = Q_ERR_STRING_TRUNCATED;
1748  goto finish;
1749  }
1750 
1751  // everything ok, execute it
1752  Com_Printf("Execing %s\n", path);
1753  Cbuf_InsertText(buf, f);
1754  ret = Q_ERR_SUCCESS;
1755 
1756 finish:
1757  FS_FreeFile(f);
1758  return ret;
1759 }
1760 
1761 /*
1762 ===============
1763 Cmd_Exec_f
1764 ===============
1765 */
1766 static void Cmd_Exec_f(void)
1767 {
1768  char buffer[MAX_QPATH];
1769  size_t len;
1770  qerror_t ret;
1771 
1772  if (Cmd_Argc() != 2) {
1773  Com_Printf("%s <filename> : execute a script file\n", Cmd_Argv(0));
1774  return;
1775  }
1776 
1777  len = FS_NormalizePathBuffer(buffer, Cmd_Argv(1), sizeof(buffer));
1778  if (len >= sizeof(buffer)) {
1779  Q_PrintError("exec", Q_ERR_NAMETOOLONG);
1780  return;
1781  }
1782 
1783  if (len == 0) {
1784  Q_PrintError("exec", Q_ERR_NAMETOOSHORT);
1785  return;
1786  }
1787 
1788  ret = Cmd_ExecuteFile(buffer, 0);
1789  if (ret == Q_ERR_NOENT && COM_CompareExtension(buffer, ".cfg")) {
1790  // try with .cfg extension
1791  len = Q_strlcat(buffer, ".cfg", sizeof(buffer));
1792  if (len >= sizeof(buffer)) {
1793  Q_PrintError("exec", Q_ERR_NAMETOOLONG);
1794  return;
1795  }
1796 
1797  ret = Cmd_ExecuteFile(buffer, 0);
1798  }
1799 
1800  if (ret) {
1801  Com_Printf("Couldn't exec %s: %s\n", buffer, Q_ErrorString(ret));
1802  }
1803 }
1804 
1805 void Cmd_Config_g(genctx_t *ctx)
1806 {
1807  FS_File_g(NULL, "*.cfg", FS_SEARCH_SAVEPATH | FS_SEARCH_BYFILTER | FS_SEARCH_STRIPEXT, ctx);
1808 }
1809 
1810 static void Cmd_Exec_c(genctx_t *ctx, int argnum)
1811 {
1812  if (argnum == 1) {
1813  Cmd_Config_g(ctx);
1814  }
1815 }
1816 
1817 /*
1818 ===============
1819 Cmd_Echo_f
1820 
1821 Just prints the rest of the line to the console
1822 ===============
1823 */
1824 static void Cmd_Echo_f(void)
1825 {
1826  Com_Printf("%s\n", Cmd_Args());
1827 }
1828 
1829 static const cmd_option_t o_echo[] = {
1830  { "h", "help", "display this message" },
1831  { "e", "escapes", "enable interpretation of backslash escapes" },
1832  { "c:color", "color", "print text in this color" },
1833  { "n", "no-newline", "do not output the trailing newline" },
1834  { NULL }
1835 };
1836 
1837 static void Cmd_EchoEx_c(genctx_t *ctx, int argnum)
1838 {
1839  Cmd_Option_c(o_echo, NULL, ctx, argnum);
1840 }
1841 
1842 static char *unescape_string(char *dst, const char *src)
1843 {
1844  int c1, c2;
1845  char *p = dst;
1846 
1847  while (*src) {
1848  if (src[0] == '\\' && src[1]) {
1849  switch (src[1]) {
1850  case 'a': *p++ = '\a'; break;
1851  case 'b': *p++ = '\b'; break;
1852  case 't': *p++ = '\t'; break;
1853  case 'n': *p++ = '\n'; break;
1854  case 'v': *p++ = '\v'; break;
1855  case 'f': *p++ = '\f'; break;
1856  case 'r': *p++ = '\r'; break;
1857  case '\\': *p++ = '\\'; break;
1858  case 'x':
1859  if ((c1 = Q_charhex(src[2])) == -1) {
1860  break;
1861  }
1862  if ((c2 = Q_charhex(src[3])) == -1) {
1863  break;
1864  }
1865  *p++ = (c1 << 4) | c2;
1866  src += 2;
1867  break;
1868  default:
1869  *p++ = src[1];
1870  break;
1871  }
1872  src += 2;
1873  } else {
1874  *p++ = *src++;
1875  }
1876  }
1877  *p = 0;
1878 
1879  return dst;
1880 }
1881 
1882 static void Cmd_EchoEx_f(void)
1883 {
1884  char buffer[MAX_STRING_CHARS], *s;
1885  qboolean escapes = qfalse;
1886  color_index_t color = COLOR_NONE;
1887  const char *newline = "\n";
1888  int c;
1889 
1890  while ((c = Cmd_ParseOptions(o_echo)) != -1) {
1891  switch (c) {
1892  case 'h':
1893  Cmd_PrintUsage(o_echo, "[text]");
1894  Com_Printf("Print a line of text into the console.\n");
1896  return;
1897  case 'e':
1898  escapes = qtrue;
1899  break;
1900  case 'c':
1901  color = Com_ParseColor(cmd_optarg, COLOR_NONE);
1902  break;
1903  case 'n':
1904  newline = "";
1905  break;
1906  default:
1907  return;
1908  }
1909  }
1910 
1912  if (escapes) {
1913  s = unescape_string(buffer, s);
1914  }
1915 
1917  Com_Printf("%s%s", s, newline);
1918  Com_SetColor(COLOR_NONE);
1919 }
1920 
1921 /*
1922 ============
1923 Cmd_List_f
1924 ============
1925 */
1926 static void Cmd_List_f(void)
1927 {
1928  cmd_function_t *cmd;
1929  int i, total;
1930  char *filter = NULL;
1931 
1932  if (cmd_argc > 1) {
1933  filter = cmd_argv[1];
1934  }
1935 
1936  i = total = 0;
1937  FOR_EACH_CMD(cmd) {
1938  total++;
1939  if (filter && !Com_WildCmp(filter, cmd->name)) {
1940  continue;
1941  }
1942  Com_Printf("%s\n", cmd->name);
1943  i++;
1944  }
1945  Com_Printf("%i of %i commands\n", i, total);
1946 }
1947 
1948 /*
1949 ============
1950 Cmd_MacroList_f
1951 ============
1952 */
1953 static void Cmd_MacroList_f(void)
1954 {
1955  cmd_macro_t *macro;
1956  int i, total;
1957  char *filter = NULL;
1958  char buffer[MAX_QPATH];
1959 
1960  if (cmd_argc > 1) {
1961  filter = cmd_argv[1];
1962  }
1963 
1964  i = 0;
1965  for (macro = cmd_macros, total = 0; macro; macro = macro->next, total++) {
1966  if (filter && !Com_WildCmp(filter, macro->name)) {
1967  continue;
1968  }
1969  macro->function(buffer, sizeof(buffer));
1970  Com_Printf("%-16s %s\n", macro->name, buffer);
1971  i++;
1972  }
1973  Com_Printf("%i of %i macros\n", i, total);
1974 }
1975 
1976 static void Cmd_Text_f(void)
1977 {
1979  Cbuf_AddText(cmd_current, "\n");
1980 }
1981 
1982 static void Cmd_Complete_f(void)
1983 {
1984  cmd_function_t *cmd;
1985  char *name;
1986  unsigned hash;
1987  size_t len;
1988 
1989  if (cmd_argc < 2) {
1990  Com_Printf("Usage: %s <command>", cmd_argv[0]);
1991  return;
1992  }
1993 
1994  name = cmd_argv[1];
1995 
1996 // fail if the command is a variable name
1997  if (Cvar_Exists(name, qtrue)) {
1998  Com_Printf("%s is already defined as a cvar\n", name);
1999  return;
2000  }
2001 
2002 // fail if the command already exists
2003  cmd = Cmd_Find(name);
2004  if (cmd) {
2005  //Com_Printf("%s is already defined\n", name);
2006  return;
2007  }
2008 
2009  len = strlen(name) + 1;
2010  cmd = Cmd_Malloc(sizeof(*cmd) + len);
2011  cmd->name = (char *)(cmd + 1);
2012  memcpy(cmd->name, name, len);
2013  cmd->function = NULL;
2014  cmd->completer = NULL;
2015 
2016  List_Append(&cmd_functions, &cmd->listEntry);
2017 
2018  hash = Com_HashString(name, CMD_HASH_SIZE);
2019  List_Append(&cmd_hash[hash], &cmd->hashEntry);
2020 }
2021 
2022 static const cmdreg_t c_cmd[] = {
2023  { "cmdlist", Cmd_List_f },
2024  { "macrolist", Cmd_MacroList_f },
2025  { "exec", Cmd_Exec_f, Cmd_Exec_c },
2026  { "echo", Cmd_Echo_f },
2027  { "_echo", Cmd_EchoEx_f, Cmd_EchoEx_c },
2028  { "alias", Cmd_Alias_f, Cmd_Alias_c },
2029  { "unalias", Cmd_UnAlias_f, Cmd_UnAlias_c },
2030  { "wait", Cmd_Wait_f },
2031  { "text", Cmd_Text_f },
2032  { "complete", Cmd_Complete_f },
2033  { "trigger", Cmd_Trigger_f },
2034  { "untrigger", Cmd_UnTrigger_f },
2035  { "if", Cmd_If_f },
2036  { "openurl", Cmd_OpenURL_f },
2037 
2038  { NULL }
2039 };
2040 
2041 /*
2042 ============
2043 Cmd_Init
2044 ============
2045 */
2046 void Cmd_Init(void)
2047 {
2048  int i;
2049 
2050  List_Init(&cmd_functions);
2051  for (i = 0; i < CMD_HASH_SIZE; i++) {
2052  List_Init(&cmd_hash[i]);
2053  }
2054 
2055  List_Init(&cmd_alias);
2056  for (i = 0; i < ALIAS_HASH_SIZE; i++) {
2057  List_Init(&cmd_aliasHash[i]);
2058  }
2059 
2060  List_Init(&cmd_triggers);
2061 
2063 }
2064 
Com_ParseColor
color_index_t Com_ParseColor(const char *s, color_index_t last)
Definition: utils.c:204
Cmd_Alias_g
void Cmd_Alias_g(genctx_t *ctx)
Definition: cmd.c:288
cmd_string_len
static size_t cmd_string_len
Definition: cmd.c:835
Q_strlcat
size_t Q_strlcat(char *dst, const char *src, size_t size)
Definition: shared.c:735
c_cmd
static const cmdreg_t c_cmd[]
Definition: cmd.c:2022
COM_IsFloat
qboolean COM_IsFloat(const char *s)
Definition: shared.c:307
Cmd_WhiteSpaceTail
size_t Cmd_WhiteSpaceTail(void)
Definition: cmd.c:879
Cmd_ExecuteFile
qerror_t Cmd_ExecuteFile(const char *path, unsigned flags)
Definition: cmd.c:1710
Cmd_Trigger_f
static void Cmd_Trigger_f(void)
Definition: cmd.c:486
cmd_optarg
char * cmd_optarg
Definition: cmd.c:848
ALIAS_HASH_SIZE
#define ALIAS_HASH_SIZE
Definition: cmd.c:214
Cmd_FindCompleter
xcompleter_t Cmd_FindCompleter(const char *name)
Definition: cmd.c:1627
cmdalias_s::listEntry
list_t listEntry
Definition: cmd.c:223
Cmd_AddCommand
void Cmd_AddCommand(const char *name, xcommand_t function)
Definition: cmd.c:1562
CL_ForwardToServer
qboolean CL_ForwardToServer(void)
Definition: main.c:321
Cmd_Command_g
void Cmd_Command_g(genctx_t *ctx)
Definition: cmd.c:1634
cmdalias_t
struct cmdalias_s cmdalias_t
Cmd_Wait_f
static void Cmd_Wait_f(void)
Definition: cmd.c:63
cmd_current
cmdbuf_t * cmd_current
Definition: cmd.c:52
Q_ErrorString
const char * Q_ErrorString(qerror_t error)
Definition: error.c:51
Cbuf_Init
void Cbuf_Init(void)
Definition: cmd.c:79
Com_HashString
unsigned Com_HashString(const char *s, unsigned size)
Definition: utils.c:339
Cmd_Exists
qboolean Cmd_Exists(const char *name)
Definition: cmd.c:1613
Cmd_UnAlias_c
static void Cmd_UnAlias_c(genctx_t *ctx, int argnum)
Definition: cmd.c:425
Cmd_ExecTrigger
void Cmd_ExecTrigger(const char *string)
Definition: cmd.c:576
Cmd_ExecuteString
void Cmd_ExecuteString(cmdbuf_t *buf, const char *text)
Definition: cmd.c:1698
FS_FPrintf
ssize_t FS_FPrintf(qhandle_t f, const char *format,...)
Definition: files.c:2039
Cmd_From
from_t Cmd_From(void)
Definition: cmd.c:851
Cmd_Config_g
void Cmd_Config_g(genctx_t *ctx)
Definition: cmd.c:1805
list_triggers
static void list_triggers(void)
Definition: cmd.c:466
cmd_optind
int cmd_optind
Definition: cmd.c:847
Cmd_PrintUsage
void Cmd_PrintUsage(const cmd_option_t *opt, const char *suffix)
Definition: cmd.c:1143
cmd_function_t
struct cmd_function_s cmd_function_t
Cmd_Args
char * Cmd_Args(void)
Definition: cmd.c:933
cmd_trigger_t
Definition: cmd.c:445
cmd_string
static char cmd_string[MAX_STRING_CHARS]
Definition: cmd.c:834
cmd_argv
static char * cmd_argv[MAX_STRING_TOKENS]
Definition: cmd.c:830
FOR_EACH_TRIGGER_SAFE
#define FOR_EACH_TRIGGER_SAFE(trig, next)
Definition: cmd.c:442
Cbuf_InsertText
void Cbuf_InsertText(cmdbuf_t *buf, const char *text)
Definition: cmd.c:128
Cmd_TokenizeString
void Cmd_TokenizeString(const char *text, qboolean macroExpand)
Definition: cmd.c:1399
Cmd_AliasFind
static cmdalias_t * Cmd_AliasFind(const char *name)
Definition: cmd.c:236
Cmd_Shift
void Cmd_Shift(void)
Definition: cmd.c:1034
Cmd_ArgsBuffer
size_t Cmd_ArgsBuffer(char *buffer, size_t size)
Definition: cmd.c:969
Cmd_RawArgsFrom
char * Cmd_RawArgsFrom(int from)
Definition: cmd.c:1021
Cmd_Deregister
void Cmd_Deregister(const cmdreg_t *reg)
Definition: cmd.c:1580
Cmd_Argv
char * Cmd_Argv(int arg)
Definition: cmd.c:899
Cmd_MacroList_f
static void Cmd_MacroList_f(void)
Definition: cmd.c:1953
Cmd_Argc
int Cmd_Argc(void)
Definition: cmd.c:889
Prompt_AddMatch
qboolean Prompt_AddMatch(genctx_t *ctx, const char *s)
Definition: prompt.c:149
Cmd_If_f
static void Cmd_If_f(void)
Definition: cmd.c:604
Cmd_ExecuteCommand
void Cmd_ExecuteCommand(cmdbuf_t *buf)
Definition: cmd.c:1645
cmdalias_s::value
char * value
Definition: cmd.c:224
cmd_buffer
cmdbuf_t cmd_buffer
Definition: cmd.c:49
Cvar_Exists
qboolean Cvar_Exists(const char *var_name, qboolean weak)
Definition: cvar.c:73
cmd_trigger_t::command
char * command
Definition: cmd.c:448
Com_Generic_c
void Com_Generic_c(genctx_t *ctx, int argnum)
Definition: common.c:748
FOR_EACH_TRIGGER
#define FOR_EACH_TRIGGER(trig)
Definition: cmd.c:440
cmd_argc
static int cmd_argc
Definition: cmd.c:829
FS_NormalizePathBuffer
size_t FS_NormalizePathBuffer(char *out, const char *in, size_t size)
Definition: files.c:400
Cmd_ArgsFrom
char * Cmd_ArgsFrom(int from)
Definition: cmd.c:981
Cmd_RemoveCommand
void Cmd_RemoveCommand(const char *name)
Definition: cmd.c:1593
cmd_offsets
static size_t cmd_offsets[MAX_STRING_TOKENS]
Definition: cmd.c:839
Cmd_EchoEx_c
static void Cmd_EchoEx_c(genctx_t *ctx, int argnum)
Definition: cmd.c:1837
Cmd_Complete_f
static void Cmd_Complete_f(void)
Definition: cmd.c:1982
Cmd_UnTrigger_f
static void Cmd_UnTrigger_f(void)
Definition: cmd.c:525
cmd_aliasHash
static list_t cmd_aliasHash[ALIAS_HASH_SIZE]
Definition: cmd.c:229
Cmd_List_f
static void Cmd_List_f(void)
Definition: cmd.c:1926
Cmd_ParseOptions
int Cmd_ParseOptions(const cmd_option_t *opt)
Definition: cmd.c:1057
cmdalias_s
Definition: cmd.c:221
cmd_trigger_t::entry
list_t entry
Definition: cmd.c:446
FOR_EACH_ALIAS_HASH
#define FOR_EACH_ALIAS_HASH(alias, hash)
Definition: cmd.c:216
cmd_args
static char cmd_args[MAX_STRING_CHARS]
Definition: cmd.c:845
cmdalias_s::hashEntry
list_t hashEntry
Definition: cmd.c:222
Cmd_Find
static cmd_function_t * Cmd_Find(const char *name)
Definition: cmd.c:1508
find_trigger
static cmd_trigger_t * find_trigger(const char *command, const char *match)
Definition: cmd.c:453
Cmd_MacroExpandString
char * Cmd_MacroExpandString(const char *text, qboolean aliasHack)
Definition: cmd.c:1218
Cbuf_AddText
void Cbuf_AddText(cmdbuf_t *buf, const char *text)
Definition: cmd.c:95
va
char * va(const char *format,...)
Definition: shared.c:429
FS_File_g
void FS_File_g(const char *path, const char *ext, unsigned flags, genctx_t *ctx)
Definition: files.c:2954
Z_Free
void Z_Free(void *ptr)
Definition: zone.c:147
cmd_function_s::name
char * name
Definition: cmd.c:823
Cmd_Exec_c
static void Cmd_Exec_c(genctx_t *ctx, int argnum)
Definition: cmd.c:1810
Cmd_Register
void Cmd_Register(const cmdreg_t *reg)
Definition: cmd.c:1572
m
static struct mdfour * m
Definition: mdfour.c:32
o_echo
static const cmd_option_t o_echo[]
Definition: cmd.c:1829
cmd_hash
static list_t cmd_hash[CMD_HASH_SIZE]
Definition: cmd.c:827
Cmd_Text_f
static void Cmd_Text_f(void)
Definition: cmd.c:1976
Cbuf_Alloc
char * Cbuf_Alloc(cmdbuf_t *buf, size_t len)
Definition: cmd.c:107
cmd_macros
static cmd_macro_t * cmd_macros
Definition: cmd.c:731
FOR_EACH_ALIAS
#define FOR_EACH_ALIAS(alias)
Definition: cmd.c:218
Q_strlcpy
size_t Q_strlcpy(char *dst, const char *src, size_t size)
Definition: shared.c:715
COM_Compress
size_t COM_Compress(char *data)
Definition: shared.c:543
cmd_data
static char cmd_data[MAX_STRING_CHARS]
Definition: cmd.c:842
Cmd_Alias_f
void Cmd_Alias_f(void)
Definition: cmd.c:307
cmd_null_string
static char * cmd_null_string
Definition: cmd.c:831
Cmd_AliasCommand
char * Cmd_AliasCommand(const char *name)
Definition: cmd.c:251
unescape_string
static char * unescape_string(char *dst, const char *src)
Definition: cmd.c:1842
Cmd_UnAlias_f
static void Cmd_UnAlias_f(void)
Definition: cmd.c:350
Cmd_Macro_g
void Cmd_Macro_g(genctx_t *ctx)
Definition: cmd.c:754
Cmd_Init
void Cmd_Init(void)
Definition: cmd.c:2046
cmdalias_s::name
char name[1]
Definition: cmd.c:225
cmd_triggers
static list_t cmd_triggers
Definition: cmd.c:451
Cmd_EchoEx_f
static void Cmd_EchoEx_f(void)
Definition: cmd.c:1882
cmd_macroHash
static cmd_macro_t * cmd_macroHash[MACRO_HASH_SIZE]
Definition: cmd.c:732
Cmd_ArgsRange
static char * Cmd_ArgsRange(int from, int to)
Definition: cmd.c:999
FOR_EACH_CMD_HASH
#define FOR_EACH_CMD_HASH(cmd, hash)
Definition: cmd.c:812
c
statCounters_t c
Definition: main.c:30
Cmd_FindArgForOffset
int Cmd_FindArgForOffset(size_t offset)
Definition: cmd.c:867
Cvar_Command
void Cvar_Command(cvar_t *v)
Definition: cvar.c:672
cmd_trigger_t::match
char * match
Definition: cmd.c:447
Cmd_RegCommand
static void Cmd_RegCommand(const cmdreg_t *reg)
Definition: cmd.c:1523
cmd_function_s::listEntry
list_t listEntry
Definition: cmd.c:819
cmd_function_s::hashEntry
list_t hashEntry
Definition: cmd.c:818
cmd_function_s::completer
xcompleter_t completer
Definition: cmd.c:822
Cmd_Echo_f
static void Cmd_Echo_f(void)
Definition: cmd.c:1824
Cmd_AliasSet
void Cmd_AliasSet(const char *name, const char *cmd)
Definition: cmd.c:263
cmd_function_s
Definition: cmd.c:817
Com_SetColor
void Com_SetColor(color_index_t color)
Definition: common.c:373
Cbuf_Execute
void Cbuf_Execute(cmdbuf_t *buf)
Definition: cmd.c:152
Cmd_ArgvBuffer
size_t Cmd_ArgvBuffer(int arg, char *buffer, size_t size)
Definition: cmd.c:912
client.h
Cmd_PrintHelp
void Cmd_PrintHelp(const cmd_option_t *opt)
Definition: cmd.c:1160
Cmd_Option_c
void Cmd_Option_c(const cmd_option_t *opt, xgenerator_t g, genctx_t *ctx, int argnum)
Definition: cmd.c:1183
Cmd_PrintHint
void Cmd_PrintHint(void)
Definition: cmd.c:1178
cmd_optopt
char * cmd_optopt
Definition: cmd.c:849
Cmd_AddMacro
void Cmd_AddMacro(const char *name, xmacro_t function)
Definition: cmd.c:770
Cvar_FindVar
cvar_t * Cvar_FindVar(const char *var_name)
Definition: cvar.c:45
Cmd_Alias_c
static void Cmd_Alias_c(genctx_t *ctx, int argnum)
Definition: cmd.c:416
color
static vec4_t color
Definition: mesh.c:33
MACRO_HASH_SIZE
#define MACRO_HASH_SIZE
Definition: cmd.c:729
Q_concat
size_t Q_concat(char *dest, size_t size,...)
Definition: shared.c:758
Cmd_Exec_f
static void Cmd_Exec_f(void)
Definition: cmd.c:1766
CMD_HASH_SIZE
#define CMD_HASH_SIZE
Definition: cmd.c:810
FOR_EACH_CMD
#define FOR_EACH_CMD(cmd)
Definition: cmd.c:814
Cmd_FindMacro
cmd_macro_t * Cmd_FindMacro(const char *name)
Definition: cmd.c:739
cmd_alias
static list_t cmd_alias
Definition: cmd.c:228
FS_LoadFileEx
ssize_t FS_LoadFileEx(const char *path, void **buffer, unsigned flags, memtag_t tag)
Definition: files.c:1864
Cmd_RawString
char * Cmd_RawString(void)
Definition: cmd.c:959
cmd_functions
static list_t cmd_functions
Definition: cmd.c:826
Cmd_FindFunction
xcommand_t Cmd_FindFunction(const char *name)
Definition: cmd.c:1620
Cmd_CopyString
#define Cmd_CopyString(string)
Definition: cmd.c:36
Cmd_Malloc
#define Cmd_Malloc(size)
Definition: cmd.c:35
Cmd_ArgOffset
size_t Cmd_ArgOffset(int arg)
Definition: cmd.c:856
cmd_buffer_text
char cmd_buffer_text[CMD_BUFFER_SIZE]
Definition: cmd.c:48
Cmd_OpenURL_f
static void Cmd_OpenURL_f(void)
Definition: cmd.c:693
cmd_string_tail
static size_t cmd_string_tail
Definition: cmd.c:836
cmd_function_s::function
xcommand_t function
Definition: cmd.c:821
Cmd_RawArgs
char * Cmd_RawArgs(void)
Definition: cmd.c:951