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