]> icculus.org git repositories - btb/d2x.git/blob - main/cmd.c
add explicit set command. concatenate all args when setting
[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 void cvar_cmd_set(int argc, char **argv);
85
86
87 /* execute a parsed command */
88 void cmd_execute(int argc, char **argv)
89 {
90         cmd_t *cmd;
91         cmd_alias_t *alias;
92
93         for (cmd = cmd_list; cmd; cmd = cmd->next) {
94                 if (!stricmp(argv[0], cmd->name)) {
95                         con_printf(CON_DEBUG, "cmd_execute: executing %s\n", argv[0]);
96                         return cmd->function(argc, argv);
97                 }
98         }
99
100         for (alias = cmd_alias_list; alias; alias = alias->next) {
101                 if (!stricmp(argv[0], alias->name)) {
102                         con_printf(CON_DEBUG, "cmd_execute: pushing alias \"%s\": %s\n", alias->name, alias->value);
103                         cmd_insert(alias->value);
104                         return;
105                 }
106         }
107
108         /* Otherwise */
109         if (argc > 1) {  // set value of cvar
110                 char *new_argv[argc+1];
111                 int i;
112
113                 new_argv[0] = "set";
114                 for (i = 0; i < argc; i++)
115                         new_argv[i+1] = argv[i];
116                 cvar_cmd_set(argc + 1, new_argv);
117         }
118         con_printf(CON_VERBOSE, "%s: %f\n", argv[0], cvar(argv[0]));
119 }
120
121
122 /* Parse an input string */
123 void cmd_parse(char *input)
124 {
125         char buffer[CMD_MAX_LENGTH];
126         char *tokens[CMD_MAX_TOKENS];
127         int num_tokens;
128         int i, l;
129         int quoted = 0;
130
131         Assert(input != NULL);
132         
133         /* Strip leading spaces */
134         for (i=0; isspace(input[i]); i++) ;
135         strncpy( buffer, &input[i], CMD_MAX_LENGTH );
136
137         //printf("lead strip \"%s\"\n",buffer);
138         l = strlen(buffer);
139         /* If command is empty, give up */
140         if (l==0) return;
141
142         /* Strip trailing spaces */
143         for (i=l-1; i>0 && isspace(buffer[i]); i--) ;
144         buffer[i+1] = 0;
145         //printf("trail strip \"%s\"\n",buffer);
146
147         /* Split into tokens */
148         l = strlen(buffer);
149         num_tokens = 1;
150
151         tokens[0] = buffer;
152         for (i=1; i<l; i++) {
153                 if (buffer[i] == '"') {
154                         quoted = 1 - quoted;
155                         continue;
156                 }
157                 if ((isspace(buffer[i]) || buffer[i] == '=') && !quoted) {
158                         buffer[i] = 0;
159                         while (isspace(buffer[i+1]) && (i+1 < l)) i++;
160                         tokens[num_tokens++] = &buffer[i+1];
161                 }
162         }
163
164         /* Check for matching commands */
165         cmd_execute(num_tokens, tokens);
166 }
167
168
169 int cmd_queue_wait = 0;
170
171 void cmd_queue_process()
172 {
173         cmd_queue_t *cmd;
174
175         while (!cmd_queue_wait && cmd_queue_head) {
176                 cmd = cmd_queue_head;
177                 cmd_queue_head = cmd_queue_head->next;
178                 if (!cmd_queue_head)
179                         cmd_queue_tail = NULL;
180
181                 con_printf(CON_DEBUG, "cmd_queue_process: processing %s\n", cmd->command_line);
182                 cmd_parse(cmd->command_line);  // Note, this may change the queue
183
184                 d_free(cmd->command_line);
185                 d_free(cmd);
186         }
187
188         if (cmd_queue_wait) {
189                 con_printf(CON_DEBUG, "cmd_queue_process: waiting\n");
190                 cmd_queue_wait--;
191                 return;
192         }
193 }
194
195
196 /* Add some commands to the queue to be executed */
197 void cmd_enqueue(int insert, char *input)
198 {
199         cmd_queue_t *new, *head, *tail;
200         char *line;
201
202         Assert(input != NULL);
203         head = tail = NULL;
204
205         while (*input) {
206                 int quoted = 0;
207
208                 /* Strip leading spaces */
209                 while(isspace(*input) || *input == ';')
210                         input++;
211
212                 /* If command is empty, give up */
213                 if (! *input)
214                         continue;
215
216                 /* Now at start of a command line */
217                 line = input;
218
219                 /* Find the end of this line (\n, ;, or nul) */
220                 do {
221                         if (!*input)
222                                 break;
223                         if (*input == '"') {
224                                 quoted = 1 - quoted;
225                                 continue;
226                         } else if ( *input == '\n' || (!quoted && *input == ';') ) {
227                                 *input = 0;
228                         }
229                 } while (*input++);
230
231                 /* make a new queue item, add it to list */
232                 MALLOC(new, cmd_queue_t, 1);
233                 new->command_line = d_strdup(line);
234                 new->next = NULL;
235
236                 if (!head)
237                         head = new;
238                 if (tail)
239                         tail->next = new;
240                 tail = new;
241
242                 con_printf(CON_DEBUG, "cmd_enqueue: adding %s\n", line);
243         }
244
245         if (insert) {
246                  /* add our list to the head of the main list */
247                 if (cmd_queue_head)
248                         tail->next = cmd_queue_head;
249                 if (!cmd_queue_tail)
250                         cmd_queue_tail = tail;
251                 
252                 cmd_queue_head = head;
253                 con_printf(CON_DEBUG, "cmd_enqueue: added to front of list\n");
254         } else {
255                 /* add our list to the tail of the main list */
256                 if (!cmd_queue_head)
257                         cmd_queue_head = head;
258                 if (cmd_queue_tail)
259                         cmd_queue_tail->next = head;
260                 
261                 cmd_queue_tail = tail;
262                 con_printf(CON_DEBUG, "cmd_enqueue: added to back of list\n");
263         }
264 }
265
266 void cmd_enqueuef(int insert, char *fmt, ...)
267 {
268         va_list arglist;
269         char buf[CMD_MAX_LENGTH];
270         
271         va_start (arglist, fmt);
272         vsnprintf (buf, CMD_MAX_LENGTH, fmt, arglist);
273         va_end (arglist);
274         
275         cmd_enqueue(insert, buf);
276 }
277
278
279 /* Attempt to autocomplete an input string */
280 char *cmd_complete(char *input)
281 {
282         cmd_t *ptr;
283         cmd_alias_t *aptr;
284
285         int len = strlen(input);
286
287         if (!len)
288                 return NULL;
289
290         for (ptr = cmd_list; ptr != NULL; ptr = ptr->next)
291                 if (!strnicmp(input, ptr->name, len))
292                         return ptr->name;
293
294         for (aptr = cmd_alias_list; aptr != NULL; aptr = aptr->next)
295                 if (!strnicmp(input, aptr->name, len))
296                         return aptr->name;
297
298         return cvar_complete(input);
299 }
300
301
302 int cmd_handle_keybinding(unsigned char key)
303 {
304         if (cmd_keybinding_list[key]) {
305                 cmd_insert(cmd_keybinding_list[key]);
306                 return 1;
307         }
308         return 0;
309 }
310
311
312
313 /* alias */
314 void cmd_alias(int argc, char **argv)
315 {
316         cmd_alias_t *alias;
317         char buf[CMD_MAX_LENGTH] = "";
318         int i;
319
320         if (argc < 2)
321         {
322                 con_printf(CON_NORMAL, "aliases:\n");
323                 for (alias = cmd_alias_list; alias; alias = alias->next)
324                         con_printf(CON_NORMAL, "%s: %s\n", alias->name, alias->value);
325                 return;
326         }
327
328         for (i = 2; i < argc; i++) {
329                 if (i > 2)
330                         strncat(buf, " ", CMD_MAX_LENGTH);
331                 strncat(buf, argv[i], CMD_MAX_LENGTH);
332         }
333
334         for (alias = cmd_alias_list; alias; alias = alias->next) {
335                 if (!stricmp(argv[1], alias->name))
336                 {
337                         d_free(alias->value);
338                         alias->value = d_strdup(buf);
339                         return;
340                 }
341         }
342
343         MALLOC(alias, cmd_alias_t, 1);
344         strncpy(alias->name, argv[1], ALIAS_NAME_MAX);
345         alias->value = d_strdup(buf);
346         alias->next = cmd_alias_list;
347         cmd_alias_list = alias;
348 }
349
350 /* bind */
351 /* FIXME: key_text is not really adequate for this */
352 void cmd_bind(int argc, char **argv)
353 {
354         char buf[CMD_MAX_LENGTH] = "";
355         unsigned char key = 0;
356         int i;
357
358         if (argc < 2)
359         {
360                 con_printf(CON_NORMAL, "key bindings:\n");
361                 for (i = 0; i < 256; i++) {
362                         if (!cmd_keybinding_list[i])
363                                 continue;
364                         con_printf(CON_NORMAL, "%s: %s\n", key_text[i], cmd_keybinding_list[i]);
365                 }
366                 return;
367         }
368
369         for (i = 2; i < argc; i++) {
370                 if (i > 2)
371                         strncat(buf, " ", CMD_MAX_LENGTH);
372                 strncat(buf, argv[i], CMD_MAX_LENGTH);
373         }
374
375         for (i = 0; i < 256; i++) {
376                 if (!stricmp(argv[1], key_text[i])) {
377                         key = i;
378                         break;
379                 }
380         }
381
382         if (!key) {
383                 con_printf(CON_CRITICAL, "bind: key %s not found\n", argv[1]);
384                 return;
385         }
386
387         if (cmd_keybinding_list[key])
388                 d_free(cmd_keybinding_list[key]);
389         cmd_keybinding_list[key] = d_strdup(buf);
390 }
391
392 /* +/- actions */
393 int Console_button_states[CMD_NUM_BUTTONS];
394 void cmd_attack_on(int argc, char **argv) { Console_button_states[CMD_ATTACK] = 1; }
395 void cmd_attack_off(int argc, char **argv) { Console_button_states[CMD_ATTACK] = 0; }
396 void cmd_attack2_on(int argc, char **argv) { Console_button_states[CMD_ATTACK2] = 1; }
397 void cmd_attack2_off(int argc, char **argv) { Console_button_states[CMD_ATTACK2] = 0; }
398
399 /* weapon select */
400 void cmd_impulse(int argc, char**argv)
401 {
402         if (argc < 2)
403                 return;
404         int n = atoi(argv[1]);
405         if (n >= 1 && n <= 20) {
406                 select_weapon((n-1) % 10, (n-1) / 10, 0, 1);
407         }
408 }
409
410 /* echo to console */
411 void cmd_echo(int argc, char **argv)
412 {
413         char buf[CMD_MAX_LENGTH] = "";
414         int i;
415         for (i = 1; i < argc; i++) {
416                 if (i > 1)
417                         strncat(buf, " ", CMD_MAX_LENGTH);
418                 strncat(buf, argv[i], CMD_MAX_LENGTH);
419         }
420         con_printf(CON_NORMAL, "%s\n", buf);
421 }
422
423 /* execute script */
424 void cmd_exec(int argc, char **argv) {
425         cmd_queue_t *new, *head, *tail;
426         PHYSFS_File *f;
427         char line[CMD_MAX_LENGTH] = "";
428
429         if (argc < 2)
430                 return;
431
432         head = tail = NULL;
433
434         f = PHYSFSX_openReadBuffered(argv[1]);
435         if (!f) {
436                 con_printf(CON_CRITICAL, "exec: %s not found\n", argv[1]);
437                 return;
438         }
439         while (PHYSFSX_gets(f, line)) {
440                 /* make a new queue item, add it to list */
441                 MALLOC(new, cmd_queue_t, 1);
442                 new->command_line = d_strdup(line);
443                 new->next = NULL;
444
445                 if (!head)
446                         head = new;
447                 if (tail)
448                         tail->next = new;
449                 tail = new;
450
451                 con_printf(CON_DEBUG, "cmd_exec: adding %s\n", line);
452         }
453         PHYSFS_close(f);
454
455         /* add our list to the head of the main list */
456         if (cmd_queue_head)
457                 tail->next = cmd_queue_head;
458         if (!cmd_queue_tail)
459                 cmd_queue_tail = tail;
460
461         cmd_queue_head = head;
462         con_printf(CON_DEBUG, "cmd_exec: added to front of list\n");
463 }
464
465
466 /* execute script */
467 void cmd_wait(int argc, char **argv)
468 {
469         if (argc < 2)
470                 cmd_queue_wait = 1;
471         else
472                 cmd_queue_wait = atoi(argv[1]);
473 }
474
475
476 void cmd_free(void)
477 {
478         int i;
479         void *p, *temp;
480
481         p = cmd_list;
482         while (p) {
483                 temp = p;
484                 p = ((cmd_t *)p)->next;
485                 d_free(temp);
486         }
487
488         p = cmd_alias_list;
489         while (p) {
490                 d_free(((cmd_alias_t *)p)->value);
491                 temp = p;
492                 p = ((cmd_alias_t *)p)->next;
493                 d_free(temp);
494         }
495
496         for (i = 0; i < 256; i++)
497                 if (cmd_keybinding_list[i])
498                         d_free(cmd_keybinding_list[i]);
499 }
500
501
502 void cmd_init(void)
503 {
504         memset(Console_button_states, 0, sizeof(int) * CMD_NUM_BUTTONS);
505
506         cmd_addcommand("alias", cmd_alias);
507         cmd_addcommand("bind", cmd_bind);
508
509         cmd_addcommand("+attack", cmd_attack_on);
510         cmd_addcommand("-attack", cmd_attack_off);
511         cmd_addcommand("+attack2", cmd_attack2_on);
512         cmd_addcommand("-attack2", cmd_attack2_off);
513
514         cmd_addcommand("impulse", cmd_impulse);
515
516         cmd_addcommand("echo", cmd_echo);
517
518         cmd_addcommand("exec", cmd_exec);
519
520         cmd_addcommand("wait", cmd_wait);
521
522         atexit(cmd_free);
523 }