]> icculus.org git repositories - btb/d2x.git/blob - main/cmd.c
explicit casts
[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 "inferno.h"
17
18
19 typedef struct cmd_s
20 {
21         char          *name;
22         cmd_handler_t function;
23         struct cmd_s  *next;
24 } cmd_t;
25
26 /* The list of cmds */
27 static cmd_t *cmd_list = NULL;
28
29
30 #define ALIAS_NAME_MAX 32
31 typedef struct cmd_alias_s
32 {
33         char           name[ALIAS_NAME_MAX];
34         char           *value;
35         struct cmd_alias_s *next;
36 } cmd_alias_t;
37
38 /* The list of aliases */
39 static cmd_alias_t *cmd_alias_list = NULL;
40
41
42 /* add a new console command */
43 void cmd_addcommand(char *cmd_name, cmd_handler_t cmd_func)
44 {
45         cmd_t *cmd;
46
47         Assert(cmd_name != NULL);
48
49         for (cmd = cmd_list; cmd; cmd = cmd->next) {
50                 if (!stricmp(cmd_name, cmd->name))
51                 {
52                         Int3();
53                         con_printf(CON_NORMAL, "command %s already exists, not adding\n", cmd_name);
54                         return;
55                 }
56         }
57
58         /* create command, insert at front of list */
59         MALLOC(cmd, cmd_t, 1);
60         cmd->name = cmd_name;
61         cmd->function = cmd_func;
62         cmd->next = cmd_list;
63         con_printf(CON_DEBUG, "cmd_addcommand: added %s\n", cmd->name);
64         cmd_list = cmd;
65 }
66
67
68 typedef struct cmd_queue_s
69 {
70         char *command_line;
71         struct cmd_queue_s *next;
72 } cmd_queue_t;
73
74 /* The list of commands to be executed */
75 static cmd_queue_t *cmd_queue_head = NULL;
76 static cmd_queue_t *cmd_queue_tail = NULL;
77
78
79 void cvar_cmd_set(int argc, char **argv);
80
81
82 /* execute a parsed command */
83 void cmd_execute(int argc, char **argv)
84 {
85         cmd_t *cmd;
86         cmd_alias_t *alias;
87
88         for (cmd = cmd_list; cmd; cmd = cmd->next) {
89                 if (!stricmp(argv[0], cmd->name)) {
90                         con_printf(CON_DEBUG, "cmd_execute: executing %s\n", argv[0]);
91                         return cmd->function(argc, argv);
92                 }
93         }
94
95         for (alias = cmd_alias_list; alias; alias = alias->next) {
96                 if (!stricmp(argv[0], alias->name)) {
97                         con_printf(CON_DEBUG, "cmd_execute: pushing alias \"%s\": %s\n", alias->name, alias->value);
98                         cmd_insert(alias->value);
99                         return;
100                 }
101         }
102
103         /* Otherwise */
104         {  // set value of cvar
105                 char *new_argv[argc+1];
106                 int i;
107
108                 new_argv[0] = "set";
109                 for (i = 0; i < argc; i++)
110                         new_argv[i+1] = argv[i];
111                 cvar_cmd_set(argc + 1, new_argv);
112         }
113 }
114
115
116 /* Parse an input string */
117 void cmd_parse(char *input)
118 {
119         char buffer[CMD_MAX_LENGTH];
120         char *tokens[CMD_MAX_TOKENS];
121         int num_tokens;
122         int i, l;
123
124         Assert(input != NULL);
125         
126         /* Strip leading spaces */
127         for (i=0; isspace(input[i]); i++) ;
128         strncpy( buffer, &input[i], CMD_MAX_LENGTH );
129
130         //printf("lead strip \"%s\"\n",buffer);
131         l = (int)strlen(buffer);
132         /* If command is empty, give up */
133         if (l==0) return;
134
135         /* Strip trailing spaces */
136         for (i=l-1; i>0 && isspace(buffer[i]); i--) ;
137         buffer[i+1] = 0;
138         //printf("trail strip \"%s\"\n",buffer);
139
140         /* Split into tokens */
141         l = (int)strlen(buffer);
142         num_tokens = 1;
143
144         tokens[0] = buffer;
145         for (i=1; i<l; i++) {
146                 if (buffer[i] == '"') {
147                         tokens[num_tokens - 1] = &buffer[++i];
148                         while (i < l && buffer[i] != '"')
149                                 i++;
150                         buffer[i] = 0;
151                         continue;
152                 }
153                 if (isspace(buffer[i]) || buffer[i] == '=') {
154                         buffer[i] = 0;
155                         while (isspace(buffer[i+1]) && (i+1 < l)) i++;
156                         tokens[num_tokens++] = &buffer[i+1];
157                 }
158         }
159
160         /* Check for matching commands */
161         cmd_execute(num_tokens, tokens);
162 }
163
164
165 int cmd_queue_wait = 0;
166
167 int cmd_queue_process(void)
168 {
169         cmd_queue_t *cmd;
170
171         while (!cmd_queue_wait && cmd_queue_head) {
172                 cmd = cmd_queue_head;
173                 cmd_queue_head = cmd_queue_head->next;
174                 if (!cmd_queue_head)
175                         cmd_queue_tail = NULL;
176
177                 con_printf(CON_DEBUG, "cmd_queue_process: processing %s\n", cmd->command_line);
178                 cmd_parse(cmd->command_line);  // Note, this may change the queue
179
180                 d_free(cmd->command_line);
181                 d_free(cmd);
182         }
183
184         if (cmd_queue_wait > 0) {
185                 cmd_queue_wait--;
186                 if (Function_mode == FMODE_GAME) {
187                         con_printf(CON_DEBUG, "cmd_queue_process: waiting\n");
188                         return 1;
189                 }
190         }
191
192         return 0;
193 }
194
195
196 /* execute until there are no commands left */
197 void cmd_queue_flush(void)
198 {
199         while (cmd_queue_process()) {
200         }
201 }
202
203
204 /* Add some commands to the queue to be executed */
205 void cmd_enqueue(int insert, char *input)
206 {
207         cmd_queue_t *new, *head, *tail;
208         char output[CMD_MAX_LENGTH];
209         char *optr;
210
211         Assert(input != NULL);
212         head = tail = NULL;
213
214         while (*input) {
215                 optr = output;
216                 int quoted = 0;
217
218                 /* Strip leading spaces */
219                 while(isspace(*input) || *input == ';')
220                         input++;
221
222                 /* If command is empty, give up */
223                 if (! *input)
224                         continue;
225
226                 /* Find the end of this line (\n, ;, or nul) */
227                 do {
228                         if (!*input)
229                                 break;
230                         if (*input == '"') {
231                                 quoted = 1 - quoted;
232                                 continue;
233                         } else if ( *input == '\n' || (!quoted && *input == ';') ) {
234                                 input++;
235                                 break;
236                         }
237                 } while ((*optr++ = *input++));
238                 *optr = 0;
239
240                 /* make a new queue item, add it to list */
241                 MALLOC(new, cmd_queue_t, 1);
242                 new->command_line = d_strdup(output);
243                 new->next = NULL;
244
245                 if (!head)
246                         head = new;
247                 if (tail)
248                         tail->next = new;
249                 tail = new;
250
251                 con_printf(CON_DEBUG, "cmd_enqueue: adding %s\n", output);
252         }
253
254         if (insert) {
255                  /* add our list to the head of the main list */
256                 if (cmd_queue_head)
257                         tail->next = cmd_queue_head;
258                 if (!cmd_queue_tail)
259                         cmd_queue_tail = tail;
260                 
261                 cmd_queue_head = head;
262                 con_printf(CON_DEBUG, "cmd_enqueue: added to front of list\n");
263         } else {
264                 /* add our list to the tail of the main list */
265                 if (!cmd_queue_head)
266                         cmd_queue_head = head;
267                 if (cmd_queue_tail)
268                         cmd_queue_tail->next = head;
269                 
270                 cmd_queue_tail = tail;
271                 con_printf(CON_DEBUG, "cmd_enqueue: added to back of list\n");
272         }
273 }
274
275 void cmd_enqueuef(int insert, char *fmt, ...)
276 {
277         va_list arglist;
278         char buf[CMD_MAX_LENGTH];
279         
280         va_start (arglist, fmt);
281         vsnprintf (buf, CMD_MAX_LENGTH, fmt, arglist);
282         va_end (arglist);
283         
284         cmd_enqueue(insert, buf);
285 }
286
287
288 /* Attempt to autocomplete an input string */
289 char *cmd_complete(char *input)
290 {
291         cmd_t *ptr;
292         cmd_alias_t *aptr;
293
294         int len = (int)strlen(input);
295
296         if (!len)
297                 return NULL;
298
299         for (ptr = cmd_list; ptr != NULL; ptr = ptr->next)
300                 if (!strnicmp(input, ptr->name, len))
301                         return ptr->name;
302
303         for (aptr = cmd_alias_list; aptr != NULL; aptr = aptr->next)
304                 if (!strnicmp(input, aptr->name, len))
305                         return aptr->name;
306
307         return cvar_complete(input);
308 }
309
310
311 /* alias */
312 void cmd_alias(int argc, char **argv)
313 {
314         cmd_alias_t *alias;
315         char buf[CMD_MAX_LENGTH] = "";
316         int i;
317
318         if (argc == 2 && !stricmp(argv[1], "-h")) {
319                 con_printf(CON_NORMAL, "%s <name> <commands>\n", argv[0]);
320                 con_printf(CON_NORMAL, "    define <name> as an alias for <commands>\n");
321                 con_printf(CON_NORMAL, "%s <name>\n", argv[0]);
322                 con_printf(CON_NORMAL, "    show the current definition of <name>\n");
323                 con_printf(CON_NORMAL, "%s\n", argv[0]);
324                 con_printf(CON_NORMAL, "    show all defined aliases\n");
325                 return;
326         }
327
328         if (argc < 2) {
329                 con_printf(CON_NORMAL, "aliases:\n");
330                 for (alias = cmd_alias_list; alias; alias = alias->next)
331                         con_printf(CON_NORMAL, "%s: %s\n", alias->name, alias->value);
332                 return;
333         }
334
335         if (argc == 2) {
336                 for (alias = cmd_alias_list; alias; alias = alias->next)
337                         if (!stricmp(argv[1], alias->name)) {
338                                 con_printf(CON_NORMAL, "%s: %s\n", alias->name, alias->value);
339                                 return;
340                         }
341
342                 con_printf(CON_NORMAL, "alias: %s not found\n", argv[1]);
343                 return;
344         }
345
346         for (i = 2; i < argc; i++) {
347                 if (i > 2)
348                         strncat(buf, " ", CMD_MAX_LENGTH);
349                 strncat(buf, argv[i], CMD_MAX_LENGTH);
350         }
351
352         for (alias = cmd_alias_list; alias; alias = alias->next) {
353                 if (!stricmp(argv[1], alias->name)) {
354                         d_free(alias->value);
355                         alias->value = d_strdup(buf);
356                         return;
357                 }
358         }
359
360         MALLOC(alias, cmd_alias_t, 1);
361         strncpy(alias->name, argv[1], ALIAS_NAME_MAX);
362         alias->value = d_strdup(buf);
363         alias->next = cmd_alias_list;
364         cmd_alias_list = alias;
365 }
366
367
368 /* unalias */
369 void cmd_unalias(int argc, char **argv)
370 {
371         cmd_alias_t *alias, *prev_alias = NULL;
372
373         if (argc != 2 || (argc == 2 && !stricmp(argv[1], "-h"))) {
374                 con_printf(CON_NORMAL, "%s <name>\n", argv[0]);
375                 con_printf(CON_NORMAL, "    undefine the alias <name>\n");
376                 return;
377         }
378
379         for (alias = cmd_alias_list; alias ; alias = alias->next) {
380                 if (!stricmp(argv[1], alias->name))
381                         break;
382                 prev_alias = alias;
383         }
384
385         if (!alias) {
386                 con_printf(CON_NORMAL, "alias: %s not found\n", argv[1]);
387                 return;
388         }
389
390         if (prev_alias)
391                 prev_alias->next = alias->next;
392         else
393                 cmd_alias_list = alias->next;
394
395         d_free(alias->value);
396         d_free(alias);
397 }
398
399
400 /* echo to console */
401 void cmd_echo(int argc, char **argv)
402 {
403         char buf[CMD_MAX_LENGTH] = "";
404         int i;
405
406         if (argc == 2 && !stricmp(argv[1], "-h")) {
407                 con_printf(CON_NORMAL, "usage: %s [text]\n", argv[0]);
408                 con_printf(CON_NORMAL, "    write <text> to the console\n");
409
410                 return;
411         }
412
413         for (i = 1; i < argc; i++) {
414                 if (i > 1)
415                         strncat(buf, " ", CMD_MAX_LENGTH);
416                 strncat(buf, argv[i], CMD_MAX_LENGTH);
417         }
418         con_printf(CON_NORMAL, "%s\n", buf);
419 }
420
421 /* execute script */
422 void cmd_exec(int argc, char **argv) {
423         cmd_queue_t *new, *head, *tail;
424         PHYSFS_File *f;
425         char line[CMD_MAX_LENGTH] = "";
426
427         if (argc != 2 || (argc == 2 && !stricmp(argv[1], "-h"))) {
428                 con_printf(CON_NORMAL, "usage: %s <file>\n", argv[0]);
429                 con_printf(CON_NORMAL, "    execute <file>\n");
430
431                 return;
432         }
433
434         head = tail = NULL;
435
436         f = PHYSFSX_openReadBuffered(argv[1]);
437         if (!f) {
438                 con_printf(CON_CRITICAL, "exec: %s not found\n", argv[1]);
439                 return;
440         }
441         while (PHYSFSX_gets(f, line)) {
442                 /* make a new queue item, add it to list */
443                 MALLOC(new, cmd_queue_t, 1);
444                 new->command_line = d_strdup(line);
445                 new->next = NULL;
446
447                 if (!head)
448                         head = new;
449                 if (tail)
450                         tail->next = new;
451                 tail = new;
452
453                 con_printf(CON_DEBUG, "cmd_exec: adding %s\n", line);
454         }
455         PHYSFS_close(f);
456
457         /* add our list to the head of the main list */
458         if (cmd_queue_head)
459                 tail->next = cmd_queue_head;
460         if (!cmd_queue_tail)
461                 cmd_queue_tail = tail;
462
463         cmd_queue_head = head;
464         con_printf(CON_DEBUG, "cmd_exec: added to front of list\n");
465 }
466
467
468 /* get help */
469 void cmd_help(int argc, char **argv)
470 {
471         cmd_t *cmd;
472
473         if (argc > 2 || (argc == 2 && !stricmp(argv[1], "-h"))) {
474                 con_printf(CON_NORMAL, "usage: %s [command]\n", argv[0]);
475                 con_printf(CON_NORMAL, "    get help for <command>, or list all commands if not specified.\n");
476
477                 return;
478         }
479
480         if (argc < 2) {
481                 con_printf(CON_NORMAL, "Available commands:\n");
482                 for (cmd = cmd_list; cmd; cmd = cmd->next) {
483                         con_printf(CON_NORMAL, "    %s\n", cmd->name);
484                 }
485
486                 return;
487         }
488
489         cmd_insertf("%s -h", argv[1]);
490 }
491
492
493 /* execute script */
494 void cmd_wait(int argc, char **argv)
495 {
496         if (argc > 2 || (argc == 2 && !stricmp(argv[1], "-h"))) {
497                 con_printf(CON_NORMAL, "usage: %s [n]\n", argv[0]);
498                 con_printf(CON_NORMAL, "    stop processing commands, resume in <n> cycles (default 1)\n");
499
500                 return;
501         }
502
503         if (argc < 2)
504                 cmd_queue_wait = 1;
505         else
506                 cmd_queue_wait = atoi(argv[1]);
507 }
508
509
510 void cmd_free(void)
511 {
512         void *p, *temp;
513
514         p = cmd_list;
515         while (p) {
516                 temp = p;
517                 p = ((cmd_t *)p)->next;
518                 d_free(temp);
519         }
520
521         p = cmd_alias_list;
522         while (p) {
523                 d_free(((cmd_alias_t *)p)->value);
524                 temp = p;
525                 p = ((cmd_alias_t *)p)->next;
526                 d_free(temp);
527         }
528 }
529
530
531 void cmd_init(void)
532 {
533         cmd_addcommand("alias", cmd_alias);
534         cmd_addcommand("unalias", cmd_unalias);
535         cmd_addcommand("echo", cmd_echo);
536         cmd_addcommand("exec", cmd_exec);
537         cmd_addcommand("help", cmd_help);
538         cmd_addcommand("wait", cmd_wait);
539
540         atexit(cmd_free);
541 }