]> icculus.org git repositories - btb/d2x.git/blob - main/cmd.c
formatting
[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         int i, l;
127
128         Assert(input != NULL);
129         
130         /* Strip leading spaces */
131         while( isspace(*input) ) { ++input; }
132         strncpy( buffer, input, CMD_MAX_LENGTH );
133
134         //printf("lead strip \"%s\"\n",buffer);
135         l = (int)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 = (int)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, CMD_MAX_LENGTH, 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
296         int len = (int)strlen(input);
297
298         if (!len)
299                 return NULL;
300
301         for (ptr = cmd_list; ptr != NULL; ptr = ptr->next)
302                 if (!strnicmp(input, ptr->name, len))
303                         return ptr->name;
304
305         for (aptr = cmd_alias_list; aptr != NULL; aptr = aptr->next)
306                 if (!strnicmp(input, aptr->name, len))
307                         return aptr->name;
308
309         return cvar_complete(input);
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                 con_printf(CON_NORMAL, "aliases:\n");
322                 for (alias = cmd_alias_list; alias; alias = alias->next)
323                         con_printf(CON_NORMAL, "%s: %s\n", alias->name, alias->value);
324                 return;
325         }
326
327         if (argc == 2) {
328                 for (alias = cmd_alias_list; alias; alias = alias->next)
329                         if (!stricmp(argv[1], alias->name)) {
330                                 con_printf(CON_NORMAL, "%s: %s\n", alias->name, alias->value);
331                                 return;
332                         }
333
334                 con_printf(CON_NORMAL, "alias: %s not found\n", argv[1]);
335                 return;
336         }
337
338         for (i = 2; i < argc; i++) {
339                 if (i > 2)
340                         strncat(buf, " ", CMD_MAX_LENGTH);
341                 strncat(buf, argv[i], CMD_MAX_LENGTH);
342         }
343
344         for (alias = cmd_alias_list; alias; alias = alias->next) {
345                 if (!stricmp(argv[1], alias->name)) {
346                         d_free(alias->value);
347                         alias->value = d_strdup(buf);
348                         return;
349                 }
350         }
351
352         MALLOC(alias, cmd_alias_t, 1);
353         strncpy(alias->name, argv[1], ALIAS_NAME_MAX);
354         alias->value = d_strdup(buf);
355         alias->next = cmd_alias_list;
356         cmd_alias_list = alias;
357 }
358
359
360 /* unalias */
361 void cmd_unalias(int argc, char **argv)
362 {
363         cmd_alias_t *alias, *prev_alias = NULL;
364
365         if (argc < 2 || argc > 2) {
366                 cmd_insertf("help %s", argv[0]);
367                 return;
368         }
369         
370         for (alias = cmd_alias_list; alias ; alias = alias->next) {
371                 if (!stricmp(argv[1], alias->name))
372                         break;
373                 prev_alias = alias;
374         }
375
376         if (!alias) {
377                 con_printf(CON_NORMAL, "unalias: %s not found\n", argv[1]);
378                 return;
379         }
380
381         if (prev_alias)
382                 prev_alias->next = alias->next;
383         else
384                 cmd_alias_list = alias->next;
385
386         d_free(alias->value);
387         d_free(alias);
388 }
389
390
391 /* echo to console */
392 void cmd_echo(int argc, char **argv)
393 {
394         char buf[CMD_MAX_LENGTH] = "";
395         int i;
396
397         for (i = 1; i < argc; i++) {
398                 if (i > 1)
399                         strncat(buf, " ", CMD_MAX_LENGTH);
400                 strncat(buf, argv[i], CMD_MAX_LENGTH);
401         }
402         con_printf(CON_NORMAL, "%s\n", buf);
403 }
404
405 /* execute script */
406 void cmd_exec(int argc, char **argv) {
407         cmd_queue_t *new, *head, *tail;
408         PHYSFS_File *f;
409         char line[CMD_MAX_LENGTH] = "";
410
411         if (argc < 2 || argc > 2) {
412                 cmd_insertf("help %s", argv[0]);
413                 return;
414         }
415         
416         head = tail = NULL;
417
418         f = PHYSFSX_openReadBuffered(argv[1]);
419         if (!f) {
420                 con_printf(CON_CRITICAL, "exec: %s not found\n", argv[1]);
421                 return;
422         }
423         while (PHYSFSX_gets(f, line)) {
424                 /* make a new queue item, add it to list */
425                 MALLOC(new, cmd_queue_t, 1);
426                 new->command_line = d_strdup(line);
427                 new->next = NULL;
428
429                 if (!head)
430                         head = new;
431                 if (tail)
432                         tail->next = new;
433                 tail = new;
434
435                 con_printf(CON_DEBUG, "cmd_exec: adding %s\n", line);
436         }
437         PHYSFS_close(f);
438
439         /* add our list to the head of the main list */
440         if (cmd_queue_head)
441                 tail->next = cmd_queue_head;
442         if (!cmd_queue_tail)
443                 cmd_queue_tail = tail;
444
445         cmd_queue_head = head;
446         con_printf(CON_DEBUG, "cmd_exec: added to front of list\n");
447 }
448
449
450 /* get help */
451 void cmd_help(int argc, char **argv)
452 {
453         cmd_t *cmd;
454
455         if (argc > 2) {
456                 cmd_insertf("help %s", argv[0]);
457                 return;
458         }
459
460         if (argc < 2) {
461                 con_printf(CON_NORMAL, "Available commands:\n");
462                 for (cmd = cmd_list; cmd; cmd = cmd->next) {
463                         con_printf(CON_NORMAL, "    %s\n", cmd->name);
464                 }
465
466                 return;
467         }
468
469         for (cmd = cmd_list; cmd != NULL; cmd = cmd->next)
470                 if (!stricmp(argv[1], cmd->name))
471                         break;
472
473         if (!cmd) {
474                 con_printf(CON_URGENT, "Command %s not found\n", argv[1]);
475                 return;
476         }
477
478         if (!cmd->help_text) {
479                 con_printf(CON_NORMAL, "%s: no help found\n", argv[1]);
480                 return;
481         }
482
483         con_printf(CON_NORMAL, "%s\n", cmd->help_text);
484 }
485
486
487 /* execute script */
488 void cmd_wait(int argc, char **argv)
489 {
490         if (argc > 2) {
491                 cmd_insertf("help %s", argv[0]);
492                 return;
493         }
494
495         if (argc < 2)
496                 cmd_queue_wait = 1;
497         else
498                 cmd_queue_wait = atoi(argv[1]);
499 }
500
501
502 void cmd_free(void)
503 {
504         cmd_t *cmd_p;
505         cmd_alias_t *alias_p;
506
507         cmd_p = cmd_list;
508         while (cmd_p) {
509                 cmd_t *temp = cmd_p;
510                 cmd_p = cmd_p->next;
511                 d_free(temp);
512         }
513
514         alias_p = cmd_alias_list;
515         while (alias_p) {
516                 cmd_alias_t *temp = alias_p;
517                 d_free(alias_p->value);
518                 alias_p = alias_p->next;
519                 d_free(temp);
520         }
521 }
522
523
524 void cmd_init(void)
525 {
526         cmd_addcommand("alias",     cmd_alias,      "alias <name> <commands>\n" "    define <name> as an alias for <commands>\n"
527                                                     "alias <name>\n"            "    show the current definition of <name>\n"
528                                                     "alias\n"                   "    show all defined aliases");
529         cmd_addcommand("unalias",   cmd_unalias,    "unalias <name>\n"          "    undefine the alias <name>");
530         cmd_addcommand("echo",      cmd_echo,       "echo [text]\n"             "    write <text> to the console");
531         cmd_addcommand("exec",      cmd_exec,       "exec <file>\n"             "    execute <file>");
532         cmd_addcommand("help",      cmd_help,       "help [command]\n"          "    get help for <command>, or list all commands if not specified.");
533         cmd_addcommand("wait",      cmd_wait,       "usage: wait [n]\n"         "    stop processing commands, resume in <n> cycles (default 1)");
534
535         atexit(cmd_free);
536 }