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