1 /*
2 * cmd.c -- functions for powwow's built-in #commands
3 *
4 * (created: Finn Arne Gangstad (Ilie), Dec 25th, 1993)
5 *
6 * Copyright (C) 1998 by Massimiliano Ghilardi
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <signal.h>
19 #include <ctype.h>
20 #include <time.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #ifdef HAVE_LIBDL
30 #include <dlfcn.h>
31 #endif
32
33 #include "defines.h"
34 #include "main.h"
35 #include "utils.h"
36 #include "beam.h"
37 #include "cmd.h"
38 #include "cmd2.h"
39 #include "edit.h"
40 #include "list.h"
41 #include "map.h"
42 #include "tcp.h"
43 #include "tty.h"
44 #include "eval.h"
45 #include "log.h"
46
47 /* local function declarations */
48 #define _ __P ((char *arg))
49
50 static void cmd_help _, cmd_shell _, cmd_action _, cmd_add _,
51 cmd_addstatic _, cmd_alias _, cmd_at _, cmd_beep _, cmd_bind _,
52 cmd_cancel _, cmd_capture _, cmd_clear _, cmd_connect _, cmd_cpu _,
53 cmd_do _, cmd_delim _, cmd_edit _, cmd_emulate _, cmd_exe _,
54 cmd_file _, cmd_for _, cmd_hilite _, cmd_history _, cmd_host _,
55 cmd_identify _, cmd_if _, cmd_in _, cmd_init _, cmd_isprompt _,
56 cmd_key _, cmd_keyedit _,
57 cmd_load _, cmd_map _, cmd_mark _, cmd_movie _,
58 cmd_net _, cmd_nice _, cmd_option _,
59 cmd_prefix _, cmd_print _, cmd_prompt _, cmd_put _,
60 cmd_qui _, cmd_quit _, cmd_quote _,
61 cmd_rawsend _, cmd_rawprint _, cmd_rebind _, cmd_rebindall _, cmd_rebindALL _,
62 cmd_record _, cmd_request _, cmd_reset _, cmd_retrace _,
63 cmd_save _, cmd_send _, cmd_setvar _, cmd_snoop _, cmd_spawn _, cmd_stop _,
64 cmd_time _, cmd_var _, cmd_ver _, cmd_while _, cmd_write _,
65 cmd_eval _, cmd_zap _, cmd_module _, cmd_group _, cmd_speedwalk _, cmd_groupdelim _;
66
67 #ifdef BUG_TELNET
68 static void cmd_color _;
69 #endif
70
71 #undef _
72
73 /* This must be init'd now at runtime */
74 cmdstruct *commands = NULL;
75
76 #define C(name, func, help) { NULL, name, help, func, NULL }
77
78 /* The builtin commands */
79 cmdstruct default_commands[] =
80 {
81 C("help", cmd_help,
82 "[keys|math|command]\tthis text, or help on specified topic"),
83 C("17", (function_str)0,
84 "command\t\t\trepeat \"command\" 17 times"),
85 #ifndef NO_SHELL
86 C("!", cmd_shell,
87 "shell-command\t\texecute a shell command using /bin/sh"),
88 #endif
89 C("action", cmd_action,
90 "[[<|=|>|%][+|-]name] [{pattern|(expression)} [=[command]]]\n"
91 "\t\t\t\tdelete/list/define actions"),
92 C("add", cmd_add,
93 "{string|(expr)}\t\tadd the string to word completion list"),
94 C("addstatic", cmd_addstatic,
95 "{string|(expr)}\t\tadd the string to the static word completion list"),
96 C("alias", cmd_alias,
97 "[name[=[text]]]\t\tdelete/list/define aliases"),
98 C("at", cmd_at,
99 "[name [(time-string) [command]]\n\t\t\t\tset time of delayed label"),
100 C("beep", cmd_beep,
101 "\t\t\t\tmake your terminal beep (like #print (*7))"),
102 C("bind", cmd_bind,
103 "[edit|name [seq][=[command]]]\n"
104 "\t\t\t\tdelete/list/define key bindings"),
105 C("cancel", cmd_cancel,
106 "[number]\t\tcancel editing session"),
107 C("capture", cmd_capture,
108 "[filename]\t\tbegin/end of capture to file"),
109 C("clear", cmd_clear,
110 "\t\t\tclear input line (use from spawned programs)"),
111 #ifdef BUG_TELNET
112 C("color", cmd_color,
113 "attr\t\t\tset default colors/attributes"),
114 #endif
115 C("connect", cmd_connect,
116 "[connect-id [initstr] [address port]\n"
117 "\t\t\t\topen a new connection"),
118 C("cpu", cmd_cpu,
119 "\t\t\t\tshow CPU time used by powwow"),
120 C("delim", cmd_delim,
121 "[normal|program|{custom [chars]}]\n"
122 "\t\t\t\tset word completion delimeters"),
123 C("do", cmd_do,
124 "(expr) command\t\trepeat \"command\" (expr) times"),
125 C("edit", cmd_edit,
126 "\t\t\t\tlist editing sessions"),
127 C("emulate", cmd_emulate,
128 "[<|!]{text|(expr)}\tprocess result as if received from host"),
129 C("exe", cmd_exe,
130 "[<|!]{text|(string-expr)}\texecute result as if typed from keyboard"),
131 C("file", cmd_file,
132 "[=[filename]]\t\tset/show powwow definition file"),
133 C("for", cmd_for,
134 "([init];check;[loop]) command\n"
135 "\t\t\t\twhile \"check\" is true exec \"command\""),
136 C("group", cmd_group,
137 "[name] [on|off|list]\tgroup alias/action manipulation"),
138 C("groupdelim", cmd_groupdelim,
139 "[delimiter]\tchange delimiter for action/alias groups"),
140 C("hilite", cmd_hilite,
141 "[attr]\t\t\thighlight your input line"),
142 C("history", cmd_history,
143 "[{number|(expr)}]\tlist/execute commands in history"),
144 C("host", cmd_host,
145 "[hostname port]]\tset/show address of default host"),
146 C("identify", cmd_identify,
147 "[startact [endact]]\tsend MUME client identification"),
148 C("if", cmd_if,
149 "(expr) instr1 [; #else instr2]\n"
150 "\t\t\t\tif \"expr\" is true execute \"instr1\",\n"
151 "\t\t\t\totherwise execute \"instr2\""),
152 C("in", cmd_in,
153 "[label [(delay) [command]]]\tdelete/list/define delayed labels"),
154 C("init", cmd_init,
155 "[=[command]]\t\tdefine command to execute on connect to host"),
156 C("isprompt", cmd_isprompt,
157 "\t\t\trecognize a prompt as such"),
158 C("key", cmd_key,
159 "name\t\t\texecute the \"name\" key binding"),
160 C("keyedit", cmd_keyedit,
161 "editing-name\t\trun a line-editing function"),
162 C("load", cmd_load,
163 "[filename]\t\tload powwow settings from file"),
164 C("map", cmd_map,
165 "[-[number]|walksequence]\tshow/clear/edit (auto)map"),
166 C("mark", cmd_mark,
167 "[string[=[attr]]]\t\tdelete/list/define markers"),
168 #ifdef HAVE_LIBDL
169 C("module", cmd_module,
170 "[name]\t\t\tload shared library extension"),
171 #endif
172 C("movie", cmd_movie,
173 "[filename]\t\tbegin/end of movie record to file"),
174 C("net", cmd_net,
175 "\t\t\t\tprint amount of data received from/sent to host"),
176 C("nice", cmd_nice,
177 "[{number|(expr)}[command]]\n"
178 "\t\t\t\tset/show priority of new actions/marks"),
179 C("option", cmd_option,
180 "[[+|-|=]name]|list\tlist or view various options"),
181 C("prefix", cmd_prefix,
182 "string\t\t\tprefix all lines with string"),
183 C("", cmd_eval,
184 "(expr)\t\t\tevaluate expression, trashing result"),
185 C("print", cmd_print,
186 "[<|!][text|(expr)]\tprint text/result on screen, appending a \\n\n"
187 "\t\t\t\tif no argument, prints value of variable $0"),
188 C("prompt", cmd_prompt,
189 "[[<|=|>|%][+|-]name] [{pattern|(expression)} [=[prompt-command]]]\n"
190 "\t\t\t\tdelete/list/define actions on prompts"),
191 C("put", cmd_put,
192 "{text|(expr)}\t\tput text/result of expression in history"),
193 C("qui", cmd_qui,
194 "\t\t\t\tdo nothing"),
195 C("quit", cmd_quit,
196 "\t\t\t\tquit powwow"),
197 C("quote", cmd_quote,
198 "[on|off]\t\t\ttoggle verbatim-flag on/off"),
199 C("rawsend", cmd_rawsend,
200 "{string|(expr)}\tsend raw data to the MUD"),
201 C("rawprint", cmd_rawprint,
202 "{string|(expr)}\tsend raw data to the screen"),
203 C("rebind", cmd_rebind,
204 "name [seq]\t\tchange sequence of a key binding"),
205 C("rebindall", cmd_rebindall,
206 "\t\t\trebind all key bindings"),
207 C("rebindALL", cmd_rebindALL,
208 "\t\t\trebind ALL key bindings, even trivial ones"),
209 C("record", cmd_record,
210 "[filename]\t\tbegin/end of record to file"),
211 C("request", cmd_request,
212 "[editor][prompt][all]\tsend various identification strings"),
213 C("reset", cmd_reset,
214 "<list-name>\t\tclear the whole defined list and reload default"),
215 C("retrace", cmd_retrace,
216 "[number]\t\tretrace the last number steps"),
217 C("save", cmd_save,
218 "[filename]\t\tsave powwow settings to file"),
219 C("send", cmd_send,
220 "[<|!]{text|(expr)}\teval expression, sending result to the MUD"),
221 C("setvar", cmd_setvar,
222 "name[=text|(expr)]\tset/show internal limits and variables"),
223 C("snoop", cmd_snoop,
224 "connect-id\t\ttoggle output display for connections"),
225 C("spawn", cmd_spawn,
226 "connect-id command\ttalk with a shell command"),
227 C("speedwalk", cmd_speedwalk,
228 "[speedwalk sequence]\texecute a speedwalk sequence explicitly"),
229 C("stop", cmd_stop,
230 "\t\t\t\tremove all delayed commands from active list"),
231 C("time", cmd_time,
232 "\t\t\t\tprint current time and date"),
233 C("var", cmd_var,
234 "variable [= [<|!]{string|(expr)} ]\n"
235 "\t\t\t\twrite result into the variable"),
236 C("ver", cmd_ver,
237 "\t\t\t\tshow powwow version"),
238 C("while", cmd_while,
239 "(expr) instr\t\twhile \"expr\" is true execute \"instr\""),
240 C("write", cmd_write,
241 "[>|!](expr;name)\t\twrite result of expr to \"name\" file"),
242 C("zap", cmd_zap,
243 "connect-id\t\t\tclose a connection"),
244 { NULL }
245 };
246
_cmd_sort_name(cmdstruct * cmd)247 char *_cmd_sort_name( cmdstruct *cmd ) {
248 if( cmd -> sortname == NULL )
249 return( cmd -> name );
250 else
251 return( cmd -> sortname );
252 }
253
254 /* Adds a cmd to commands (inserts the ptr in the list, DO NOT FREE IT) */
cmd_add_command(cmdstruct * cmd)255 void cmd_add_command( cmdstruct *cmd ) {
256 /* do insert/sort */
257 cmdstruct *c = commands;
258
259 /*
260 * make sure it doesn't override another commmand
261 * this is important not just because it'd be irritating,
262 * but if a module defined the command in the global static
263 * space, it would create an infinite loop because the -> next
264 * ptr would point at itself
265 *
266 * doing it up front because based on the sortname, we may never see
267 * the dup item if we do it at sort time
268 */
269 for( c = commands; c != NULL; c = c -> next ) {
270 if( strcmp( cmd -> name, c -> name ) == 0 ) {
271 PRINTF( "#error %s is already defined\n", c -> name );
272 return;
273 }
274 }
275
276
277 /* catch insertion to head of list */
278 if( commands == NULL ) {
279 /* no commands yet */
280 commands = cmd;
281 cmd -> next = NULL;
282 return;
283 }
284
285 if( strcmp( _cmd_sort_name( commands ), _cmd_sort_name( cmd ) ) > 0 ) {
286 /* this is lower in sort than every item, so
287 * make it the head of the list */
288 cmd -> next = commands;
289 commands = cmd;
290 return;
291 }
292
293 for( c = commands; c != NULL; c = c -> next ) {
294 if( strcmp( _cmd_sort_name( cmd ), _cmd_sort_name( c ) ) >= 0 ) {
295 /* Need second check to handle empty string case */
296 if( c -> next == NULL || strcmp( _cmd_sort_name( cmd ), _cmd_sort_name( c -> next ) ) <= 0 ) {
297 /*PRINTF( "Inserting %s after %s\n", cmd -> name, c -> name ); */
298
299 /* insert after this one, it is greater than this
300 * entry but less than the next */
301 cmd -> next = c -> next;
302 c -> next = cmd;
303 return;
304 }
305 }
306 }
307
308 PRINTF( "ERROR INSERTING COMMAND\n" );
309 }
310
311 /* Init the command listing, called from main */
initialize_cmd(void)312 void initialize_cmd(void) {
313 int i;
314
315 /* Now add the default command list */
316 for( i = 0; default_commands[ i ].name; i++ )
317 cmd_add_command( &default_commands[ i ] );
318 }
319
320 #ifdef HAVE_LIBDL
__P1(char *,arg)321 static void cmd_module __P1 (char *,arg) {
322 char libname[1024];
323 void *lib;
324 void (*func)();
325
326 int pindex;
327 struct stat junk;
328 char *prefixes[] = {
329 PLUGIN_DIR,
330 ".",
331 "/lib/powwow",
332 "/usr/lib/powwow",
333 "/usr/local/lib/powwow",
334 "$HOME/.powwow/lib" /* this doesn't work, but is here to remind me :p */
335 };
336
337 arg = skipspace(arg);
338
339 /* I changed it to work this way so that you can have libs in multiple places and
340 * also eventually to allow it to use .dll instead of .so under the cygwin environment */
341 for( pindex = 0; pindex < 5; pindex++ ) {
342 memset( libname, 0, sizeof libname );
343
344 /* don't look for name without .so, it breaks if you have a file
345 * with the same name in the current dir and making it .so for sure
346 * will skip these files since they are probably not libs to load
347 snprintf( libname, 1024, "%s/%s", prefixes[ pindex ], arg );
348 if( stat( libname, &junk ) == 0 ) {
349 break;
350 }
351 */
352
353 snprintf( libname, 1024, "%s/%s.so", prefixes[ pindex ], arg );
354 if( stat( libname, &junk ) == 0 ) {
355 break;
356 }
357 }
358
359 /* open lib */
360 lib = dlopen( libname, RTLD_GLOBAL | RTLD_LAZY );
361 if( ! lib ) {
362 PRINTF( "#module error: %s\n", dlerror() );
363 return;
364 }else{
365 PRINTF( "#module loaded %s\n", libname );
366 }
367
368 func = dlsym( lib, "powwow_init" );
369 if( func ) {
370 (*func)();
371 }else{
372 PRINTF( "#module error: %s\n", dlerror() );
373 }
374 }
375 #endif
376
__P1(char *,arg)377 static void cmd_group __P1 (char *,arg) {
378 char *group;
379 int active;
380 aliasnode *p;
381 actionnode *a;
382
383 arg = skipspace(arg);
384
385 if( *arg ) {
386 arg = first_regular( group = arg, ' ');
387 *arg = '\0';
388 arg = skipspace( arg + 1 );
389
390 if( strcmp( arg, "on" ) == 0 ) {
391 active = 1;
392 }else if( strcmp( arg, "off" ) == 0 ) {
393 active = 0;
394 }else if( strcmp( arg, "list" ) == 0 ) {
395 PRINTF( "#not implemented\n" );
396 return;
397 }else{
398 PRINTF( "#unknown group command, use off/on/list\n" );
399 return;
400 }
401
402 /* Now loop over all aliases/actions by groupname and toggle */
403 for( p = sortedaliases; p; p = p -> snext ) {
404 if( p -> group && strcmp( p -> group, group ) == 0 ) {
405 p -> active = active;
406 }
407 }
408
409 /* Same for actions */
410 for( a = actions; a; a = a -> next ) {
411 if( a -> group && strcmp( a -> group, group ) == 0 ) {
412 a -> active = active;
413 }
414 }
415 }else{
416 PRINTF( "#group name required\n" );
417 }
418 }
419
__P1(char *,arg)420 static void cmd_groupdelim __P1 (char *,arg) {
421 if( *arg != 0 ) {
422 free( group_delim );
423 group_delim = my_strdup( arg );
424 PRINTF( "#group delimiter is now '%s'\n", group_delim );
425 }
426 }
427
__P1(char *,arg)428 static void cmd_help __P1 (char *,arg)
429 {
430 int i, size;
431 char *text, *tmp;
432 FILE *f;
433 char line[BUFSIZE];
434 int len;
435 cmdstruct *c;
436
437 arg = skipspace(arg);
438 if (*arg == '#') arg++;
439 if (!*arg) {
440 size = 25;
441 for( c = commands; c != NULL; c = c -> next )
442 size += strlen(c -> name) + strlen(c -> help) + 5;
443
444 text = tmp = (char *)malloc(size);
445 if (!text) {
446 errmsg("malloc");
447 return;
448 }
449
450 /* do not use sprintf() return value, almost every OS returns a different thing. */
451 sprintf(tmp, "#help\n#commands available:\n");
452 tmp += strlen(tmp);
453
454 for( c = commands; c != NULL; c = c -> next ) {
455 sprintf(tmp, "#%s %s\n", c -> name, c -> help);
456 tmp += strlen(tmp);
457 }
458
459 message_edit(text, strlen(text), 1, 1);
460 return;
461 }
462
463 if (!strncmp(arg, "copyright", strlen(arg))) {
464 int fd, left, got = 0;
465 struct stat stbuf;
466
467 if (stat(copyfile, &stbuf) < 0) {
468 errmsg("stat(copyright file)");
469 return;
470 }
471
472 if (!(text = (char *)malloc(left = stbuf.st_size))) {
473 errmsg("malloc");
474 return;
475 }
476 if ((fd = open(copyfile, O_RDONLY)) < 0) {
477 errmsg("open(copyright file)");
478 free(text);
479 return;
480 }
481 while (left > 0) {
482 while ((i = read(fd, text + got, left)) < 0 && errno == EINTR)
483 ;
484 if (i < 0 && errno == EINTR) {
485 errmsg("read (copyright file)");
486 free(text);
487 close(fd);
488 return;
489 }
490 if (i == 0)
491 break;
492 left -= i, got += i;
493 }
494 close(fd);
495 message_edit(text, strlen(text), 1, 1);
496 return;
497 }
498
499 /* !copyright */
500
501 f = fopen(helpfile, "r");
502 if (!f) {
503 PRINTF("#cannot open help file \"%s\": %s\n",
504 helpfile, strerror(errno));
505 return;
506 }
507
508 while ((tmp = fgets(line, BUFSIZE, f)) &&
509 (line[0] != '@' || strncmp(line + 1, arg, strlen(arg))))
510 ;
511
512 if (!tmp) {
513 PRINTF("#no entry for \"%s\" in the help file.\n", arg);
514 fclose(f);
515 return;
516 }
517
518 if (!(text = (char *)malloc(size = BUFSIZE))) {
519 errmsg("malloc");
520 fclose(f);
521 return;
522 }
523
524 /* the first line becomes $TITLE */
525 tmp = strchr(line, '\n');
526 if (tmp) *tmp = '\0';
527 i = sprintf(text, "Help on '%s'\n", line + 1);
528
529 /* allow multiple commands to share the same help */
530 while (fgets(line, BUFSIZE, f) && line[0] == '@') ;
531
532 do {
533 if ((len = strlen(line)) >= size - i) {
534 /* Not enough space in current buffer */
535
536 if (!(tmp = (char *)malloc(size += BUFSIZE))) {
537 errmsg("malloc");
538 free(text);
539 fclose(f);
540 return;
541 } else {
542 memcpy(tmp, text, i);
543 free(text);
544 text = tmp;
545 }
546 }
547 memcpy(text + i, line, len);
548 i += len;
549 } while (fgets(line, BUFSIZE, f) && line[0] != '@');
550
551 fclose(f);
552 text[i] = '\0'; /* safe, there is space */
553 message_edit(text, strlen(text), 1, 1);
554 }
555
__P1(char *,arg)556 static void cmd_clear __P1 (char *,arg)
557 {
558 if (line_status == 0) {
559 clear_input_line(opt_compact);
560 if (!opt_compact) {
561 tty_putc('\n');
562 col0 = 0;
563 }
564 status(1);
565 }
566 }
567
568 #ifndef NO_SHELL
__P1(char *,arg)569 static void cmd_shell __P1 (char *,arg)
570 {
571 if (!*arg) {
572 if (opt_info) {
573 PRINTF("#that's easy.\n");
574 }
575 } else {
576 tty_quit();
577
578 if (system(arg) == -1) {
579 perror("system()");
580 }
581
582 tty_start();
583 tty_gotoxy(col0 = 0, line0 = lines -1);
584 tty_puts(tty_clreoln);
585 }
586 }
587 #endif
588
__P1(char *,arg)589 static void cmd_alias __P1 (char *,arg)
590 {
591 arg = skipspace(arg);
592 if (!*arg)
593 show_aliases();
594 else
595 parse_alias(arg);
596 }
597
__P1(char *,arg)598 static void cmd_action __P1 (char *,arg)
599 {
600 arg = skipspace(arg);
601 if (!*arg)
602 show_actions();
603 else
604 parse_action(arg, 0);
605 }
606
__P1(char *,arg)607 static void cmd_prompt __P1 (char *,arg)
608 {
609 arg = skipspace(arg);
610 if (!*arg)
611 show_prompts();
612 else
613 parse_action(arg, 1);
614 }
615
__P1(char *,arg)616 static void cmd_beep __P1 (char *,arg)
617 {
618 tty_putc('\007');
619 }
620
621 /*
622 * create/list/edit/delete bindings
623 */
__P1(char *,arg)624 static void cmd_bind __P1 (char *,arg)
625 {
626 arg = skipspace(arg);
627 if (!*arg)
628 show_binds(0);
629 else if (!strcmp(arg, "edit"))
630 show_binds(1);
631 else
632 parse_bind(arg);
633 }
634
__P1(char *,arg)635 static void cmd_delim __P1 (char *,arg)
636 {
637 char buf[BUFSIZE];
638 int n;
639
640 arg = skipspace(arg);
641 if (!*arg) {
642 PRINTF("#delim: \"%s\" (%s)\n", delim_name[delim_mode], DELIM);
643 return;
644 }
645
646 arg = split_first_word(buf, BUFSIZE, arg);
647 n = 0;
648 while (n < DELIM_MODES && strncmp(delim_name[n], buf, strlen(buf)) != 0)
649 n++;
650
651 if (n >= DELIM_MODES) {
652 PRINTF("#delim [normal|program|{custom <chars>}\n");
653 return;
654 }
655
656 if (n == DELIM_CUSTOM) {
657 if (!strchr(arg, ' ')) {
658 my_strncpy(buf+1, arg, BUFSIZE-2);
659 *buf = ' '; /* force ' ' in the delims */
660 arg = buf;
661 }
662 unescape(arg);
663 set_custom_delimeters(arg);
664 } else
665 delim_mode = n;
666 }
667
__P1(char *,arg)668 static void cmd_do __P1 (char *,arg)
669 {
670 int type;
671 long result;
672
673 arg = skipspace(arg);
674 if (*arg != '(') {
675 PRINTF("#do: ");
676 print_error(error=MISMATCH_PAREN_ERROR);
677 return;
678 }
679 arg++;
680
681 type = evall(&result, &arg);
682 if (REAL_ERROR) return;
683
684 if (type != TYPE_NUM) {
685 PRINTF("#do: ");
686 print_error(error=NO_NUM_VALUE_ERROR);
687 return;
688 }
689
690 if (*arg == ')') { /* skip the ')' */
691 if (*++arg == ' ')
692 arg++;
693 }
694 else {
695 PRINTF("#do: ");
696 print_error(error=MISSING_PAREN_ERROR);
697 return;
698 }
699
700 if (result >= 0)
701 while (!error && result--)
702 (void)parse_instruction(arg, 1, 0, 1);
703 else {
704 PRINTF("#do: bogus repeat count \"%ld\"\n", result);
705 }
706 }
707
__P1(char *,arg)708 static void cmd_hilite __P1 (char *,arg)
709 {
710 int attr;
711
712 arg = skipspace(arg);
713 attr = parse_attributes(arg);
714 if (attr == -1) {
715 PRINTF("#attribute syntax error.\n");
716 if (opt_info)
717 show_attr_syntax();
718 } else {
719 attr_string(attr, edattrbeg, edattrend);
720
721 edattrbg = ATTR(attr) & ATTR_INVERSE ? 1
722 : BACKGROUND(attr) != NO_COLOR || ATTR(attr) & ATTR_BLINK;
723
724 if (opt_info) {
725 PRINTF("#input highlighting is now %so%s%s.\n",
726 edattrbeg, (attr == NOATTRCODE) ? "ff" : "n",
727 edattrend);
728 }
729 }
730 }
731
__P1(char *,arg)732 static void cmd_history __P1 (char *,arg)
733 {
734 int num = 0;
735 long buf;
736
737 arg = skipspace(arg);
738
739 if (history_done >= MAX_HIST) {
740 print_error(error=HISTORY_RECURSION_ERROR);
741 return;
742 }
743 history_done++;
744
745 if (*arg == '(') {
746 arg++;
747 num = evall(&buf, &arg);
748 if (!REAL_ERROR && num != TYPE_NUM)
749 error=NO_NUM_VALUE_ERROR;
750 if (REAL_ERROR) {
751 PRINTF("#history: ");
752 print_error(error=NO_NUM_VALUE_ERROR);
753 return;
754 }
755 num = (int)buf;
756 } else
757 num = atoi(arg);
758
759 if (num > 0)
760 exe_history(num);
761 else
762 show_history(-num);
763 }
764
__P1(char *,arg)765 static void cmd_host __P1 (char *,arg)
766 {
767 char newhost[BUFSIZE];
768
769 arg = skipspace(arg);
770 if (*arg) {
771 arg = split_first_word(newhost, BUFSIZE, arg);
772 if (*arg) {
773 my_strncpy(hostname, newhost, BUFSIZE-1);
774 portnumber = atoi(arg);
775 if (opt_info) {
776 PRINTF("#host set to: %s %d\n", hostname, portnumber);
777 }
778 } else {
779 PRINTF("#host: missing portnumber.\n");
780 }
781 } else if (*hostname)
782 sprintf(inserted_next, "#host %.*s %d", BUFSIZE-INTLEN-8,
783 hostname, portnumber);
784 else {
785 PRINTF("#syntax: #host hostname port\n");
786 }
787 }
788
__P1(char *,arg)789 static void cmd_request __P1 (char *,arg)
790 {
791 char *idprompt = "~$#EP2\nG\n";
792 char *ideditor = "~$#EI\n";
793 char buf[256];
794 int all, len;
795
796 if (tcp_fd == -1) {
797 PRINTF("#not connected to a MUD!\n");
798 return;
799 }
800 while (*(arg = skipspace(arg))) {
801 arg = split_first_word(buf, 256, arg);
802 if (*buf) {
803 all = !strcmp(buf, "all");
804 len = strlen(buf);
805 if ((all || !strncmp(buf, "editor", len))) {
806 tcp_raw_write(tcp_fd, ideditor, strlen(ideditor));
807 CONN_LIST(tcp_fd).flags |= IDEDITOR;
808 if (opt_info) {
809 PRINTF("#request editor: %s done!\n", ideditor);
810 }
811 }
812 if ((all || !strncmp(buf, "prompt", len))) {
813 tcp_raw_write(tcp_fd, idprompt, strlen(idprompt));
814 CONN_LIST(tcp_fd).flags |= IDPROMPT;
815 if (opt_info) {
816 PRINTF("#request prompt: %s done!\n", idprompt);
817 }
818 }
819 }
820 }
821
822 }
823
__P1(char *,arg)824 static void cmd_identify __P1 (char *,arg)
825 {
826 edit_start[0] = edit_end[0] = '\0';
827 if (*arg) {
828 char *p = strchr(arg, ' ');
829 if (p) {
830 *(p++) = '\0';
831 my_strncpy(edit_end, p, BUFSIZE-1);
832 }
833 my_strncpy(edit_start, arg, BUFSIZE-1);
834 }
835 cmd_request("editor");
836 }
837
__P1(char *,arg)838 static void cmd_in __P1 (char *,arg)
839 {
840 char *name;
841 long millisec, buf;
842 int type;
843 delaynode **p;
844
845 arg = skipspace(arg);
846 if (!*arg) {
847 show_delays();
848 return;
849 }
850
851 arg = first_regular(name = arg, ' ');
852 if (*arg)
853 *arg++ = 0;
854
855 unescape(name);
856
857 p = lookup_delay(name, 0);
858 if (!*p) p = lookup_delay(name, 1);
859
860 if (!*arg && !*p) {
861 PRINTF("#unknown delay label, cannot show: \"%s\"\n", name);
862 return;
863 }
864 if (!*arg) {
865 show_delaynode(*p, 1);
866 return;
867 }
868 if (*arg != '(') {
869 PRINTF("#in: ");
870 print_error(error=MISMATCH_PAREN_ERROR);
871 return;
872 }
873 arg++; /* skip the '(' */
874
875 type = evall(&buf, &arg);
876 if (!REAL_ERROR) {
877 if (type!=TYPE_NUM)
878 error=NO_NUM_VALUE_ERROR;
879 else if (*arg != ')')
880 error=MISSING_PAREN_ERROR;
881 }
882 if (REAL_ERROR) {
883 PRINTF("#in: ");
884 print_error(error);
885 return;
886 }
887
888 arg = skipspace(arg+1);
889 millisec = buf;
890 if (*p && millisec)
891 change_delaynode(p, arg, millisec);
892 else if (!*p && millisec) {
893 if (*arg)
894 new_delaynode(name, arg, millisec);
895 else {
896 PRINTF("#cannot create delay label without a command.\n");
897 }
898 } else if (*p && !millisec) {
899 if (opt_info) {
900 PRINTF("#deleting delay label: %s %s\n", name, (*p)->command);
901 }
902 delete_delaynode(p);
903 } else {
904 PRINTF("#unknown delay label, cannot delete: \"%s\"\n", name);
905 }
906 }
907
__P1(char *,arg)908 static void cmd_at __P1 (char *,arg)
909 {
910 char *name, *buf = NULL;
911 char dayflag=0;
912 struct tm *twhen;
913 int num, hour = -1, minute = -1, second = -1;
914 delaynode **p;
915 long millisec;
916 ptr pbuf = (ptr)0;
917
918 arg = skipspace(arg);
919 if (!*arg) {
920 show_delays();
921 return;
922 }
923
924 arg = first_regular(name = arg, ' ');
925 if (*arg)
926 *arg++ = 0;
927
928 unescape(name);
929
930 p = lookup_delay(name, 0);
931 if (!*p) p = lookup_delay(name, 1);
932
933 if (!*arg && !*p) {
934 PRINTF("#unknown delay label, cannot show: \"%s\"\n", name);
935 return;
936 }
937 if (!*arg) {
938 show_delaynode(*p, 2);
939 return;
940 }
941 if (*arg != '(') {
942 PRINTF("#in: ");
943 print_error(error=MISMATCH_PAREN_ERROR);
944 return;
945 }
946 arg++; /* skip the '(' */
947
948 (void)evalp(&pbuf, &arg);
949 if (REAL_ERROR) {
950 print_error(error);
951 ptrdel(pbuf);
952 return;
953 }
954 if (pbuf) {
955 /* convert time-string into hour, minute, second */
956 buf = skipspace(ptrdata(pbuf));
957 if (!*buf || !isdigit(*buf)) {
958 PRINTF("#at: ");
959 print_error(error=NO_NUM_VALUE_ERROR);
960 ptrdel(pbuf);
961 return;
962 }
963 num = atoi(buf);
964 second = num % 100;
965 minute = (num /= 100) % 100;
966 hour = num / 100;
967 }
968 if (hour < 0 || hour>23 || minute < 0 || minute>59
969 || second < 0 || second>59) {
970
971 PRINTF("#at: #error: invalid time \"%s\"\n",
972 pbuf && buf ? buf : (char *)"");
973 error=OUT_RANGE_ERROR;
974 ptrdel(pbuf);
975 return;
976 }
977 ptrdel(pbuf);
978
979 if (*arg == ')') { /* skip the ')' */
980 if (*++arg == ' ')
981 arg++;
982 }
983 else {
984 PRINTF("#at: ");
985 print_error(error=MISSING_PAREN_ERROR);
986 return;
987 }
988
989 arg = skipspace(arg);
990 update_now();
991 twhen = localtime((time_t *)&now.tv_sec);
992 /* put current year, month, day in calendar struct */
993
994 if (hour < twhen->tm_hour ||
995 (hour == twhen->tm_hour &&
996 (minute < twhen->tm_min ||
997 (minute == twhen->tm_min &&
998 second <= twhen->tm_sec)))) {
999 dayflag = 1;
1000 /* it is NOT possible to define an #at refering to the past */
1001 }
1002
1003 /* if you use a time smaller than the current, it refers to tomorrow */
1004
1005 millisec = (hour - twhen->tm_hour) * 3600 + (minute - twhen->tm_min) * 60 +
1006 second - twhen->tm_sec + (dayflag ? 24*60*60 : 0);
1007 millisec *= mSEC_PER_SEC; /* Comparing time with current calendar,
1008 we finally got the delay */
1009 millisec -= now.tv_usec / uSEC_PER_mSEC;
1010
1011 if (*p)
1012 change_delaynode(p, arg, millisec);
1013 else
1014 if (*arg)
1015 new_delaynode(name, arg, millisec);
1016 else {
1017 PRINTF("#cannot create delay label without a command.\n");
1018 }
1019 }
1020
__P1(char *,arg)1021 static void cmd_init __P1 (char *,arg)
1022 {
1023 arg = skipspace(arg);
1024
1025 if (*arg == '=') {
1026 if (*++arg) {
1027 my_strncpy(initstr, arg, BUFSIZE-1);
1028 if (opt_info) {
1029 PRINTF("#init: %s\n", initstr);
1030 }
1031 } else {
1032 *initstr = '\0';
1033 if (opt_info) {
1034 PRINTF("#init cleared.\n");
1035 }
1036 }
1037 } else
1038 sprintf(inserted_next, "#init =%.*s", BUFSIZE-8, initstr);
1039 }
1040
__P1(char *,arg)1041 static void cmd_isprompt __P1 (char *,arg)
1042 {
1043 if (tcp_fd == tcp_main_fd) {
1044 int i;
1045 long l;
1046 arg = skipspace(arg);
1047 if (*arg == '(') {
1048 arg++;
1049 i = evall(&l, &arg);
1050 if (!REAL_ERROR) {
1051 if (i!=TYPE_NUM)
1052 error=NO_NUM_VALUE_ERROR;
1053 else if (*arg != ')')
1054 error=MISSING_PAREN_ERROR;
1055 }
1056 if (REAL_ERROR) {
1057 PRINTF("#isprompt: ");
1058 print_error(error);
1059 return;
1060 }
1061 i = (int)l;
1062 } else
1063 i = atoi(arg);
1064
1065 if (i == 0)
1066 surely_isprompt = -1;
1067 else if (i < 0) {
1068 if (i > -NUMPARAM && *VAR[-i].str)
1069 ptrtrunc(prompt->str, surely_isprompt = ptrlen(*VAR[-i].str));
1070 } else
1071 ptrtrunc(prompt->str, surely_isprompt = i);
1072 }
1073 }
1074
__P1(char *,arg)1075 static void cmd_key __P1 (char *,arg)
1076 {
1077 keynode *q=NULL;
1078
1079 arg = skipspace(arg);
1080 if (!*arg)
1081 return;
1082
1083 if ((q = *lookup_key(arg)))
1084 q->funct(q->call_data);
1085 else {
1086 PRINTF("#no such key: \"%s\"\n", arg);
1087 }
1088 }
1089
__P1(char *,arg)1090 static void cmd_keyedit __P1 (char *,arg)
1091 {
1092 int function;
1093 char *param;
1094
1095 arg = skipspace(arg);
1096 if (!*arg)
1097 return;
1098
1099 if ((function = lookup_edit_name(arg, ¶m)))
1100 internal_functions[function].funct(param);
1101 else {
1102 PRINTF("#no such editing function: \"%s\"\n", arg);
1103 }
1104 }
1105
__P1(char *,arg)1106 static void cmd_map __P1 (char *,arg)
1107 {
1108 arg = skipspace(arg);
1109 if (!*arg) /* show map */
1110 map_show();
1111 else if (*arg == '-') /* retrace steps without walking */
1112 map_retrace(atoi(arg + 1), 0);
1113 else
1114 map_walk(arg, 1, 1);
1115 }
1116
__P1(char *,arg)1117 static void cmd_retrace __P1 (char *,arg)
1118 {
1119 map_retrace(atoi(arg), 1);
1120 }
1121
__P1(char *,arg)1122 static void cmd_mark __P1 (char *,arg)
1123 {
1124 if (!*arg)
1125 show_marks();
1126 else
1127 parse_mark(arg);
1128 }
1129
__P1(char *,arg)1130 static void cmd_nice __P1 (char *,arg)
1131 {
1132 int nnice = a_nice;
1133 arg = skipspace(arg);
1134 if (!*arg) {
1135 PRINTF("#nice: %d\n", a_nice);
1136 return;
1137 }
1138 if (isdigit(*arg)) {
1139 a_nice = 0;
1140 while (isdigit(*arg)) {
1141 a_nice *= 10;
1142 a_nice += *arg++ - '0';
1143 }
1144 }
1145 else if (*arg++=='(') {
1146 long buf;
1147 int type;
1148
1149 type = evall(&buf, &arg);
1150 if (!REAL_ERROR && type!=TYPE_NUM)
1151 error=NO_NUM_VALUE_ERROR;
1152 else if (!REAL_ERROR && *arg++ != ')')
1153 error=MISSING_PAREN_ERROR;
1154 if (REAL_ERROR) {
1155 PRINTF("#nice: ");
1156 print_error(error);
1157 return;
1158 }
1159 a_nice = (int)buf;
1160 if (a_nice<0)
1161 a_nice = 0;
1162 }
1163 arg = skipspace(arg);
1164 if (*arg) {
1165 parse_instruction(arg, 0, 0, 1);
1166 a_nice = nnice;
1167 }
1168 }
1169
__P1(char *,arg)1170 static void cmd_prefix __P1 (char *,arg)
1171 {
1172 strcpy(prefixstr, arg);
1173 if (opt_info) {
1174 PRINTF("#prefix %s.\n", *arg ? "set" : "cleared");
1175 }
1176 }
1177
__P1(char *,arg)1178 static void cmd_quote __P1 (char *,arg)
1179 {
1180 arg = skipspace(arg);
1181 if (!*arg)
1182 verbatim ^= 1;
1183 else if (!strcmp(arg, "on"))
1184 verbatim = 1;
1185 else if (!strcmp(arg, "off"))
1186 verbatim = 0;
1187 if (opt_info) {
1188 PRINTF("#%s mode.\n", verbatim ? "verbatim" : "normal");
1189 }
1190 }
1191
1192 /*
1193 * change the escape sequence of an existing binding
1194 */
__P1(char *,arg)1195 static void cmd_rebind __P1 (char *,arg)
1196 {
1197 parse_rebind(arg);
1198 }
1199
__P1(char *,arg)1200 static void cmd_rebindall __P1 (char *,arg)
1201 {
1202 keynode *kp;
1203 char *seq;
1204
1205 for (kp = keydefs; kp; kp = kp->next) {
1206 seq = kp->sequence;
1207 if (kp->seqlen == 1 && seq[0] < ' ')
1208 ;
1209 else if (kp->seqlen == 2 && seq[0] == '\033' && isalnum(seq[1]))
1210 ;
1211 else {
1212 parse_rebind(kp->name);
1213 if (error)
1214 break;
1215 }
1216 }
1217 }
1218
__P1(char *,arg)1219 static void cmd_rebindALL __P1 (char *,arg)
1220 {
1221 keynode *kp;
1222
1223 for (kp = keydefs; kp; kp = kp->next) {
1224 parse_rebind(kp->name);
1225 if (error)
1226 break;
1227 }
1228 }
1229
__P1(char *,arg)1230 static void cmd_reset __P1 (char *,arg)
1231 {
1232 char all = 0;
1233 arg = skipspace(arg);
1234 if (!*arg) {
1235 PRINTF("#reset: must specify one of:\n");
1236 tty_puts(" alias, action, bind, in (or at), mark, prompt, var, all.\n");
1237 return;
1238 }
1239 all = !strcmp(arg, "all");
1240 if (all || !strcmp(arg, "alias")) {
1241 int n;
1242 for (n = 0; n < MAX_HASH; n++) {
1243 while (aliases[n])
1244 delete_aliasnode(&aliases[n]);
1245 }
1246 if (!all)
1247 return;
1248 }
1249 if (all || !strcmp(arg, "action")) {
1250 while (actions)
1251 delete_actionnode(&actions);
1252 if (!all)
1253 return;
1254 }
1255 if (all || !strcmp(arg, "bind")) {
1256 while (keydefs)
1257 delete_keynode(&keydefs);
1258 tty_add_initial_binds();
1259 tty_add_walk_binds();
1260 if (!all)
1261 return;
1262 }
1263 if (all || !strcmp(arg, "in") || !strcmp(arg, "at")) {
1264 while (delays)
1265 delete_delaynode(&delays);
1266 while (dead_delays)
1267 delete_delaynode(&dead_delays);
1268 if (!all)
1269 return;
1270 }
1271 if (all || !strcmp(arg, "mark")) {
1272 while (markers)
1273 delete_marknode(&markers);
1274 if (!all)
1275 return;
1276 }
1277 if (all || !strcmp(arg, "prompt")) {
1278 while (prompts)
1279 delete_promptnode(&prompts);
1280 if (!all)
1281 return;
1282 }
1283 if (all || !strcmp(arg, "var")) {
1284 int n;
1285 varnode **first;
1286
1287 for (n = 0; n < MAX_HASH; n++) {
1288 while (named_vars[0][n])
1289 delete_varnode(&named_vars[0][n], 0);
1290 first = &named_vars[1][n];
1291 while (*first) {
1292 if (is_permanent_variable(*first))
1293 first = &(*first)->next;
1294 else
1295 delete_varnode(first, 1);
1296 }
1297 }
1298
1299 for (n = 0; n < NUMVAR; n++) {
1300 *var[n].num = 0;
1301 ptrdel(*var[n].str);
1302 *var[n].str = NULL;
1303 }
1304 if (!all)
1305 return;
1306 }
1307 }
1308
__P1(char *,arg)1309 static void cmd_snoop __P1 (char *,arg)
1310 {
1311 if (!*arg) {
1312 PRINTF("#snoop: which connection?\n");
1313 } else
1314 tcp_togglesnoop(arg);
1315 }
1316
__P1(char *,arg)1317 static void cmd_stop __P1 (char *,arg)
1318 {
1319 delaynode *dying;
1320
1321 if (delays)
1322 update_now();
1323
1324 while (delays) {
1325 dying = delays;
1326 delays = dying->next;
1327 dying->when.tv_sec = now.tv_sec;
1328 dying->when.tv_usec = now.tv_usec;
1329 dying->next = dead_delays;
1330 dead_delays = dying;
1331 }
1332 if (opt_info) {
1333 PRINTF("#all delayed labels are now disabled.\n");
1334 }
1335 }
1336
__P1(char *,arg)1337 static void cmd_time __P1 (char *,arg)
1338 {
1339 struct tm *s;
1340 char buf[BUFSIZE];
1341
1342 update_now();
1343 s = localtime((time_t *)&now.tv_sec);
1344 (void)strftime(buf, BUFSIZE - 1, "%a, %d %b %Y %H:%M:%S", s);
1345 PRINTF("#current time is %s\n", buf);
1346 }
1347
__P1(char *,arg)1348 static void cmd_ver __P1 (char *,arg)
1349 {
1350 printver();
1351 }
1352
__P1(char *,arg)1353 static void cmd_emulate __P1 (char *,arg)
1354 {
1355 char kind;
1356 FILE *fp;
1357 long start, end, i = 1;
1358 int len;
1359 ptr pbuf = (ptr)0;
1360
1361 arg = redirect(arg, &pbuf, &kind, "emulate", 0, &start, &end);
1362 if (REAL_ERROR || !arg)
1363 return;
1364
1365 if (kind) {
1366 char buf[BUFSIZE];
1367
1368 fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r");
1369 if (!fp) {
1370 PRINTF("#emulate: #error opening \"%s\"\n", arg);
1371 print_error(error=SYNTAX_ERROR);
1372 ptrdel(pbuf);
1373 return;
1374 }
1375 status(-1); /* we're pretending we got something from the MUD */
1376 while (!error && (!start || i<=end) && fgets(buf, BUFSIZE, fp))
1377 if (!start || i++>=start)
1378 process_remote_input(buf, strlen(buf));
1379
1380 if (kind == '!') pclose(fp); else fclose(fp);
1381 } else {
1382 status(-1); /* idem */
1383 /* WARNING: no guarantee there is space! */
1384 arg[len = strlen(arg)] = '\n';
1385 arg[++len] = '\0';
1386 process_remote_input(arg, len);
1387 }
1388 ptrdel(pbuf);
1389 }
1390
__P1(char *,arg)1391 static void cmd_eval __P1 (char *,arg)
1392 {
1393 arg = skipspace(arg);
1394 if (*arg=='(') {
1395 arg++;
1396 (void)evaln(&arg);
1397 if (*arg != ')') {
1398 PRINTF("#(): ");
1399 print_error(error=MISSING_PAREN_ERROR);
1400 }
1401 }
1402 else {
1403 PRINTF("#(): ");
1404 print_error(error=MISMATCH_PAREN_ERROR);
1405 }
1406 }
1407
__P1(char *,arg)1408 static void cmd_exe __P1 (char *,arg)
1409 {
1410 char kind;
1411 char *clear;
1412 long offset, start, end, i = 1;
1413 FILE *fp;
1414 ptr pbuf = (ptr)0;
1415
1416 arg = redirect(arg, &pbuf, &kind, "exe", 0, &start, &end);
1417 if (REAL_ERROR || !arg)
1418 return;
1419
1420 if (kind) {
1421 char buf[BUFSIZE];
1422
1423 fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r");
1424 if (!fp) {
1425 PRINTF("#exe: #error opening \"%s\"\n", arg);
1426 error = SYNTAX_ERROR;
1427 ptrdel(pbuf);
1428 return;
1429 }
1430 offset = 0;
1431 /* We may go in to a loop if a single function is more than 4k, but if that's
1432 * the case then maybe you should break it down a little bit :p */
1433 while (!error && (!start || i<=end) && fgets(buf + offset, BUFSIZE - offset, fp))
1434 /* If it ends with \\\n then it's a line continuation, so clear
1435 * the \\\n and do another fgets */
1436 if (buf[offset + strlen(buf + offset) - 2] == '\\') {
1437 /* Clear \n prefixed with a literal backslash '\\' */
1438 if ((clear = strstr(buf + offset, "\\\n")))
1439 *clear = '\0';
1440 offset += strlen(buf + offset);
1441 } else {
1442 if (!start || i++ >= start) {
1443 buf[strlen(buf)-1] = '\0';
1444 parse_user_input(buf, 0);
1445 offset = 0;
1446 }
1447 }
1448
1449 if (kind == '!') pclose(fp); else fclose(fp);
1450 } else
1451 parse_user_input(arg, 0);
1452 ptrdel(pbuf);
1453 }
1454
__P1(char *,arg)1455 static void cmd_print __P1 (char *,arg)
1456 {
1457 char kind;
1458 long start, end, i = 1;
1459 FILE *fp;
1460 ptr pbuf = (ptr)0;
1461
1462 clear_input_line(opt_compact);
1463
1464 if (!*arg) {
1465 smart_print(*VAR[0].str ? ptrdata(*VAR[0].str) : (char *)"", 1);
1466 return;
1467 }
1468
1469 arg = redirect(arg, &pbuf, &kind, "print", 1, &start, &end);
1470 if (REAL_ERROR || !arg)
1471 return;
1472
1473 if (kind) {
1474 char buf[BUFSIZE];
1475 fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r");
1476 if (!fp) {
1477 PRINTF("#print: #error opening \"%s\"\n", arg);
1478 error=SYNTAX_ERROR;
1479 ptrdel(pbuf);
1480 return;
1481 }
1482 while (!error && (!start || i <= end) && fgets(buf, BUFSIZE, fp))
1483 if (!start || i++>=start)
1484 tty_puts(buf);
1485 tty_putc('\n');
1486
1487 if (kind == '!') pclose(fp); else fclose(fp);
1488 } else
1489 smart_print(arg, 1);
1490 ptrdel(pbuf);
1491 }
1492
__P1(char *,arg)1493 static void cmd_send __P1 (char *,arg)
1494 {
1495 char *newline, kind;
1496 long start, end, i = 1;
1497 FILE *fp;
1498 ptr pbuf = (ptr)0;
1499
1500 arg = redirect(arg, &pbuf, &kind, "send", 0, &start, &end);
1501 if (REAL_ERROR ||!arg)
1502 return;
1503
1504 if (kind) {
1505 char buf[BUFSIZE];
1506 fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r");
1507 if (!fp) {
1508 PRINTF("#send: #error opening \"%s\"\n", arg);
1509 error = SYNTAX_ERROR;
1510 ptrdel(pbuf);
1511 return;
1512 }
1513 while (!error && (!start || i<=end) && fgets(buf, BUFSIZE, fp)) {
1514 if ((newline = strchr(buf, '\n')))
1515 *newline = '\0';
1516
1517 if (!start || i++>=start) {
1518 if (opt_echo) {
1519 PRINTF("[%s]\n", buf);
1520 }
1521 tcp_write(tcp_fd, buf);
1522 }
1523 }
1524 if (kind == '!') pclose(fp); else fclose(fp);
1525 } else {
1526 if (opt_echo) {
1527 PRINTF("[%s]\n", arg);
1528 }
1529 tcp_write(tcp_fd, arg);
1530 }
1531 ptrdel(pbuf);
1532 }
1533
__P1(char *,arg)1534 static void cmd_rawsend __P1 (char *,arg)
1535 {
1536 char *tmp = skipspace(arg);
1537
1538 if (*tmp=='(') {
1539 ptr pbuf = (ptr)0;
1540 arg = tmp + 1;
1541 (void)evalp(&pbuf, &arg);
1542 if (REAL_ERROR) {
1543 print_error(error);
1544 ptrdel(pbuf);
1545 } else if (pbuf)
1546 tcp_raw_write(tcp_fd, ptrdata(pbuf), ptrlen(pbuf));
1547 } else {
1548 int len;
1549 if ((len = memunescape(arg, strlen(arg))))
1550 tcp_raw_write(tcp_fd, arg, len);
1551 }
1552 }
1553
__P1(char *,arg)1554 static void cmd_rawprint __P1 (char *,arg)
1555 {
1556 char *tmp = skipspace(arg);
1557
1558 if (*tmp=='(') {
1559 ptr pbuf = (ptr)0;
1560 arg = tmp + 1;
1561 (void)evalp(&pbuf, &arg);
1562 if (REAL_ERROR) {
1563 print_error(error);
1564 ptrdel(pbuf);
1565 } else if (pbuf)
1566 tty_raw_write(ptrdata(pbuf), ptrlen(pbuf));
1567 } else {
1568 int len;
1569 if ((len = memunescape(arg, strlen(arg))))
1570 tty_raw_write(arg, len);
1571 }
1572 }
1573
1574
__P1(char *,arg)1575 static void cmd_write __P1 (char *,arg)
1576 {
1577 ptr p1 = (ptr)0, p2 = (ptr)0;
1578 char *tmp = skipspace(arg), kind;
1579 FILE *fp;
1580
1581 kind = *tmp;
1582 if (kind == '!' || kind == '>')
1583 arg = ++tmp;
1584 else
1585 kind = 0;
1586
1587 if (*tmp=='(') {
1588 arg = tmp + 1;
1589 (void)evalp(&p1, &arg);
1590 if (REAL_ERROR)
1591 goto write_cleanup;
1592
1593 if (*arg == CMDSEP) {
1594 arg++;
1595 (void)evalp(&p2, &arg);
1596 if (!REAL_ERROR && !p2)
1597 error = NO_STRING_ERROR;
1598 if (REAL_ERROR)
1599 goto write_cleanup;
1600 } else {
1601 PRINTF("#write: ");
1602 error=SYNTAX_ERROR;
1603 goto write_cleanup;
1604 }
1605 if (*arg != ')') {
1606 PRINTF("#write: ");
1607 error=MISSING_PAREN_ERROR;
1608 goto write_cleanup;
1609 }
1610 arg = ptrdata(p2);
1611
1612 fp = (kind == '!') ? popen(arg, "w") : fopen(arg, kind ? "w" : "a");
1613 if (!fp) {
1614 PRINTF("#write: #error opening \"%s\"\n", arg);
1615 error=SYNTAX_ERROR;
1616 goto write_cleanup2;
1617 }
1618 fprintf(fp, "%s\n", p1 ? ptrdata(p1) : (char *)"");
1619 fflush(fp);
1620 if (kind == '!') pclose(fp); else fclose(fp);
1621 } else {
1622 PRINTF("#write: ");
1623 error=MISMATCH_PAREN_ERROR;
1624 }
1625
1626 write_cleanup:
1627 if (REAL_ERROR)
1628 print_error(error);
1629 write_cleanup2:
1630 ptrdel(p1);
1631 ptrdel(p2);
1632 }
1633
__P1(char *,arg)1634 static void cmd_var __P1 (char *,arg)
1635 {
1636 char *buf, *expr, *tmp, kind, type, right = 0, deleting = 0;
1637 varnode **p_named_var = NULL, *named_var = NULL;
1638 FILE *fp;
1639 long start, end, i = 1;
1640 int len, idx;
1641 ptr pbuf = (ptr)0;
1642
1643 arg = skipspace(arg);
1644 expr = first_regular(arg, '=');
1645
1646 if (*expr) {
1647 *expr++ = '\0'; /* skip the = */
1648 if (!*expr)
1649 deleting = 1;
1650 else {
1651 right = 1;
1652 if (*expr == ' ')
1653 expr++;
1654 }
1655 }
1656
1657 if (*arg == '$')
1658 type = TYPE_TXT_VAR;
1659 else if (*arg == '@')
1660 type = TYPE_NUM_VAR;
1661 else if (*arg) {
1662 print_error(error=INVALID_NAME_ERROR);
1663 return;
1664 } else {
1665 show_vars();
1666 return;
1667 }
1668
1669 kind = *++arg;
1670 if (isalpha(kind) || kind == '_') {
1671 /* found a named variable */
1672 tmp = arg;
1673 while (*tmp && (isalnum(*tmp) || *tmp == '_'))
1674 tmp++;
1675 if (*tmp) {
1676 print_error(error=INVALID_NAME_ERROR);
1677 return;
1678 }
1679 kind = type==TYPE_TXT_VAR ? 1 : 0;
1680 p_named_var = lookup_varnode(arg, kind);
1681 if (!*p_named_var) {
1682 /* it doesn't (yet) exist */
1683 if (!deleting) {
1684 /* so create it */
1685 named_var = add_varnode(arg, kind);
1686 if (REAL_ERROR)
1687 return;
1688 if (opt_info) {
1689 PRINTF("#new variable: \"%s\"\n", arg - 1);
1690 }
1691 } else {
1692 print_error(error=UNDEFINED_VARIABLE_ERROR);
1693 return;
1694 }
1695 } else
1696 /* it exists, hold on */
1697 named_var = *p_named_var;
1698
1699 idx = named_var->index;
1700 } else {
1701 /* not a named variable, may be
1702 * an unnamed variable or an expression */
1703 kind = type==TYPE_TXT_VAR ? 1 : 0;
1704 tmp = skipspace(arg);
1705 if (*tmp == '(') {
1706 /* an expression */
1707 arg = tmp+1;
1708 idx = evalp(&pbuf, &arg);
1709 if (!REAL_ERROR && idx != TYPE_TXT)
1710 error=NO_STRING_ERROR;
1711 if (REAL_ERROR) {
1712 PRINTF("#var: ");
1713 print_error(error);
1714 ptrdel(pbuf);
1715 return;
1716 }
1717 if (pbuf)
1718 buf = ptrdata(pbuf);
1719 else {
1720 print_error(error=INVALID_NAME_ERROR);
1721 return;
1722 }
1723 char err_det;
1724 if (isdigit(*buf) || *buf=='-' || *buf=='+') {
1725 if (sscanf(buf, "%d%c", &idx, &err_det)==1) {
1726 if (idx < -NUMVAR || idx >= NUMPARAM) {
1727 print_error(error=OUT_RANGE_ERROR);
1728 ptrdel(pbuf);
1729 return;
1730 }
1731 } else {
1732 print_error(error=INVALID_NAME_ERROR);
1733 return;
1734 }
1735 } else {
1736 if (!isalpha(*buf) && *buf!='_') {
1737 print_error(error=INVALID_NAME_ERROR);
1738 return;
1739 }
1740 tmp = buf + 1;
1741 while (*tmp && (isalnum(*tmp) || *tmp=='_'))
1742 tmp++;
1743 if (*tmp) {
1744 print_error(error=INVALID_NAME_ERROR);
1745 return;
1746 }
1747 if (!(named_var = *(p_named_var = lookup_varnode(buf, kind)))) {
1748 if (!deleting) {
1749 named_var = add_varnode(buf, kind);
1750 if (REAL_ERROR) {
1751 print_error(error);
1752 ptrdel(pbuf);
1753 return;
1754 }
1755 if (opt_info) {
1756 PRINTF("#new variable: %c%s\n", kind
1757 ? '$' : '@', buf);
1758 }
1759 } else {
1760 print_error(error=UNDEFINED_VARIABLE_ERROR);
1761 ptrdel(pbuf);
1762 return;
1763 }
1764 }
1765 idx = named_var->index;
1766 }
1767 } else {
1768 /* an unnamed var */
1769 long buf2;
1770
1771 idx = evall(&buf2, &arg);
1772 if (!REAL_ERROR && idx != TYPE_NUM)
1773 error=NO_STRING_ERROR;
1774 if (REAL_ERROR) {
1775 PRINTF("#var: ");
1776 print_error(error);
1777 return;
1778 }
1779 idx = (int)buf2;
1780 if (idx < -NUMVAR || idx >= NUMPARAM) {
1781 print_error(error=OUT_RANGE_ERROR);
1782 return;
1783 }
1784 /* ok, it's an unnamed var */
1785 }
1786 }
1787
1788
1789 if (type == TYPE_TXT_VAR && right && !*VAR[idx].str) {
1790 /* create it */
1791 *VAR[idx].str = ptrnew(PARAMLEN);
1792 if (MEM_ERROR) {
1793 print_error(error);
1794 ptrdel(pbuf);
1795 return;
1796 }
1797 }
1798
1799 if (deleting) {
1800 /* R.I.P. named variables */
1801 if (named_var) {
1802 if (is_permanent_variable(named_var)) {
1803 PRINTF("#cannot delete variable: \"%s\"\n", arg - 1);
1804 } else {
1805 delete_varnode(p_named_var, kind);
1806 if (opt_info) {
1807 PRINTF("#deleted variable: \"%s\"\n", arg - 1);
1808 }
1809 }
1810 } else if ((type = TYPE_TXT_VAR)) {
1811 /* R.I.P. unnamed variables */
1812 if (*VAR[idx].str) {
1813 if (idx < 0) {
1814 ptrdel(*VAR[idx].str);
1815 *VAR[idx].str = 0;
1816 } else
1817 ptrzero(*VAR[idx].str);
1818 }
1819 } else
1820 *VAR[idx].num = 0;
1821 ptrdel(pbuf);
1822 return;
1823 } else if (!right) {
1824 /* no right-hand expression, just show */
1825 if (named_var) {
1826 if (type == TYPE_TXT_VAR) {
1827 pbuf = ptrescape(pbuf, *VAR[idx].str, 0);
1828 if (REAL_ERROR) {
1829 print_error(error);
1830 ptrdel(pbuf);
1831 return;
1832 }
1833 sprintf(inserted_next, "#($%.*s = \"%.*s\")",
1834 BUFSIZE - 10, named_var->name,
1835 BUFSIZE - (int)strlen(named_var->name) - 10,
1836 pbuf ? ptrdata(pbuf) : (char *)"");
1837 } else {
1838 sprintf(inserted_next, "#(@%.*s = %ld)",
1839 BUFSIZE - 8, named_var->name,
1840 *VAR[idx].num);
1841 }
1842 } else {
1843 if (type == TYPE_TXT_VAR) {
1844 pbuf = ptrescape(pbuf, *VAR[idx].str, 0);
1845 sprintf(inserted_next, "#($%d = \"%.*s\")", idx,
1846 BUFSIZE - INTLEN - 10,
1847 pbuf ? ptrdata(pbuf) : (char *)"");
1848 } else {
1849 sprintf(inserted_next, "#(@%d = %ld)", idx,
1850 *VAR[idx].num);
1851 }
1852 }
1853 ptrdel(pbuf);
1854 return;
1855 }
1856
1857 /* only case left: assign a value to a variable */
1858 arg = redirect(expr, &pbuf, &kind, "var", 1, &start, &end);
1859 if (REAL_ERROR || !arg)
1860 return;
1861
1862 if (kind) {
1863 char buf2[BUFSIZE];
1864 fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r");
1865 if (!fp) {
1866 PRINTF("#var: #error opening \"%s\"\n", arg);
1867 error=SYNTAX_ERROR;
1868 ptrdel(pbuf);
1869 return;
1870 }
1871 len = 0;
1872 i = 1;
1873 while (!error && (!start || i<=end) && fgets(buf2+len, BUFSIZE-len, fp))
1874 if (!start || i++>=start)
1875 len += strlen(buf2 + len);
1876
1877 if (kind == '!') pclose(fp); else fclose(fp);
1878 if (len>PARAMLEN)
1879 len = PARAMLEN;
1880 buf2[len] = '\0';
1881 arg = buf2;
1882 }
1883
1884 if (type == TYPE_NUM_VAR) {
1885 arg = skipspace(arg);
1886 type = 1;
1887 len = 0;
1888
1889 if (*arg == '-')
1890 arg++, type = -1;
1891 else if (*arg == '+')
1892 arg++;
1893
1894 if (isdigit(kind=*arg)) while (isdigit(kind)) {
1895 len*=10;
1896 len+=(kind-'0');
1897 kind=*++arg;
1898 }
1899 else {
1900 PRINTF("#var: ");
1901 print_error(error=NO_NUM_VALUE_ERROR);
1902 }
1903 *VAR[idx].num = len * type;
1904 }
1905 else {
1906 *VAR[idx].str = ptrmcpy(*VAR[idx].str, arg, strlen(arg));
1907 if (MEM_ERROR)
1908 print_error(error);
1909 }
1910 ptrdel(pbuf);
1911 }
1912
__P1(char *,arg)1913 static void cmd_setvar __P1 (char *,arg)
1914 {
1915 char *name;
1916 int i, func = 0; /* show */
1917 long buf;
1918
1919 name = arg = skipspace(arg);
1920 arg = first_regular(arg, '=');
1921 if (*arg) {
1922 *arg++ = '\0';
1923 if (*arg) {
1924 func = 1; /* set */
1925 if (*arg == '(') {
1926 arg++;
1927 i = evall(&buf, &arg);
1928 if (!REAL_ERROR && i != TYPE_NUM)
1929 error=NO_NUM_VALUE_ERROR;
1930 else if (!REAL_ERROR && *arg != ')')
1931 error=MISSING_PAREN_ERROR;
1932 } else
1933 buf = strtol(arg, NULL, 0);
1934 } else
1935 buf = 0;
1936
1937 if (REAL_ERROR) {
1938 PRINTF("#setvar: ");
1939 print_error(error);
1940 return;
1941 }
1942 }
1943
1944 i = strlen(name);
1945 if (i && !strncmp(name, "timer", i)) {
1946 vtime t;
1947 update_now();
1948 if (func == 0)
1949 sprintf(inserted_next, "#setvar timer=%ld",
1950 diff_vtime(&now, &ref_time));
1951 else {
1952 t.tv_usec = ((-buf) % mSEC_PER_SEC) * uSEC_PER_mSEC;
1953 t.tv_sec = (-buf) / mSEC_PER_SEC;
1954 ref_time.tv_usec = now.tv_usec;
1955 ref_time.tv_sec = now.tv_sec;
1956 add_vtime(&ref_time, &t);
1957 }
1958 }
1959 else if (i && !strncmp(name, "lines", i)) {
1960 if (func == 0)
1961 sprintf(inserted_next, "#setvar lines=%d", lines);
1962 else {
1963 if (buf > 0)
1964 lines = (int)buf;
1965 if (opt_info) {
1966 PRINTF("#setvar: lines=%d\n", lines);
1967 }
1968 }
1969 }
1970 else if (i && !strncmp(name, "mem", i)) {
1971 if (func == 0)
1972 sprintf(inserted_next, "#setvar mem=%d", limit_mem);
1973 else {
1974 if (buf == 0 || buf >= PARAMLEN)
1975 limit_mem = buf <= INT_MAX ? (int)buf : INT_MAX;
1976 if (opt_info) {
1977 PRINTF("#setvar: mem=%d%s\n", limit_mem,
1978 limit_mem ? "" : " (unlimited)");
1979 }
1980 }
1981 }
1982 else if (i && !strncmp(name, "buffer", i)) {
1983 if (func == 0)
1984 sprintf(inserted_next, "#setvar buffer=%d", log_getsize());
1985 else
1986 log_resize(buf);
1987 } else {
1988 update_now();
1989 PRINTF("#setvar buffer=%d\n#setvar lines=%d\n#setvar mem=%d\n#setvar timer=%ld\n",
1990 log_getsize(), lines, limit_mem, diff_vtime(&now, &ref_time));
1991 }
1992 }
1993
__P1(char *,arg)1994 static void cmd_if __P1 (char *,arg)
1995 {
1996 long buf;
1997 int type;
1998
1999 arg = skipspace(arg);
2000 if (*arg!='(') {
2001 PRINTF("#if: ");
2002 print_error(error=MISMATCH_PAREN_ERROR);
2003 return;
2004 }
2005 arg++; /* skip the '(' */
2006
2007 type = evall(&buf, &arg);
2008 if (!REAL_ERROR) {
2009 if (type!=TYPE_NUM)
2010 error=NO_NUM_VALUE_ERROR;
2011 if (*arg != ')')
2012 error=MISSING_PAREN_ERROR;
2013 else { /* skip the ')' */
2014 if (*++arg == ' ')
2015 arg++;
2016 }
2017 }
2018 if (REAL_ERROR) {
2019 PRINTF("#if: ");
2020 print_error(error);
2021 return;
2022 }
2023
2024 if (buf)
2025 (void)parse_instruction(arg, 0, 0, 1);
2026 else {
2027 arg = get_next_instr(arg);
2028 if (!strncmp(arg = skipspace(arg), "#else ", 6))
2029 (void)parse_instruction(arg + 6, 0, 0, 1);
2030 }
2031 }
2032
__P1(char *,arg)2033 static void cmd_for __P1 (char *,arg)
2034 {
2035 int type = TYPE_NUM, loop=MAX_LOOP;
2036 long buf;
2037 char *check, *tmp, *increm = 0;
2038
2039 arg = skipspace(arg);
2040 if (*arg != '(') {
2041 PRINTF("#for: ");
2042 print_error(error=MISMATCH_PAREN_ERROR);
2043 return;
2044 }
2045 push_params();
2046 if (REAL_ERROR)
2047 return;
2048
2049 arg = skipspace(arg + 1); /* skip the '(' */
2050 if (*arg != CMDSEP)
2051 (void)evaln(&arg); /* execute <init> */
2052
2053 check = arg + 1;
2054
2055 if (REAL_ERROR)
2056 ;
2057 else if (*arg != CMDSEP) {
2058 PRINTF("#for: ");
2059 print_error(error=MISSING_SEPARATOR_ERROR);
2060 }
2061 else while (!error && loop
2062 && (increm=check, (type = evall(&buf, &increm)) == TYPE_NUM
2063 && !error && *increm == CMDSEP && buf)) {
2064
2065 tmp = first_regular(increm + 1, ')');
2066 if (*tmp)
2067 (void)parse_instruction(tmp + 1, 1, 1, 1);
2068 else {
2069 PRINTF("#for: ");
2070 print_error(error=MISSING_PAREN_ERROR);
2071 }
2072
2073 if (!error) {
2074 tmp = increm + 1;
2075 if (*tmp != ')')
2076 (void)evaln(&tmp);
2077 }
2078
2079 loop--;
2080 }
2081 if (REAL_ERROR)
2082 ;
2083 else if (increm && *increm != CMDSEP)
2084 error=MISSING_SEPARATOR_ERROR;
2085 else if (!loop)
2086 error=MAX_LOOP_ERROR;
2087 else if (type != TYPE_NUM)
2088 error=NO_NUM_VALUE_ERROR;
2089 if (REAL_ERROR) {
2090 PRINTF("#for: ");
2091 print_error(error);
2092 }
2093 if (error!=DYN_STACK_UND_ERROR && error!=DYN_STACK_OV_ERROR)
2094 pop_params();
2095 }
2096
__P1(char *,arg)2097 static void cmd_while __P1 (char *,arg)
2098 {
2099 int type = TYPE_NUM, loop=MAX_LOOP;
2100 long buf;
2101 char *check, *tmp;
2102
2103 arg = skipspace(arg);
2104 if (!*arg) {
2105 PRINTF("#while: ");
2106 print_error(error=MISMATCH_PAREN_ERROR);
2107 return;
2108 }
2109 push_params();
2110
2111 check = ++arg; /* skip the '(' */
2112 while (!error && loop
2113 && (arg=check, (type = evall(&buf, &arg)) == TYPE_NUM &&
2114 !error && *arg == ')' && buf)) {
2115
2116 if (*(tmp = arg + 1) == ' ') /* skip the ')' */
2117 tmp++;
2118 if (*tmp)
2119 (void)parse_instruction(tmp, 1, 1, 1);
2120 loop--;
2121 }
2122 if (REAL_ERROR)
2123 ;
2124 else if (*arg != ')')
2125 error=MISSING_PAREN_ERROR;
2126 else if (!loop)
2127 error=MAX_LOOP_ERROR;
2128 else if (type != TYPE_NUM)
2129 error=NO_NUM_VALUE_ERROR;
2130 if (REAL_ERROR) {
2131 PRINTF("#while: ");
2132 print_error(error);
2133 }
2134 if (error!=DYN_STACK_UND_ERROR && error!=DYN_STACK_OV_ERROR)
2135 pop_params();
2136 }
2137
__P1(char *,arg)2138 static void cmd_capture __P1 (char *,arg)
2139 {
2140 arg = skipspace(arg);
2141
2142 if (!*arg) {
2143 if (capturefile) {
2144 log_flush();
2145 fclose(capturefile);
2146 capturefile = NULL;
2147 if (opt_info) {
2148 PRINTF("#end of capture to file.\n");
2149 }
2150 } else {
2151 PRINTF("#capture to what file?\n");
2152 }
2153 } else {
2154 if (capturefile) {
2155 PRINTF("#capture already active.\n");
2156 } else {
2157 short append = 0;
2158 /* Append to log file, if the name starts with '>' */
2159 if (*arg == '>') {
2160 arg++;
2161 append = 1;
2162 }
2163 if ((capturefile = fopen(arg, (append) ? "a" : "w")) == NULL) {
2164 PRINTF("#error writing file \"%s\"\n", arg);
2165 } else if (opt_info) {
2166 PRINTF("#capture to \"%s\" active, \"#capture\" ends.\n", arg);
2167 }
2168 }
2169 }
2170 }
2171
__P1(char *,arg)2172 static void cmd_movie __P1 (char *,arg)
2173 {
2174 arg = skipspace(arg);
2175
2176 if (!*arg) {
2177 if (moviefile) {
2178 log_flush();
2179 fclose(moviefile);
2180 moviefile = NULL;
2181 if (opt_info) {
2182 PRINTF("#end of movie to file.\n");
2183 }
2184 } else {
2185 PRINTF("#movie to what file?\n");
2186 }
2187 } else {
2188 if (moviefile) {
2189 PRINTF("#movie already active.\n");
2190 } else {
2191 if ((moviefile = fopen(arg, "w")) == NULL) {
2192 PRINTF("#error writing file \"%s\"\n", arg);
2193 } else {
2194 if (opt_info) {
2195 PRINTF("#movie to \"%s\" active, \"#movie\" ends.\n", arg);
2196 }
2197 update_now();
2198 movie_last = now;
2199 log_clearsleep();
2200 }
2201 }
2202 }
2203 }
2204
__P1(char *,arg)2205 static void cmd_record __P1 (char *,arg)
2206 {
2207 arg = skipspace(arg);
2208
2209 if (!*arg) {
2210 if (recordfile) {
2211 fclose(recordfile);
2212 recordfile = NULL;
2213 if (opt_info) {
2214 PRINTF("#end of record to file.\n");
2215 }
2216 } else {
2217 PRINTF("#record to what file?\n");
2218 }
2219 } else {
2220 if (recordfile) {
2221 PRINTF("#record already active.\n");
2222 } else {
2223 if ((recordfile = fopen(arg, "w")) == NULL) {
2224 PRINTF("#error writing file \"%s\"\n", arg);
2225 } else if (opt_info) {
2226 PRINTF("#record to \"%s\" active, \"#record\" ends.\n", arg);
2227 }
2228 }
2229 }
2230 }
2231
__P1(char *,arg)2232 static void cmd_edit __P1 (char *,arg)
2233 {
2234 editsess *sp;
2235
2236 if (edit_sess) {
2237 for (sp = edit_sess; sp; sp = sp->next) {
2238 PRINTF("# %s (%u)\n", sp->descr, sp->key);
2239 }
2240 } else {
2241 PRINTF("#no active editors.\n");
2242 }
2243 }
2244
__P1(char *,arg)2245 static void cmd_cancel __P1 (char *,arg)
2246 {
2247 editsess *sp;
2248
2249 if (!edit_sess) {
2250 PRINTF("#no editing sessions to cancel.\n");
2251 } else {
2252 if (*arg) {
2253 for (sp = edit_sess; sp; sp = sp->next)
2254 if (strtoul(arg, NULL, 10) == sp->key) {
2255 cancel_edit(sp);
2256 break;
2257 }
2258 if (!sp) {
2259 PRINTF("#unknown editing session %d\n", atoi(arg));
2260 }
2261 } else {
2262 if (edit_sess->next) {
2263 PRINTF("#several editing sessions active, use #cancel <number>\n");
2264 } else
2265 cancel_edit(edit_sess);
2266 }
2267 }
2268 }
2269
__P1(char *,arg)2270 static void cmd_net __P1 (char *,arg)
2271 {
2272 PRINTF("#received from host: %ld chars, sent to host: %ld chars.\n",
2273 received, sent);
2274 }
2275
2276
2277 #ifndef CLOCKS_PER_SEC
2278 # define CLOCKS_PER_SEC uSEC_PER_SEC
2279 #endif
2280 /* hope it works.... */
2281
__P1(char *,arg)2282 static void cmd_cpu __P1 (char *,arg)
2283 {
2284 float f, l;
2285 update_now();
2286 f = (float)((cpu_clock = clock()) - start_clock) / (float)CLOCKS_PER_SEC;
2287 l = (float)(diff_vtime(&now, &start_time)) / (float)mSEC_PER_SEC;
2288 PRINTF("#CPU time used: %.3f sec. (%.2f%%)\n", f,
2289 (l > 0 && l > f) ? f * 100.0 / l : 100.0);
2290 }
2291
__P0(void)2292 void show_stat __P0 (void)
2293 {
2294 cmd_net(NULL);
2295 cmd_cpu(NULL);
2296 }
2297
2298 #ifdef BUG_TELNET
__P1(char *,arg)2299 static void cmd_color __P1 (char *,arg)
2300 {
2301 int attrcode;
2302
2303 arg = skipspace(arg);
2304 if (!*arg) {
2305 strcpy(tty_modenorm, tty_modenormbackup);
2306 tty_puts(tty_modenorm);
2307 if (opt_info) {
2308 PRINTF("#standard color cleared.\n");
2309 }
2310 return;
2311 }
2312
2313 attrcode = parse_attributes(arg);
2314 if (attrcode == -1) {
2315 PRINTF("#invalid attribute syntax.\n");
2316 if (opt_info)
2317 show_attr_syntax();
2318 } else {
2319 int bg = BACKGROUND(attrcode), fg = FOREGROUND(attrcode);
2320 if (fg >= COLORS || bg >= COLORS) {
2321 PRINTF("#please specify foreground and background colors.\n");
2322 } else {
2323 sprintf(tty_modenorm, "\033[;%c%d;%s%dm",
2324 fg<LOWCOLORS ? '3' : '9', fg % LOWCOLORS,
2325 bg<LOWCOLORS ? "4" :"10", bg % LOWCOLORS);
2326 tty_puts(tty_modenorm);
2327 if (opt_info) {
2328 PRINTF("#standard colour set.\n");
2329 }
2330 }
2331 }
2332 }
2333 #endif
2334
__P1(char *,arg)2335 static void cmd_connect __P1 (char *,arg)
2336 {
2337 #ifdef TERM
2338 PRINTF("#connect: multiple connections not supported in term version.\n");
2339 #else
2340 char *s1, *s2, *s3 = NULL, *s4 = NULL;
2341 int argc = 1;
2342
2343 if (!*skipspace(arg)) {
2344 tcp_show();
2345 return;
2346 }
2347 else {
2348 s1 = strtok(arg, " ");
2349 s2 = strtok(NULL, " ");
2350 if (s2 && *s2) {
2351 argc++;
2352 s3 = strtok(NULL, " ");
2353 if (s3 && *s3) {
2354 argc++;
2355 s4 = strtok(NULL, " ");
2356 if (s4 && *s4)
2357 argc++;
2358 }
2359 }
2360 }
2361
2362 if (argc <= 2) {
2363 if (*hostname)
2364 tcp_open(s1, s2, hostname, portnumber);
2365 else {
2366 PRINTF("#connect: no host defined!\n#syntax: #connect session-id [[init-str] [hostname] [port]]\n");
2367 }
2368 } else if (argc == 3) {
2369 if (!*hostname) {
2370 my_strncpy(hostname, s2, BUFSIZE-1);
2371 portnumber = atoi(s3);
2372 }
2373 tcp_open(s1, NULL, s2, atoi(s3));
2374 } else {
2375 if (!*hostname) {
2376 my_strncpy(hostname, s3, BUFSIZE-1);
2377 portnumber = atoi(s4);
2378 }
2379 tcp_open(s1, s2, s3, atoi(s4));
2380 }
2381 #endif /* TERM */
2382 }
2383
2384
__P1(char *,arg)2385 static void cmd_spawn __P1 (char *,arg)
2386 {
2387 char s[BUFSIZE];
2388 if (*(arg = skipspace(arg))) {
2389 arg = split_first_word(s, BUFSIZE, arg);
2390 if (*arg && *s) {
2391 tcp_spawn(s, arg);
2392 return;
2393 }
2394 }
2395 PRINTF("#syntax: #spawn connect-id command\n");
2396 }
2397
2398 /* If you have speedwalk off but still want to use a speedwalk sequence,
2399 * you can manually trigger a speedwalk this way */
__P1(char *,arg)2400 static void cmd_speedwalk __P1 (char *,arg)
2401 {
2402 char save_speedwalk = opt_speedwalk;
2403 PRINTF( "Executing speedwalk '%s'\n", arg );
2404 opt_speedwalk = 1;
2405 if( ! map_walk( skipspace(arg), 0, 0 ) ) {
2406 PRINTF( "Error executing speedwalk\n" );
2407 }
2408 opt_speedwalk = save_speedwalk;
2409 }
2410
__P1(char *,arg)2411 static void cmd_zap __P1 (char *,arg)
2412 {
2413 if (!*arg) {
2414 PRINTF("#zap: no connection name.\n");
2415 } else
2416 tcp_close(arg);
2417 }
2418
__P1(char *,arg)2419 static void cmd_qui __P1 (char *,arg)
2420 {
2421 PRINTF("#you have to write '#quit' - no less, to quit!\n");
2422 }
2423
__P1(char *,arg)2424 static void cmd_quit __P1 (char *,arg)
2425 {
2426 if (*arg) { /* no skipspace() here! */
2427 PRINTF("#quit: spurious argument?\n");
2428 } else
2429 exit_powwow();
2430 }
2431
2432 static const struct {
2433 const char *name;
2434 char *option;
2435 const char *doc;
2436 } options[] = {
2437 { "autoclear", &opt_autoclear,
2438 "clear input line before executing commands" },
2439 { "autoprint", &opt_autoprint,
2440 "#print lines matched by actions" },
2441 { "compact", &opt_compact,
2442 "remove prompt when receiving new messages from mud" },
2443 { "debug", &opt_debug,
2444 "print commands before executing" },
2445 { "echo", &opt_echo,
2446 "print command action commands when executed" },
2447 { "exit", &opt_exit,
2448 "automatically exit powwow when mud connection closes" },
2449 { "history", &opt_history,
2450 "also save command history" },
2451 { "info", &opt_info,
2452 "print information about command effects" },
2453 { "keyecho", &opt_keyecho,
2454 "print command bound to key when executed" },
2455 { "reprint", &opt_reprint,
2456 "reprint sent commands when getting new prompt" },
2457 { "sendsize", &opt_sendsize,
2458 "send terminal size when opening connection" },
2459 { "speedwalk", &opt_speedwalk,
2460 "enable speed walking (ness3ew...)" },
2461 { "words", &opt_words,
2462 "also save word history" },
2463 { "wrap", &opt_wrap,
2464 "enable word wrapping" },
2465 { NULL }
2466 };
2467
2468 /* print all options to 'file', or tty if file is NULL; return -1 on
2469 * error, 1 on success */
__P1(FILE *,file)2470 int print_all_options __P1 (FILE *,file)
2471 {
2472 const char *prefix = "#option";
2473 int width = (file ? 80 : cols) - 16;
2474 int len = 0, i;
2475 for (i = 0; options[i].name; ++i) {
2476 int res;
2477 if (file)
2478 res = fprintf(file, "%s %c%s", prefix,
2479 *options[i].option ? '+' : '-',
2480 options[i].name);
2481 else
2482 res = tty_printf("%s %c%s", prefix,
2483 *options[i].option ? '+' : '-',
2484 options[i].name);
2485 if (res < 0)
2486 return -1;
2487 /* don't rely on printf() return value */
2488 len += strlen(prefix) + strlen(options[i].name) + 2;
2489 if (len >= width) {
2490 prefix = "\n#option";
2491 len = -1;
2492 } else {
2493 prefix = "";
2494 }
2495 }
2496 if (file) {
2497 fputc('\n', file);
2498 } else {
2499 tty_putc('\n');
2500 status(1);
2501 }
2502 return 1;
2503 }
2504
__P1(char *,arg)2505 static void cmd_option __P1 (char *,arg)
2506 {
2507 char buf[BUFSIZE];
2508 int count = 0;
2509
2510 arg = skipspace(arg);
2511 if (!*arg) {
2512 print_all_options(NULL);
2513 return;
2514 }
2515
2516 while ((arg = skipspace(split_first_word(buf, BUFSIZE, arg))), *buf) {
2517 enum { MODE_ON, MODE_OFF, MODE_TOGGLE, MODE_REP } mode;
2518 char *varp = NULL;
2519 char *p = buf;
2520 char c = *p;
2521 int len = strlen(p);
2522 int i;
2523
2524 switch (c) {
2525 case '=': mode = MODE_REP; p++; break;
2526 case '+': mode = MODE_ON; p++; break;
2527 case '-': mode = MODE_OFF; p++; break;
2528 default: mode = MODE_TOGGLE; break;
2529 }
2530 count++;
2531 for (i = 0; options[i].name; i++) {
2532 if (strncmp(options[i].name, p, len) == 0) {
2533 varp = options[i].option;
2534 break;
2535 }
2536 }
2537 if (varp == NULL) {
2538 if (strncmp("list", p, len) == 0) {
2539 tty_puts("#list of options:\n");
2540 for (i = 0; options[i].name; ++i) {
2541 tty_printf("#option %c%-12s %s\n",
2542 *options[i].option ? '+' : '-',
2543 options[i].name,
2544 options[i].doc);
2545 }
2546 } else {
2547 tty_puts("#syntax: #option [[+|-|=]<name>] | list\n");
2548 }
2549 status(1);
2550 return;
2551 }
2552
2553 switch (mode) {
2554 case MODE_REP:
2555 sprintf(inserted_next, "#option %c%s", *varp ? '+' : '-',
2556 p);
2557 break;
2558 case MODE_ON: *varp = 1; break;
2559 case MODE_OFF: *varp = 0; break;
2560 case MODE_TOGGLE: *varp ^= 1; break;
2561 }
2562 /*
2563 * reset the reprint buffer if changing its status
2564 */
2565 if (varp == &opt_reprint)
2566 reprint_clear();
2567
2568 /* as above, but always print status if
2569 * "#option info" alone was typed */
2570 if (mode != MODE_REP && !*arg && count==1 &&
2571 (opt_info || (mode == MODE_TOGGLE && varp==&opt_info))) {
2572 PRINTF("#option %s is now o%s.\n",
2573 options[i].name,
2574 *varp ? "n" : "ff");
2575 }
2576 }
2577 }
2578
__P1(char *,arg)2579 static void cmd_file __P1 (char *,arg)
2580 {
2581 arg = skipspace(arg);
2582 if (*arg == '=') {
2583 set_deffile(++arg);
2584 if (opt_info) {
2585 if (*arg) {
2586 PRINTF("#save-file set to \"%s\"\n", deffile);
2587 } else {
2588 PRINTF("#save-file is now undefined.\n");
2589 }
2590 }
2591 } else if (*deffile) {
2592 sprintf(inserted_next, "#file =%.*s", BUFSIZE-8, deffile);
2593 } else {
2594 PRINTF("#save-file not defined.\n");
2595 }
2596 }
2597
__P1(char *,arg)2598 static void cmd_save __P1 (char *,arg)
2599 {
2600 arg = skipspace(arg);
2601 if (*arg) {
2602 set_deffile(arg);
2603 if (opt_info) {
2604 PRINTF("#save-file set to \"%s\"\n", deffile);
2605 }
2606 } else if (!*deffile) {
2607 PRINTF("#save-file not defined.\n");
2608 return;
2609 }
2610
2611 if (*deffile && save_settings() > 0 && opt_info) {
2612 PRINTF("#settings saved to file.\n");
2613 }
2614 }
2615
__P1(char *,arg)2616 static void cmd_load __P1 (char *,arg)
2617 {
2618 int res;
2619
2620 arg = skipspace(arg);
2621 if (*arg) {
2622 set_deffile(arg);
2623 if (opt_info) {
2624 PRINTF("#save-file set to \"%s\"\n", deffile);
2625 }
2626 }
2627 else if (!*deffile) {
2628 PRINTF("#save-file not defined.\n");
2629 return;
2630 }
2631
2632 res = read_settings();
2633
2634 if (res > 0) {
2635 /* success */
2636 if (opt_info) {
2637 PRINTF("#settings loaded from file.\n");
2638 }
2639 } else if (res < 0) {
2640 /* critical error */
2641 while (keydefs)
2642 delete_keynode(&keydefs);
2643 tty_add_initial_binds();
2644 tty_add_walk_binds();
2645 limit_mem = 1048576;
2646 PRINTF("#emergency loaded default settings.\n");
2647 }
2648 }
2649
__P3(ptr *,pbuf,char *,arg,char *,name)2650 static char *trivial_eval __P3 (ptr *,pbuf, char *,arg, char *,name)
2651 {
2652 char *tmp = skipspace(arg);
2653
2654 if (!pbuf)
2655 return NULL;
2656
2657 if (*tmp=='(') {
2658 arg = tmp + 1;
2659 (void)evalp(pbuf, &arg);
2660 if (!REAL_ERROR && *arg != ')')
2661 error=MISSING_PAREN_ERROR;
2662 if (REAL_ERROR) {
2663 PRINTF("#%s: ", name);
2664 print_error(error);
2665 return NULL;
2666 }
2667 if (*pbuf)
2668 arg = ptrdata(*pbuf);
2669 else
2670 arg = "";
2671 }
2672 else
2673 unescape(arg);
2674
2675 return arg;
2676 }
2677
__P2(char *,arg,int,is_static)2678 static void do_cmd_add __P2(char *,arg, int,is_static)
2679 {
2680 ptr pbuf = (ptr)0;
2681 char buf[BUFSIZE];
2682
2683 arg = trivial_eval(&pbuf, arg, "add");
2684 if (!REAL_ERROR)
2685 while (*arg) {
2686 arg = split_first_word(buf, BUFSIZE, arg);
2687 if (strlen(buf) >= MIN_WORDLEN)
2688 (is_static ? put_static_word : put_word)(buf);
2689 }
2690 ptrdel(pbuf);
2691 }
2692
__P1(char *,arg)2693 static void cmd_add __P1 (char *,arg)
2694 {
2695 do_cmd_add(arg, 0);
2696 }
2697
__P1(char *,arg)2698 static void cmd_addstatic __P1 (char *,arg)
2699 {
2700 do_cmd_add(arg, 1);
2701 }
2702
__P1(char *,arg)2703 static void cmd_put __P1 (char *,arg)
2704 {
2705 ptr pbuf = (ptr)0;
2706 arg = trivial_eval(&pbuf, arg, "put");
2707 if (!REAL_ERROR && *arg)
2708 put_history(arg);
2709 ptrdel(pbuf);
2710 }
2711
2712
2713