]> icculus.org git repositories - btb/d2x.git/blob - main/cmd.c
cvar autocompletion
[btb/d2x.git] / main / cmd.c
1 #ifdef HAVE_CONFIG_H
2 #include <conf.h>
3 #endif
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9
10 #include "pstypes.h"
11 #include "cmd.h"
12 #include "console.h"
13 #include "error.h"
14 #include "u_mem.h"
15 #include "strutil.h"
16 #include "weapon.h"
17 #include "key.h"
18
19
20 typedef struct cmd_s
21 {
22         char          *name;
23         cmd_handler_t function;
24         struct cmd_s  *next;
25 } cmd_t;
26
27 /* The list of cmds */
28 static cmd_t *cmd_list = NULL;
29
30
31 #define ALIAS_NAME_MAX 32
32 typedef struct cmd_alias_s
33 {
34         char           name[ALIAS_NAME_MAX];
35         char           *value;
36         struct cmd_alias_s *next;
37 } cmd_alias_t;
38
39 /* The list of aliases */
40 static cmd_alias_t *cmd_alias_list = NULL;
41
42
43 /* The list of keybindings */
44 static char *cmd_keybinding_list[256];
45
46
47 /* add a new console command */
48 void cmd_addcommand(char *cmd_name, cmd_handler_t cmd_func)
49 {
50         cmd_t *cmd;
51
52         Assert(cmd_name != NULL);
53
54         for (cmd = cmd_list; cmd; cmd = cmd->next) {
55                 if (!stricmp(cmd_name, cmd->name))
56                 {
57                         Int3();
58                         con_printf(CON_NORMAL, "command %s already exists, not adding\n", cmd_name);
59                         return;
60                 }
61         }
62
63         /* create command, insert at front of list */
64         MALLOC(cmd, cmd_t, 1);
65         cmd->name = cmd_name;
66         cmd->function = cmd_func;
67         cmd->next = cmd_list;
68         con_printf(CON_DEBUG, "cmd_addcommand: added %s\n", cmd->name);
69         cmd_list = cmd;
70 }
71
72
73 typedef struct cmd_queue_s
74 {
75         char *command_line;
76         struct cmd_queue_s *next;
77 } cmd_queue_t;
78
79 /* The list of commands to be executed */
80 static cmd_queue_t *cmd_queue_head = NULL;
81 static cmd_queue_t *cmd_queue_tail = NULL;
82
83
84 /* execute a parsed command */
85 void cmd_execute(int argc, char **argv)
86 {
87         cmd_t *cmd;
88         cmd_alias_t *alias;
89
90         for (cmd = cmd_list; cmd; cmd = cmd->next) {
91                 if (!stricmp(argv[0], cmd->name)) {
92                         con_printf(CON_DEBUG, "cmd_execute: executing %s\n", argv[0]);
93                         return cmd->function(argc, argv);
94                 }
95         }
96
97         for (alias = cmd_alias_list; alias; alias = alias->next) {
98                 if (!stricmp(argv[0], alias->name)) {
99                         con_printf(CON_DEBUG, "cmd_execute: pushing alias \"%s\": %s\n", alias->name, alias->value);
100                         cmd_insert(alias->value);
101                         return;
102                 }
103         }
104
105         /* Otherwise */
106         if (argc > 1)  // set value of cvar
107                 cvar_set(argv[0], argv[1]);
108         con_printf(CON_VERBOSE, "%s: %f\n", argv[0], cvar(argv[0]));
109 }
110
111
112 /* Parse an input string */
113 void cmd_parse(char *input)
114 {
115         char buffer[CMD_MAX_LENGTH];
116         char *tokens[CMD_MAX_TOKENS];
117         int num_tokens;
118         int i, l;
119         int quoted = 0;
120
121         Assert(input != NULL);
122         
123         /* Strip leading spaces */
124         for (i=0; isspace(input[i]); i++) ;
125         strncpy( buffer, &input[i], CMD_MAX_LENGTH );
126
127         //printf("lead strip \"%s\"\n",buffer);
128         l = strlen(buffer);
129         /* If command is empty, give up */
130         if (l==0) return;
131
132         /* Strip trailing spaces */
133         for (i=l-1; i>0 && isspace(buffer[i]); i--) ;
134         buffer[i+1] = 0;
135         //printf("trail strip \"%s\"\n",buffer);
136
137         /* Split into tokens */
138         l = strlen(buffer);
139         num_tokens = 1;
140
141         tokens[0] = buffer;
142         for (i=1; i<l; i++) {
143                 if (buffer[i] == '"') {
144                         quoted = 1 - quoted;
145                         continue;
146                 }
147                 if ((isspace(buffer[i]) || buffer[i] == '=') && !quoted) {
148                         buffer[i] = 0;
149                         while (isspace(buffer[i+1]) && (i+1 < l)) i++;
150                         tokens[num_tokens++] = &buffer[i+1];
151                 }
152         }
153
154         /* Check for matching commands */
155         cmd_execute(num_tokens, tokens);
156 }
157
158 void cmd_queue_process()
159 {
160         cmd_queue_t *cmd;
161
162         while (cmd_queue_head) {
163                 cmd = cmd_queue_head;
164                 cmd_queue_head = cmd_queue_head->next;
165                 if (!cmd_queue_head)
166                         cmd_queue_tail = NULL;
167
168                 con_printf(CON_DEBUG, "cmd_queue_process: processing %s\n", cmd->command_line);
169                 cmd_parse(cmd->command_line);  // Note, this may change the queue
170
171                 d_free(cmd->command_line);
172                 d_free(cmd);
173         }
174 }
175
176
177 /* Add some commands to the queue to be executed */
178 void cmd_enqueue(int insert, char *input)
179 {
180         cmd_queue_t *new, *head, *tail;
181         char *line;
182
183         Assert(input != NULL);
184         head = tail = NULL;
185
186         while (*input) {
187                 int quoted = 0;
188
189                 /* Strip leading spaces */
190                 while(isspace(*input) || *input == ';')
191                         input++;
192
193                 /* If command is empty, give up */
194                 if (! *input)
195                         continue;
196
197                 /* Now at start of a command line */
198                 line = input;
199
200                 /* Find the end of this line (\n, ;, or nul) */
201                 do {
202                         if (!*input)
203                                 break;
204                         if (*input == '"') {
205                                 quoted = 1 - quoted;
206                                 continue;
207                         } else if ( *input == '\n' || (!quoted && *input == ';') ) {
208                                 *input = 0;
209                                 break;
210                         }
211                 } while (*input++);
212
213                 /* make a new queue item, add it to list */
214                 MALLOC(new, cmd_queue_t, 1);
215                 new->command_line = d_strdup(line);
216                 new->next = NULL;
217
218                 if (!head)
219                         head = new;
220                 if (tail)
221                         tail->next = new;
222                 tail = new;
223
224                 con_printf(CON_DEBUG, "cmd_enqueue: adding %s\n", line);
225         }
226
227         if (insert) {
228                  /* add our list to the head of the main list */
229                 if (cmd_queue_head)
230                         tail->next = cmd_queue_head;
231                 if (!cmd_queue_tail)
232                         cmd_queue_tail = tail;
233                 
234                 cmd_queue_head = head;
235                 con_printf(CON_DEBUG, "cmd_enqueue: added to front of list\n");
236         } else {
237                 /* add our list to the tail of the main list */
238                 if (!cmd_queue_head)
239                         cmd_queue_head = new;
240                 if (cmd_queue_tail)
241                         cmd_queue_tail->next = head;
242                 
243                 cmd_queue_tail = tail;
244                 con_printf(CON_DEBUG, "cmd_enqueue: added to back of list\n");
245         }
246 }
247
248 void cmd_enqueuef(int insert, char *fmt, ...){
249         va_list arglist;
250         char buf[CMD_MAX_LENGTH];
251         
252         va_start (arglist, fmt);
253         vsnprintf (buf, CMD_MAX_LENGTH, fmt, arglist);
254         va_end (arglist);
255         
256         cmd_enqueue(insert, buf);
257 }
258
259
260 /* Attempt to autocomplete an input string */
261 char *cmd_complete(char *input)
262 {
263         return cvar_complete(input);
264 }
265
266
267 int cmd_handle_keybinding(unsigned char key)
268 {
269         if (cmd_keybinding_list[key]) {
270                 cmd_insert(cmd_keybinding_list[key]);
271                 return 1;
272         }
273         return 0;
274 }
275
276
277
278 /* alias */
279 void cmd_alias(int argc, char **argv)
280 {
281         cmd_alias_t *alias;
282         char buf[CMD_MAX_LENGTH] = "";
283         int i;
284
285         if (argc < 2)
286         {
287                 con_printf(CON_NORMAL, "aliases:\n");
288                 for (alias = cmd_alias_list; alias; alias = alias->next)
289                         con_printf(CON_NORMAL, "%s: %s\n", alias->name, alias->value);
290                 return;
291         }
292
293         for (i = 2; i < argc; i++) {
294                 if (i > 2)
295                         strncat(buf, " ", CMD_MAX_LENGTH);
296                 strncat(buf, argv[i], CMD_MAX_LENGTH);
297         }
298
299         for (alias = cmd_alias_list; alias; alias = alias->next) {
300                 if (!stricmp(argv[1], alias->name))
301                 {
302                         d_free(alias->value);
303                         alias->value = d_strdup(buf);
304                         return;
305                 }
306         }
307
308         MALLOC(alias, cmd_alias_t, 1);
309         strncpy(alias->name, argv[1], ALIAS_NAME_MAX);
310         alias->value = d_strdup(buf);
311         alias->next = cmd_alias_list;
312         cmd_alias_list = alias;
313 }
314
315 /* bind */
316 /* FIXME: key_text is not really adequate for this */
317 void cmd_bind(int argc, char **argv)
318 {
319         char buf[CMD_MAX_LENGTH] = "";
320         unsigned char key = 0;
321         int i;
322
323         if (argc < 2)
324         {
325                 con_printf(CON_NORMAL, "key bindings:\n");
326                 for (i = 0; i < 256; i++) {
327                         if (!cmd_keybinding_list[i])
328                                 continue;
329                         con_printf(CON_NORMAL, "%s: %s\n", key_text[i], cmd_keybinding_list[i]);
330                 }
331                 return;
332         }
333
334         for (i = 2; i < argc; i++) {
335                 if (i > 2)
336                         strncat(buf, " ", CMD_MAX_LENGTH);
337                 strncat(buf, argv[i], CMD_MAX_LENGTH);
338         }
339
340         for (i = 0; i < 256; i++) {
341                 if (!stricmp(argv[1], key_text[i])) {
342                         key = i;
343                         break;
344                 }
345         }
346
347         if (!key) {
348                 con_printf(CON_CRITICAL, "bind: key %s not found\n", argv[1]);
349                 return;
350         }
351
352         if (cmd_keybinding_list[key])
353                 d_free(cmd_keybinding_list[key]);
354         cmd_keybinding_list[key] = d_strdup(buf);
355 }
356
357 /* +/- actions */
358 int Console_button_states[CMD_NUM_BUTTONS];
359 void cmd_attack_on(int argc, char **argv) { Console_button_states[CMD_ATTACK] = 1; }
360 void cmd_attack_off(int argc, char **argv) { Console_button_states[CMD_ATTACK] = 0; }
361 void cmd_attack2_on(int argc, char **argv) { Console_button_states[CMD_ATTACK2] = 1; }
362 void cmd_attack2_off(int argc, char **argv) { Console_button_states[CMD_ATTACK2] = 0; }
363
364 /* weapon select */
365 void cmd_impulse(int argc, char**argv) {
366         if (argc < 2)
367                 return;
368         int n = atoi(argv[1]);
369         if (n >= 1 && n <= 20) {
370                 select_weapon((n-1) % 10, (n-1) / 10, 0, 1);
371         }
372 }
373
374 /* echo to console */
375 void cmd_echo(int argc, char **argv) {
376         char buf[CMD_MAX_LENGTH] = "";
377         int i;
378         for (i = 1; i < argc; i++) {
379                 if (i > 1)
380                         strncat(buf, " ", CMD_MAX_LENGTH);
381                 strncat(buf, argv[i], CMD_MAX_LENGTH);
382         }
383         con_printf(CON_NORMAL, "%s\n", buf);
384 }
385
386 /* execute script */
387 void cmd_exec(int argc, char **argv) {
388         cmd_queue_t *new, *head, *tail;
389         PHYSFS_File *f;
390         char line[CMD_MAX_LENGTH] = "";
391
392         if (argc < 2)
393                 return;
394
395         head = tail = NULL;
396
397         f = PHYSFSX_openReadBuffered(argv[1]);
398         if (!f) {
399                 con_printf(CON_CRITICAL, "exec: %s not found\n", argv[1]);
400                 return;
401         }
402         while (PHYSFSX_gets(f, line)) {
403                 /* make a new queue item, add it to list */
404                 MALLOC(new, cmd_queue_t, 1);
405                 new->command_line = d_strdup(line);
406                 new->next = NULL;
407
408                 if (!head)
409                         head = new;
410                 if (tail)
411                         tail->next = new;
412                 tail = new;
413
414                 con_printf(CON_DEBUG, "cmd_exec: adding %s\n", line);
415         }
416         PHYSFS_close(f);
417
418         /* add our list to the head of the main list */
419         if (cmd_queue_head)
420                 tail->next = cmd_queue_head;
421         if (!cmd_queue_tail)
422                 cmd_queue_tail = tail;
423
424         cmd_queue_head = head;
425         con_printf(CON_DEBUG, "cmd_exec: added to front of list\n");
426 }
427
428
429 void cmd_free(void)
430 {
431         int i;
432         void *p, *temp;
433
434         p = cmd_list;
435         while (p) {
436                 temp = p;
437                 p = ((cmd_t *)p)->next;
438                 d_free(temp);
439         }
440
441         p = cmd_alias_list;
442         while (p) {
443                 d_free(((cmd_alias_t *)p)->value);
444                 temp = p;
445                 p = ((cmd_alias_t *)p)->next;
446                 d_free(temp);
447         }
448
449         for (i = 0; i < 256; i++)
450                 if (cmd_keybinding_list[i])
451                         d_free(cmd_keybinding_list[i]);
452 }
453
454
455 void cmd_init(void){
456         memset(Console_button_states, 0, sizeof(int) * CMD_NUM_BUTTONS);
457
458         cmd_addcommand("alias", cmd_alias);
459         cmd_addcommand("bind", cmd_bind);
460
461         cmd_addcommand("+attack", cmd_attack_on);
462         cmd_addcommand("-attack", cmd_attack_off);
463         cmd_addcommand("+attack2", cmd_attack2_on);
464         cmd_addcommand("-attack2", cmd_attack2_off);
465
466         cmd_addcommand("impulse", cmd_impulse);
467
468         cmd_addcommand("echo", cmd_echo);
469
470         cmd_addcommand("exec", cmd_exec);
471
472         atexit(cmd_free);
473 }