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