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, &param)))
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