/* * cmd.c -- functions for powwow's built-in #commands * * (created: Finn Arne Gangstad (Ilie), Dec 25th, 1993) * * Copyright (C) 1998 by Massimiliano Ghilardi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBDL #include #endif #include "defines.h" #include "main.h" #include "utils.h" #include "beam.h" #include "cmd.h" #include "cmd2.h" #include "edit.h" #include "list.h" #include "map.h" #include "tcp.h" #include "tty.h" #include "eval.h" #include "log.h" /* local function declarations */ #define F(name) cmd_ ## name(char *arg) static void F(help), F(shell), F(action), F(add), F(addstatic), F(alias), F(at), F(beep), F(bind), F(cancel), F(capture), F(clear), F(connect), F(cpu), F(do), F(delim), F(edit), F(emulate), F(exe), F(file), F(for), F(hilite), F(history), F(host), F(identify), F(if), F(in), F(init), F(isprompt), F(key), F(keyedit), F(load), F(map), F(mark), F(movie), F(net), F(nice), F(option), F(prefix), F(print), F(prompt), F(put), F(qui), F(quit), F(quote), F(rawsend), F(rawprint), F(rebind), F(rebindall), F(rebindALL), F(record), F(request), F(reset), F(retrace), F(save), F(send), F(setvar), F(snoop), F(spawn), F(stop), F(time), F(var), F(ver), F(while), F(write), F(eval), F(zap), F(module), F(group), F(speedwalk), F(groupdelim); #ifdef BUG_TELNET static void F(color); #endif #undef F /* This must be init'd now at runtime */ cmdstruct *commands = NULL; #define C(name, func, help) { NULL, name, help, func, NULL } /* The builtin commands */ cmdstruct default_commands[] = { C("help", cmd_help, "[keys|math|command]\tthis text, or help on specified topic"), C("17", (function_str)0, "command\t\t\trepeat \"command\" 17 times"), #ifndef NO_SHELL C("!", cmd_shell, "shell-command\t\texecute a shell command using /bin/sh"), #endif C("action", cmd_action, "[[<|=|>|%][+|-]name] [{pattern|(expression)} [=[command]]]\n" "\t\t\t\tdelete/list/define actions"), C("add", cmd_add, "{string|(expr)}\t\tadd the string to word completion list"), C("addstatic", cmd_addstatic, "{string|(expr)}\t\tadd the string to the static word completion list"), C("alias", cmd_alias, "[name[=[text]]]\t\tdelete/list/define aliases"), C("at", cmd_at, "[name [(time-string) [command]]\n\t\t\t\tset time of delayed label"), C("beep", cmd_beep, "\t\t\t\tmake your terminal beep (like #print (*7))"), C("bind", cmd_bind, "[edit|name [seq][=[command]]]\n" "\t\t\t\tdelete/list/define key bindings"), C("cancel", cmd_cancel, "[number]\t\tcancel editing session"), C("capture", cmd_capture, "[filename]\t\tbegin/end of capture to file"), C("clear", cmd_clear, "\t\t\tclear input line (use from spawned programs)"), #ifdef BUG_TELNET C("color", cmd_color, "attr\t\t\tset default colors/attributes"), #endif C("connect", cmd_connect, "[connect-id [initstr] [address port]\n" "\t\t\t\topen a new connection"), C("cpu", cmd_cpu, "\t\t\t\tshow CPU time used by powwow"), C("delim", cmd_delim, "[normal|program|{custom [chars]}]\n" "\t\t\t\tset word completion delimeters"), C("do", cmd_do, "(expr) command\t\trepeat \"command\" (expr) times"), C("edit", cmd_edit, "\t\t\t\tlist editing sessions"), C("emulate", cmd_emulate, "[<|!]{text|(expr)}\tprocess result as if received from host"), C("exe", cmd_exe, "[<|!]{text|(string-expr)}\texecute result as if typed from keyboard"), C("file", cmd_file, "[=[filename]]\t\tset/show powwow definition file"), C("for", cmd_for, "([init];check;[loop]) command\n" "\t\t\t\twhile \"check\" is true exec \"command\""), C("group", cmd_group, "[name] [on|off|list]\tgroup alias/action manipulation"), C("groupdelim", cmd_groupdelim, "[delimiter]\tchange delimiter for action/alias groups"), C("hilite", cmd_hilite, "[attr]\t\t\thighlight your input line"), C("history", cmd_history, "[{number|(expr)}]\tlist/execute commands in history"), C("host", cmd_host, "[hostname port]]\tset/show address of default host"), C("identify", cmd_identify, "[startact [endact]]\tsend MUME client identification"), C("if", cmd_if, "(expr) instr1 [; #else instr2]\n" "\t\t\t\tif \"expr\" is true execute \"instr1\",\n" "\t\t\t\totherwise execute \"instr2\""), C("in", cmd_in, "[label [(delay) [command]]]\tdelete/list/define delayed labels"), C("init", cmd_init, "[=[command]]\t\tdefine command to execute on connect to host"), C("isprompt", cmd_isprompt, "\t\t\trecognize a prompt as such"), C("key", cmd_key, "name\t\t\texecute the \"name\" key binding"), C("keyedit", cmd_keyedit, "editing-name\t\trun a line-editing function"), C("load", cmd_load, "[filename]\t\tload powwow settings from file"), C("map", cmd_map, "[-[number]|walksequence]\tshow/clear/edit (auto)map"), C("mark", cmd_mark, "[string[=[attr]]]\t\tdelete/list/define markers"), #ifdef HAVE_LIBDL C("module", cmd_module, "[name]\t\t\tload shared library extension"), #endif C("movie", cmd_movie, "[filename]\t\tbegin/end of movie record to file"), C("net", cmd_net, "\t\t\t\tprint amount of data received from/sent to host"), C("nice", cmd_nice, "[{number|(expr)}[command]]\n" "\t\t\t\tset/show priority of new actions/marks"), C("option", cmd_option, "[[+|-|=]name]|list\tlist or view various options"), C("prefix", cmd_prefix, "string\t\t\tprefix all lines with string"), C("", cmd_eval, "(expr)\t\t\tevaluate expression, trashing result"), C("print", cmd_print, "[<|!][text|(expr)]\tprint text/result on screen, appending a \\n\n" "\t\t\t\tif no argument, prints value of variable $0"), C("prompt", cmd_prompt, "[[<|=|>|%][+|-]name] [{pattern|(expression)} [=[prompt-command]]]\n" "\t\t\t\tdelete/list/define actions on prompts"), C("put", cmd_put, "{text|(expr)}\t\tput text/result of expression in history"), C("qui", cmd_qui, "\t\t\t\tdo nothing"), C("quit", cmd_quit, "\t\t\t\tquit powwow"), C("quote", cmd_quote, "[on|off]\t\t\ttoggle verbatim-flag on/off"), C("rawsend", cmd_rawsend, "{string|(expr)}\tsend raw data to the MUD"), C("rawprint", cmd_rawprint, "{string|(expr)}\tsend raw data to the screen"), C("rebind", cmd_rebind, "name [seq]\t\tchange sequence of a key binding"), C("rebindall", cmd_rebindall, "\t\t\trebind all key bindings"), C("rebindALL", cmd_rebindALL, "\t\t\trebind ALL key bindings, even trivial ones"), C("record", cmd_record, "[filename]\t\tbegin/end of record to file"), C("request", cmd_request, "[editor][prompt][all]\tsend various identification strings"), C("reset", cmd_reset, "\t\tclear the whole defined list and reload default"), C("retrace", cmd_retrace, "[number]\t\tretrace the last number steps"), C("save", cmd_save, "[filename]\t\tsave powwow settings to file"), C("send", cmd_send, "[<|!]{text|(expr)}\teval expression, sending result to the MUD"), C("setvar", cmd_setvar, "name[=text|(expr)]\tset/show internal limits and variables"), C("snoop", cmd_snoop, "connect-id\t\ttoggle output display for connections"), C("spawn", cmd_spawn, "connect-id command\ttalk with a shell command"), C("speedwalk", cmd_speedwalk, "[speedwalk sequence]\texecute a speedwalk sequence explicitly"), C("stop", cmd_stop, "\t\t\t\tremove all delayed commands from active list"), C("time", cmd_time, "\t\t\t\tprint current time and date"), C("var", cmd_var, "variable [= [<|!]{string|(expr)} ]\n" "\t\t\t\twrite result into the variable"), C("ver", cmd_ver, "\t\t\t\tshow powwow version"), C("while", cmd_while, "(expr) instr\t\twhile \"expr\" is true execute \"instr\""), C("write", cmd_write, "[>|!](expr;name)\t\twrite result of expr to \"name\" file"), C("zap", cmd_zap, "connect-id\t\t\tclose a connection"), { NULL } }; char *_cmd_sort_name( cmdstruct *cmd ) { if( cmd -> sortname == NULL ) return( cmd -> name ); else return( cmd -> sortname ); } /* Adds a cmd to commands (inserts the ptr in the list, DO NOT FREE IT) */ void cmd_add_command( cmdstruct *cmd ) { /* do insert/sort */ cmdstruct *c = commands; /* * make sure it doesn't override another commmand * this is important not just because it'd be irritating, * but if a module defined the command in the global static * space, it would create an infinite loop because the -> next * ptr would point at itself * * doing it up front because based on the sortname, we may never see * the dup item if we do it at sort time */ for( c = commands; c != NULL; c = c -> next ) { if( strcmp( cmd -> name, c -> name ) == 0 ) { PRINTF( "#error %s is already defined\n", c -> name ); return; } } /* catch insertion to head of list */ if( commands == NULL ) { /* no commands yet */ commands = cmd; cmd -> next = NULL; return; } if( strcmp( _cmd_sort_name( commands ), _cmd_sort_name( cmd ) ) > 0 ) { /* this is lower in sort than every item, so * make it the head of the list */ cmd -> next = commands; commands = cmd; return; } for( c = commands; c != NULL; c = c -> next ) { if( strcmp( _cmd_sort_name( cmd ), _cmd_sort_name( c ) ) >= 0 ) { /* Need second check to handle empty string case */ if( c -> next == NULL || strcmp( _cmd_sort_name( cmd ), _cmd_sort_name( c -> next ) ) <= 0 ) { /*PRINTF( "Inserting %s after %s\n", cmd -> name, c -> name ); */ /* insert after this one, it is greater than this * entry but less than the next */ cmd -> next = c -> next; c -> next = cmd; return; } } } PRINTF( "ERROR INSERTING COMMAND\n" ); } /* Init the command listing, called from main */ void initialize_cmd(void) { int i; /* Now add the default command list */ for( i = 0; default_commands[ i ].name; i++ ) cmd_add_command( &default_commands[ i ] ); } #ifdef HAVE_LIBDL static void cmd_module(char *arg) { char libname[1024]; void *lib; void (*func)(); int pindex; struct stat junk; char *prefixes[] = { PLUGIN_DIR, ".", "/lib/powwow", "/usr/lib/powwow", "/usr/local/lib/powwow", "$HOME/.powwow/lib" /* this doesn't work, but is here to remind me :p */ }; arg = skipspace(arg); /* I changed it to work this way so that you can have libs in multiple places and * also eventually to allow it to use .dll instead of .so under the cygwin environment */ for( pindex = 0; pindex < 5; pindex++ ) { memset( libname, 0, sizeof libname ); /* don't look for name without .so, it breaks if you have a file * with the same name in the current dir and making it .so for sure * will skip these files since they are probably not libs to load snprintf( libname, 1024, "%s/%s", prefixes[ pindex ], arg ); if( stat( libname, &junk ) == 0 ) { break; } */ snprintf( libname, 1024, "%s/%s.so", prefixes[ pindex ], arg ); if( stat( libname, &junk ) == 0 ) { break; } } /* open lib */ lib = dlopen( libname, RTLD_GLOBAL | RTLD_LAZY ); if( ! lib ) { PRINTF( "#module error: %s\n", dlerror() ); return; }else{ PRINTF( "#module loaded %s\n", libname ); } func = dlsym( lib, "powwow_init" ); if( func ) { (*func)(); }else{ PRINTF( "#module error: %s\n", dlerror() ); } } #endif static void cmd_group(char *arg) { char *group; int active; aliasnode *p; actionnode *a; arg = skipspace(arg); if( *arg ) { arg = first_regular( group = arg, ' '); *arg = '\0'; arg = skipspace( arg + 1 ); if( strcmp( arg, "on" ) == 0 ) { active = 1; }else if( strcmp( arg, "off" ) == 0 ) { active = 0; }else if( strcmp( arg, "list" ) == 0 ) { PRINTF( "#not implemented\n" ); return; }else{ PRINTF( "#unknown group command, use off/on/list\n" ); return; } /* Now loop over all aliases/actions by groupname and toggle */ for( p = sortedaliases; p; p = p -> snext ) { if( p -> group && strcmp( p -> group, group ) == 0 ) { p -> active = active; } } /* Same for actions */ for( a = actions; a; a = a -> next ) { if( a -> group && strcmp( a -> group, group ) == 0 ) { a -> active = active; } } }else{ PRINTF( "#group name required\n" ); } } static void cmd_groupdelim(char *arg) { if( *arg != 0 ) { free( group_delim ); group_delim = my_strdup( arg ); PRINTF( "#group delimiter is now '%s'\n", group_delim ); } } static void cmd_help(char *arg) { int i, size; char *text, *tmp; FILE *f; char line[BUFSIZE]; int len; cmdstruct *c; arg = skipspace(arg); if (*arg == '#') arg++; if (!*arg) { size = 25; for( c = commands; c != NULL; c = c -> next ) size += strlen(c -> name) + strlen(c -> help) + 5; text = tmp = (char *)malloc(size); if (!text) { errmsg("malloc"); return; } /* do not use sprintf() return value, almost every OS returns a different thing. */ sprintf(tmp, "#help\n#commands available:\n"); tmp += strlen(tmp); for( c = commands; c != NULL; c = c -> next ) { sprintf(tmp, "#%s %s\n", c -> name, c -> help); tmp += strlen(tmp); } message_edit(text, strlen(text), 1, 1); return; } if (!strncmp(arg, "copyright", strlen(arg))) { int fd, left, got = 0; struct stat stbuf; if (stat(copyfile, &stbuf) < 0) { errmsg("stat(copyright file)"); return; } if (!(text = (char *)malloc(left = stbuf.st_size))) { errmsg("malloc"); return; } if ((fd = open(copyfile, O_RDONLY)) < 0) { errmsg("open(copyright file)"); free(text); return; } while (left > 0) { while ((i = read(fd, text + got, left)) < 0 && errno == EINTR) ; if (i < 0 && errno == EINTR) { errmsg("read (copyright file)"); free(text); close(fd); return; } if (i == 0) break; left -= i, got += i; } close(fd); message_edit(text, strlen(text), 1, 1); return; } /* !copyright */ f = fopen(helpfile, "r"); if (!f) { PRINTF("#cannot open help file \"%s\": %s\n", helpfile, strerror(errno)); return; } while ((tmp = fgets(line, BUFSIZE, f)) && (line[0] != '@' || strncmp(line + 1, arg, strlen(arg)))) ; if (!tmp) { PRINTF("#no entry for \"%s\" in the help file.\n", arg); fclose(f); return; } if (!(text = (char *)malloc(size = BUFSIZE))) { errmsg("malloc"); fclose(f); return; } /* the first line becomes $TITLE */ tmp = strchr(line, '\n'); if (tmp) *tmp = '\0'; i = sprintf(text, "Help on '%s'\n", line + 1); /* allow multiple commands to share the same help */ while (fgets(line, BUFSIZE, f) && line[0] == '@') ; do { if ((len = strlen(line)) >= size - i) { /* Not enough space in current buffer */ if (!(tmp = (char *)malloc(size += BUFSIZE))) { errmsg("malloc"); free(text); fclose(f); return; } else { memcpy(tmp, text, i); free(text); text = tmp; } } memcpy(text + i, line, len); i += len; } while (fgets(line, BUFSIZE, f) && line[0] != '@'); fclose(f); text[i] = '\0'; /* safe, there is space */ message_edit(text, strlen(text), 1, 1); } static void cmd_clear(char *arg) { if (line_status == 0) { clear_input_line(opt_compact); if (!opt_compact) { tty_putc('\n'); col0 = 0; } status(1); } } #ifndef NO_SHELL static void cmd_shell(char *arg) { if (!*arg) { if (opt_info) { PRINTF("#that's easy.\n"); } } else { tty_quit(); if (system(arg) == -1) { perror("system()"); } tty_start(); tty_gotoxy(col0 = 0, line0 = lines -1); tty_puts(tty_clreoln); } } #endif static void cmd_alias(char *arg) { arg = skipspace(arg); if (!*arg) show_aliases(); else parse_alias(arg); } static void cmd_action(char *arg) { arg = skipspace(arg); if (!*arg) show_actions(); else parse_action(arg, 0); } static void cmd_prompt(char *arg) { arg = skipspace(arg); if (!*arg) show_prompts(); else parse_action(arg, 1); } static void cmd_beep(char *arg) { tty_putc('\007'); } /* * create/list/edit/delete bindings */ static void cmd_bind(char *arg) { arg = skipspace(arg); if (!*arg) show_binds(0); else if (!strcmp(arg, "edit")) show_binds(1); else parse_bind(arg); } static void cmd_delim(char *arg) { char buf[BUFSIZE]; int n; arg = skipspace(arg); if (!*arg) { PRINTF("#delim: \"%s\" (%s)\n", delim_name[delim_mode], DELIM); return; } arg = split_first_word(buf, BUFSIZE, arg); n = 0; while (n < DELIM_MODES && strncmp(delim_name[n], buf, strlen(buf)) != 0) n++; if (n >= DELIM_MODES) { PRINTF("#delim [normal|program|{custom }\n"); return; } if (n == DELIM_CUSTOM) { if (!strchr(arg, ' ')) { my_strncpy(buf+1, arg, BUFSIZE-2); *buf = ' '; /* force ' ' in the delims */ arg = buf; } unescape(arg); set_custom_delimeters(arg); } else delim_mode = n; } static void cmd_do(char *arg) { int type; long result; arg = skipspace(arg); if (*arg != '(') { PRINTF("#do: "); print_error(error=MISMATCH_PAREN_ERROR); return; } arg++; type = evall(&result, &arg); if (REAL_ERROR) return; if (type != TYPE_NUM) { PRINTF("#do: "); print_error(error=NO_NUM_VALUE_ERROR); return; } if (*arg == ')') { /* skip the ')' */ if (*++arg == ' ') arg++; } else { PRINTF("#do: "); print_error(error=MISSING_PAREN_ERROR); return; } if (result >= 0) while (!error && result--) (void)parse_instruction(arg, 1, 0, 1); else { PRINTF("#do: bogus repeat count \"%ld\"\n", result); } } static void cmd_hilite(char *arg) { int attr; arg = skipspace(arg); attr = parse_attributes(arg); if (attr == -1) { PRINTF("#attribute syntax error.\n"); if (opt_info) show_attr_syntax(); } else { attr_string(attr, edattrbeg, edattrend); edattrbg = ATTR(attr) & ATTR_INVERSE ? 1 : BACKGROUND(attr) != NO_COLOR || ATTR(attr) & ATTR_BLINK; if (opt_info) { PRINTF("#input highlighting is now %so%s%s.\n", edattrbeg, (attr == NOATTRCODE) ? "ff" : "n", edattrend); } } } static void cmd_history(char *arg) { int num = 0; long buf; arg = skipspace(arg); if (history_done >= MAX_HIST) { print_error(error=HISTORY_RECURSION_ERROR); return; } history_done++; if (*arg == '(') { arg++; num = evall(&buf, &arg); if (!REAL_ERROR && num != TYPE_NUM) error=NO_NUM_VALUE_ERROR; if (REAL_ERROR) { PRINTF("#history: "); print_error(error=NO_NUM_VALUE_ERROR); return; } num = (int)buf; } else num = atoi(arg); if (num > 0) exe_history(num); else show_history(-num); } static void cmd_host(char *arg) { char newhost[BUFSIZE]; arg = skipspace(arg); if (*arg) { arg = split_first_word(newhost, BUFSIZE, arg); if (*arg) { my_strncpy(hostname, newhost, BUFSIZE-1); portnumber = atoi(arg); if (opt_info) { PRINTF("#host set to: %s %d\n", hostname, portnumber); } } else { PRINTF("#host: missing portnumber.\n"); } } else if (*hostname) sprintf(inserted_next, "#host %.*s %d", BUFSIZE-INTLEN-8, hostname, portnumber); else { PRINTF("#syntax: #host hostname port\n"); } } static void cmd_request(char *arg) { char *idprompt = "~$#EP2\nG\n"; char *ideditor = "~$#EI\n"; char buf[256]; int all, len; if (tcp_fd == -1) { PRINTF("#not connected to a MUD!\n"); return; } while (*(arg = skipspace(arg))) { arg = split_first_word(buf, 256, arg); if (*buf) { all = !strcmp(buf, "all"); len = strlen(buf); if ((all || !strncmp(buf, "editor", len))) { tcp_raw_write(tcp_fd, ideditor, strlen(ideditor)); CONN_LIST(tcp_fd).flags |= IDEDITOR; if (opt_info) { PRINTF("#request editor: %s done!\n", ideditor); } } if ((all || !strncmp(buf, "prompt", len))) { tcp_raw_write(tcp_fd, idprompt, strlen(idprompt)); CONN_LIST(tcp_fd).flags |= IDPROMPT; if (opt_info) { PRINTF("#request prompt: %s done!\n", idprompt); } } } } } static void cmd_identify(char *arg) { edit_start[0] = edit_end[0] = '\0'; if (*arg) { char *p = strchr(arg, ' '); if (p) { *(p++) = '\0'; my_strncpy(edit_end, p, BUFSIZE-1); } my_strncpy(edit_start, arg, BUFSIZE-1); } cmd_request("editor"); } static void cmd_in(char *arg) { char *name; long millisec, buf; int type; delaynode **p; arg = skipspace(arg); if (!*arg) { show_delays(); return; } arg = first_regular(name = arg, ' '); if (*arg) *arg++ = 0; unescape(name); p = lookup_delay(name, 0); if (!*p) p = lookup_delay(name, 1); if (!*arg && !*p) { PRINTF("#unknown delay label, cannot show: \"%s\"\n", name); return; } if (!*arg) { show_delaynode(*p, 1); return; } if (*arg != '(') { PRINTF("#in: "); print_error(error=MISMATCH_PAREN_ERROR); return; } arg++; /* skip the '(' */ type = evall(&buf, &arg); if (!REAL_ERROR) { if (type!=TYPE_NUM) error=NO_NUM_VALUE_ERROR; else if (*arg != ')') error=MISSING_PAREN_ERROR; } if (REAL_ERROR) { PRINTF("#in: "); print_error(error); return; } arg = skipspace(arg+1); millisec = buf; if (*p && millisec) change_delaynode(p, arg, millisec); else if (!*p && millisec) { if (*arg) new_delaynode(name, arg, millisec); else { PRINTF("#cannot create delay label without a command.\n"); } } else if (*p && !millisec) { if (opt_info) { PRINTF("#deleting delay label: %s %s\n", name, (*p)->command); } delete_delaynode(p); } else { PRINTF("#unknown delay label, cannot delete: \"%s\"\n", name); } } static void cmd_at(char *arg) { char *name, *buf = NULL; char dayflag=0; struct tm *twhen; int num, hour = -1, minute = -1, second = -1; delaynode **p; long millisec; ptr pbuf = (ptr)0; arg = skipspace(arg); if (!*arg) { show_delays(); return; } arg = first_regular(name = arg, ' '); if (*arg) *arg++ = 0; unescape(name); p = lookup_delay(name, 0); if (!*p) p = lookup_delay(name, 1); if (!*arg && !*p) { PRINTF("#unknown delay label, cannot show: \"%s\"\n", name); return; } if (!*arg) { show_delaynode(*p, 2); return; } if (*arg != '(') { PRINTF("#in: "); print_error(error=MISMATCH_PAREN_ERROR); return; } arg++; /* skip the '(' */ (void)evalp(&pbuf, &arg); if (REAL_ERROR) { print_error(error); ptrdel(pbuf); return; } if (pbuf) { /* convert time-string into hour, minute, second */ buf = skipspace(ptrdata(pbuf)); if (!*buf || !isdigit(*buf)) { PRINTF("#at: "); print_error(error=NO_NUM_VALUE_ERROR); ptrdel(pbuf); return; } num = atoi(buf); second = num % 100; minute = (num /= 100) % 100; hour = num / 100; } if (hour < 0 || hour>23 || minute < 0 || minute>59 || second < 0 || second>59) { PRINTF("#at: #error: invalid time \"%s\"\n", pbuf && buf ? buf : (char *)""); error=OUT_RANGE_ERROR; ptrdel(pbuf); return; } ptrdel(pbuf); if (*arg == ')') { /* skip the ')' */ if (*++arg == ' ') arg++; } else { PRINTF("#at: "); print_error(error=MISSING_PAREN_ERROR); return; } arg = skipspace(arg); update_now(); twhen = localtime((time_t *)&now.tv_sec); /* put current year, month, day in calendar struct */ if (hour < twhen->tm_hour || (hour == twhen->tm_hour && (minute < twhen->tm_min || (minute == twhen->tm_min && second <= twhen->tm_sec)))) { dayflag = 1; /* it is NOT possible to define an #at refering to the past */ } /* if you use a time smaller than the current, it refers to tomorrow */ millisec = (hour - twhen->tm_hour) * 3600 + (minute - twhen->tm_min) * 60 + second - twhen->tm_sec + (dayflag ? 24*60*60 : 0); millisec *= mSEC_PER_SEC; /* Comparing time with current calendar, we finally got the delay */ millisec -= now.tv_usec / uSEC_PER_mSEC; if (*p) change_delaynode(p, arg, millisec); else if (*arg) new_delaynode(name, arg, millisec); else { PRINTF("#cannot create delay label without a command.\n"); } } static void cmd_init(char *arg) { arg = skipspace(arg); if (*arg == '=') { if (*++arg) { my_strncpy(initstr, arg, BUFSIZE-1); if (opt_info) { PRINTF("#init: %s\n", initstr); } } else { *initstr = '\0'; if (opt_info) { PRINTF("#init cleared.\n"); } } } else sprintf(inserted_next, "#init =%.*s", BUFSIZE-8, initstr); } static void cmd_isprompt(char *arg) { if (tcp_fd == tcp_main_fd) { int i; long l; arg = skipspace(arg); if (*arg == '(') { arg++; i = evall(&l, &arg); if (!REAL_ERROR) { if (i!=TYPE_NUM) error=NO_NUM_VALUE_ERROR; else if (*arg != ')') error=MISSING_PAREN_ERROR; } if (REAL_ERROR) { PRINTF("#isprompt: "); print_error(error); return; } i = (int)l; } else i = atoi(arg); if (i == 0) surely_isprompt = -1; else if (i < 0) { if (i > -NUMPARAM && *VAR[-i].str) ptrtrunc(prompt->str, surely_isprompt = ptrlen(*VAR[-i].str)); } else ptrtrunc(prompt->str, surely_isprompt = i); } } static void cmd_key(char *arg) { keynode *q=NULL; arg = skipspace(arg); if (!*arg) return; if ((q = *lookup_key(arg))) q->funct(q->call_data); else { PRINTF("#no such key: \"%s\"\n", arg); } } static void cmd_keyedit(char *arg) { int function; char *param; arg = skipspace(arg); if (!*arg) return; if ((function = lookup_edit_name(arg, ¶m))) internal_functions[function].funct(param); else { PRINTF("#no such editing function: \"%s\"\n", arg); } } static void cmd_map(char *arg) { arg = skipspace(arg); if (!*arg) /* show map */ map_show(); else if (*arg == '-') /* retrace steps without walking */ map_retrace(atoi(arg + 1), 0); else map_walk(arg, 1, 1); } static void cmd_retrace(char *arg) { map_retrace(atoi(arg), 1); } static void cmd_mark(char *arg) { if (!*arg) show_marks(); else parse_mark(arg); } static void cmd_nice(char *arg) { int nnice = a_nice; arg = skipspace(arg); if (!*arg) { PRINTF("#nice: %d\n", a_nice); return; } if (isdigit(*arg)) { a_nice = 0; while (isdigit(*arg)) { a_nice *= 10; a_nice += *arg++ - '0'; } } else if (*arg++=='(') { long buf; int type; type = evall(&buf, &arg); if (!REAL_ERROR && type!=TYPE_NUM) error=NO_NUM_VALUE_ERROR; else if (!REAL_ERROR && *arg++ != ')') error=MISSING_PAREN_ERROR; if (REAL_ERROR) { PRINTF("#nice: "); print_error(error); return; } a_nice = (int)buf; if (a_nice<0) a_nice = 0; } arg = skipspace(arg); if (*arg) { parse_instruction(arg, 0, 0, 1); a_nice = nnice; } } static void cmd_prefix(char *arg) { strcpy(prefixstr, arg); if (opt_info) { PRINTF("#prefix %s.\n", *arg ? "set" : "cleared"); } } static void cmd_quote(char *arg) { arg = skipspace(arg); if (!*arg) verbatim ^= 1; else if (!strcmp(arg, "on")) verbatim = 1; else if (!strcmp(arg, "off")) verbatim = 0; if (opt_info) { PRINTF("#%s mode.\n", verbatim ? "verbatim" : "normal"); } } /* * change the escape sequence of an existing binding */ static void cmd_rebind(char *arg) { parse_rebind(arg); } static void cmd_rebindall(char *arg) { keynode *kp; char *seq; for (kp = keydefs; kp; kp = kp->next) { seq = kp->sequence; if (kp->seqlen == 1 && seq[0] < ' ') ; else if (kp->seqlen == 2 && seq[0] == '\033' && isalnum(seq[1])) ; else { parse_rebind(kp->name); if (error) break; } } } static void cmd_rebindALL(char *arg) { keynode *kp; for (kp = keydefs; kp; kp = kp->next) { parse_rebind(kp->name); if (error) break; } } static void cmd_reset(char *arg) { char all = 0; arg = skipspace(arg); if (!*arg) { PRINTF("#reset: must specify one of:\n"); tty_puts(" alias, action, bind, in (or at), mark, prompt, var, all.\n"); return; } all = !strcmp(arg, "all"); if (all || !strcmp(arg, "alias")) { int n; for (n = 0; n < MAX_HASH; n++) { while (aliases[n]) delete_aliasnode(&aliases[n]); } if (!all) return; } if (all || !strcmp(arg, "action")) { while (actions) delete_actionnode(&actions); if (!all) return; } if (all || !strcmp(arg, "bind")) { while (keydefs) delete_keynode(&keydefs); tty_add_initial_binds(); tty_add_walk_binds(); if (!all) return; } if (all || !strcmp(arg, "in") || !strcmp(arg, "at")) { while (delays) delete_delaynode(&delays); while (dead_delays) delete_delaynode(&dead_delays); if (!all) return; } if (all || !strcmp(arg, "mark")) { while (markers) delete_marknode(&markers); if (!all) return; } if (all || !strcmp(arg, "prompt")) { while (prompts) delete_promptnode(&prompts); if (!all) return; } if (all || !strcmp(arg, "var")) { int n; varnode **first; for (n = 0; n < MAX_HASH; n++) { while (named_vars[0][n]) delete_varnode(&named_vars[0][n], 0); first = &named_vars[1][n]; while (*first) { if (is_permanent_variable(*first)) first = &(*first)->next; else delete_varnode(first, 1); } } for (n = 0; n < NUMVAR; n++) { *var[n].num = 0; ptrdel(*var[n].str); *var[n].str = NULL; } if (!all) return; } } static void cmd_snoop(char *arg) { if (!*arg) { PRINTF("#snoop: which connection?\n"); } else tcp_togglesnoop(arg); } static void cmd_stop(char *arg) { delaynode *dying; if (delays) update_now(); while (delays) { dying = delays; delays = dying->next; dying->when.tv_sec = now.tv_sec; dying->when.tv_usec = now.tv_usec; dying->next = dead_delays; dead_delays = dying; } if (opt_info) { PRINTF("#all delayed labels are now disabled.\n"); } } static void cmd_time(char *arg) { struct tm *s; char buf[BUFSIZE]; update_now(); s = localtime((time_t *)&now.tv_sec); (void)strftime(buf, BUFSIZE - 1, "%a, %d %b %Y %H:%M:%S", s); PRINTF("#current time is %s\n", buf); } static void cmd_ver(char *arg) { printver(); } static void cmd_emulate(char *arg) { char kind; FILE *fp; long start, end, i = 1; int len; ptr pbuf = (ptr)0; arg = redirect(arg, &pbuf, &kind, "emulate", 0, &start, &end); if (REAL_ERROR || !arg) return; if (kind) { char buf[BUFSIZE]; fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); if (!fp) { PRINTF("#emulate: #error opening \"%s\"\n", arg); print_error(error=SYNTAX_ERROR); ptrdel(pbuf); return; } status(-1); /* we're pretending we got something from the MUD */ while (!error && (!start || i<=end) && fgets(buf, BUFSIZE, fp)) if (!start || i++>=start) process_remote_input(buf, strlen(buf)); if (kind == '!') pclose(fp); else fclose(fp); } else { status(-1); /* idem */ /* WARNING: no guarantee there is space! */ arg[len = strlen(arg)] = '\n'; arg[++len] = '\0'; process_remote_input(arg, len); } ptrdel(pbuf); } static void cmd_eval(char *arg) { arg = skipspace(arg); if (*arg=='(') { arg++; (void)evaln(&arg); if (*arg != ')') { PRINTF("#(): "); print_error(error=MISSING_PAREN_ERROR); } } else { PRINTF("#(): "); print_error(error=MISMATCH_PAREN_ERROR); } } static void cmd_exe(char *arg) { char kind; char *clear; long offset, start, end, i = 1; FILE *fp; ptr pbuf = (ptr)0; arg = redirect(arg, &pbuf, &kind, "exe", 0, &start, &end); if (REAL_ERROR || !arg) return; if (kind) { char buf[BUFSIZE]; fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); if (!fp) { PRINTF("#exe: #error opening \"%s\"\n", arg); error = SYNTAX_ERROR; ptrdel(pbuf); return; } offset = 0; /* We may go in to a loop if a single function is more than 4k, but if that's * the case then maybe you should break it down a little bit :p */ while (!error && (!start || i<=end) && fgets(buf + offset, BUFSIZE - offset, fp)) /* If it ends with \\\n then it's a line continuation, so clear * the \\\n and do another fgets */ if (buf[offset + strlen(buf + offset) - 2] == '\\') { /* Clear \n prefixed with a literal backslash '\\' */ if ((clear = strstr(buf + offset, "\\\n"))) *clear = '\0'; offset += strlen(buf + offset); } else { if (!start || i++ >= start) { buf[strlen(buf)-1] = '\0'; parse_user_input(buf, 0); offset = 0; } } if (kind == '!') pclose(fp); else fclose(fp); } else parse_user_input(arg, 0); ptrdel(pbuf); } static void cmd_print(char *arg) { char kind; long start, end, i = 1; FILE *fp; ptr pbuf = (ptr)0; clear_input_line(opt_compact); if (!*arg) { smart_print(*VAR[0].str ? ptrdata(*VAR[0].str) : (char *)"", 1); return; } arg = redirect(arg, &pbuf, &kind, "print", 1, &start, &end); if (REAL_ERROR || !arg) return; if (kind) { char buf[BUFSIZE]; fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); if (!fp) { PRINTF("#print: #error opening \"%s\"\n", arg); error=SYNTAX_ERROR; ptrdel(pbuf); return; } while (!error && (!start || i <= end) && fgets(buf, BUFSIZE, fp)) if (!start || i++>=start) tty_puts(buf); tty_putc('\n'); if (kind == '!') pclose(fp); else fclose(fp); } else smart_print(arg, 1); ptrdel(pbuf); } static void cmd_send(char *arg) { char *newline, kind; long start, end, i = 1; FILE *fp; ptr pbuf = (ptr)0; arg = redirect(arg, &pbuf, &kind, "send", 0, &start, &end); if (REAL_ERROR ||!arg) return; if (kind) { char buf[BUFSIZE]; fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); if (!fp) { PRINTF("#send: #error opening \"%s\"\n", arg); error = SYNTAX_ERROR; ptrdel(pbuf); return; } while (!error && (!start || i<=end) && fgets(buf, BUFSIZE, fp)) { if ((newline = strchr(buf, '\n'))) *newline = '\0'; if (!start || i++>=start) { if (opt_echo) { PRINTF("[%s]\n", buf); } tcp_write(tcp_fd, buf); } } if (kind == '!') pclose(fp); else fclose(fp); } else { if (opt_echo) { PRINTF("[%s]\n", arg); } tcp_write(tcp_fd, arg); } ptrdel(pbuf); } static void cmd_rawsend(char *arg) { char *tmp = skipspace(arg); if (*tmp=='(') { ptr pbuf = (ptr)0; arg = tmp + 1; (void)evalp(&pbuf, &arg); if (REAL_ERROR) { print_error(error); ptrdel(pbuf); } else if (pbuf) tcp_raw_write(tcp_fd, ptrdata(pbuf), ptrlen(pbuf)); } else { int len; if ((len = memunescape(arg, strlen(arg)))) tcp_raw_write(tcp_fd, arg, len); } } static void cmd_rawprint(char *arg) { char *tmp = skipspace(arg); if (*tmp=='(') { ptr pbuf = (ptr)0; arg = tmp + 1; (void)evalp(&pbuf, &arg); if (REAL_ERROR) { print_error(error); ptrdel(pbuf); } else if (pbuf) tty_raw_write(ptrdata(pbuf), ptrlen(pbuf)); } else { int len; if ((len = memunescape(arg, strlen(arg)))) tty_raw_write(arg, len); } } static void cmd_write(char *arg) { ptr p1 = (ptr)0, p2 = (ptr)0; char *tmp = skipspace(arg), kind; FILE *fp; kind = *tmp; if (kind == '!' || kind == '>') arg = ++tmp; else kind = 0; if (*tmp=='(') { arg = tmp + 1; (void)evalp(&p1, &arg); if (REAL_ERROR) goto write_cleanup; if (*arg == CMDSEP) { arg++; (void)evalp(&p2, &arg); if (!REAL_ERROR && !p2) error = NO_STRING_ERROR; if (REAL_ERROR) goto write_cleanup; } else { PRINTF("#write: "); error=SYNTAX_ERROR; goto write_cleanup; } if (*arg != ')') { PRINTF("#write: "); error=MISSING_PAREN_ERROR; goto write_cleanup; } arg = ptrdata(p2); fp = (kind == '!') ? popen(arg, "w") : fopen(arg, kind ? "w" : "a"); if (!fp) { PRINTF("#write: #error opening \"%s\"\n", arg); error=SYNTAX_ERROR; goto write_cleanup2; } fprintf(fp, "%s\n", p1 ? ptrdata(p1) : (char *)""); fflush(fp); if (kind == '!') pclose(fp); else fclose(fp); } else { PRINTF("#write: "); error=MISMATCH_PAREN_ERROR; } write_cleanup: if (REAL_ERROR) print_error(error); write_cleanup2: ptrdel(p1); ptrdel(p2); } static void cmd_var(char *arg) { char *buf, *expr, *tmp, kind, type, right = 0, deleting = 0; varnode **p_named_var = NULL, *named_var = NULL; FILE *fp; long start, end, i = 1; int len, idx; ptr pbuf = (ptr)0; arg = skipspace(arg); expr = first_regular(arg, '='); if (*expr) { *expr++ = '\0'; /* skip the = */ if (!*expr) deleting = 1; else { right = 1; if (*expr == ' ') expr++; } } if (*arg == '$') type = TYPE_TXT_VAR; else if (*arg == '@') type = TYPE_NUM_VAR; else if (*arg) { print_error(error=INVALID_NAME_ERROR); return; } else { show_vars(); return; } kind = *++arg; if (isalpha(kind) || kind == '_') { /* found a named variable */ tmp = arg; while (*tmp && (isalnum(*tmp) || *tmp == '_')) tmp++; if (*tmp) { print_error(error=INVALID_NAME_ERROR); return; } kind = type==TYPE_TXT_VAR ? 1 : 0; p_named_var = lookup_varnode(arg, kind); if (!*p_named_var) { /* it doesn't (yet) exist */ if (!deleting) { /* so create it */ named_var = add_varnode(arg, kind); if (REAL_ERROR) return; if (opt_info) { PRINTF("#new variable: \"%s\"\n", arg - 1); } } else { print_error(error=UNDEFINED_VARIABLE_ERROR); return; } } else /* it exists, hold on */ named_var = *p_named_var; idx = named_var->index; } else { /* not a named variable, may be * an unnamed variable or an expression */ kind = type==TYPE_TXT_VAR ? 1 : 0; tmp = skipspace(arg); if (*tmp == '(') { /* an expression */ arg = tmp+1; idx = evalp(&pbuf, &arg); if (!REAL_ERROR && idx != TYPE_TXT) error=NO_STRING_ERROR; if (REAL_ERROR) { PRINTF("#var: "); print_error(error); ptrdel(pbuf); return; } if (pbuf) buf = ptrdata(pbuf); else { print_error(error=INVALID_NAME_ERROR); return; } char err_det; if (isdigit(*buf) || *buf=='-' || *buf=='+') { if (sscanf(buf, "%d%c", &idx, &err_det)==1) { if (idx < -NUMVAR || idx >= NUMPARAM) { print_error(error=OUT_RANGE_ERROR); ptrdel(pbuf); return; } } else { print_error(error=INVALID_NAME_ERROR); return; } } else { if (!isalpha(*buf) && *buf!='_') { print_error(error=INVALID_NAME_ERROR); return; } tmp = buf + 1; while (*tmp && (isalnum(*tmp) || *tmp=='_')) tmp++; if (*tmp) { print_error(error=INVALID_NAME_ERROR); return; } if (!(named_var = *(p_named_var = lookup_varnode(buf, kind)))) { if (!deleting) { named_var = add_varnode(buf, kind); if (REAL_ERROR) { print_error(error); ptrdel(pbuf); return; } if (opt_info) { PRINTF("#new variable: %c%s\n", kind ? '$' : '@', buf); } } else { print_error(error=UNDEFINED_VARIABLE_ERROR); ptrdel(pbuf); return; } } idx = named_var->index; } } else { /* an unnamed var */ long buf2; idx = evall(&buf2, &arg); if (!REAL_ERROR && idx != TYPE_NUM) error=NO_STRING_ERROR; if (REAL_ERROR) { PRINTF("#var: "); print_error(error); return; } idx = (int)buf2; if (idx < -NUMVAR || idx >= NUMPARAM) { print_error(error=OUT_RANGE_ERROR); return; } /* ok, it's an unnamed var */ } } if (type == TYPE_TXT_VAR && right && !*VAR[idx].str) { /* create it */ *VAR[idx].str = ptrnew(PARAMLEN); if (MEM_ERROR) { print_error(error); ptrdel(pbuf); return; } } if (deleting) { /* R.I.P. named variables */ if (named_var) { if (is_permanent_variable(named_var)) { PRINTF("#cannot delete variable: \"%s\"\n", arg - 1); } else { delete_varnode(p_named_var, kind); if (opt_info) { PRINTF("#deleted variable: \"%s\"\n", arg - 1); } } } else if ((type = TYPE_TXT_VAR)) { /* R.I.P. unnamed variables */ if (*VAR[idx].str) { if (idx < 0) { ptrdel(*VAR[idx].str); *VAR[idx].str = 0; } else ptrzero(*VAR[idx].str); } } else *VAR[idx].num = 0; ptrdel(pbuf); return; } else if (!right) { /* no right-hand expression, just show */ if (named_var) { if (type == TYPE_TXT_VAR) { pbuf = ptrescape(pbuf, *VAR[idx].str, 0); if (REAL_ERROR) { print_error(error); ptrdel(pbuf); return; } sprintf(inserted_next, "#($%.*s = \"%.*s\")", BUFSIZE - 10, named_var->name, BUFSIZE - (int)strlen(named_var->name) - 10, pbuf ? ptrdata(pbuf) : (char *)""); } else { sprintf(inserted_next, "#(@%.*s = %ld)", BUFSIZE - 8, named_var->name, *VAR[idx].num); } } else { if (type == TYPE_TXT_VAR) { pbuf = ptrescape(pbuf, *VAR[idx].str, 0); sprintf(inserted_next, "#($%d = \"%.*s\")", idx, BUFSIZE - INTLEN - 10, pbuf ? ptrdata(pbuf) : (char *)""); } else { sprintf(inserted_next, "#(@%d = %ld)", idx, *VAR[idx].num); } } ptrdel(pbuf); return; } /* only case left: assign a value to a variable */ arg = redirect(expr, &pbuf, &kind, "var", 1, &start, &end); if (REAL_ERROR || !arg) return; if (kind) { char buf2[BUFSIZE]; fp = (kind == '!') ? popen(arg, "r") : fopen(arg, "r"); if (!fp) { PRINTF("#var: #error opening \"%s\"\n", arg); error=SYNTAX_ERROR; ptrdel(pbuf); return; } len = 0; i = 1; while (!error && (!start || i<=end) && fgets(buf2+len, BUFSIZE-len, fp)) if (!start || i++>=start) len += strlen(buf2 + len); if (kind == '!') pclose(fp); else fclose(fp); if (len>PARAMLEN) len = PARAMLEN; buf2[len] = '\0'; arg = buf2; } if (type == TYPE_NUM_VAR) { arg = skipspace(arg); type = 1; len = 0; if (*arg == '-') arg++, type = -1; else if (*arg == '+') arg++; if (isdigit(kind=*arg)) while (isdigit(kind)) { len*=10; len+=(kind-'0'); kind=*++arg; } else { PRINTF("#var: "); print_error(error=NO_NUM_VALUE_ERROR); } *VAR[idx].num = len * type; } else { *VAR[idx].str = ptrmcpy(*VAR[idx].str, arg, strlen(arg)); if (MEM_ERROR) print_error(error); } ptrdel(pbuf); } static void cmd_setvar(char *arg) { char *name; int i, func = 0; /* show */ long buf; name = arg = skipspace(arg); arg = first_regular(arg, '='); if (*arg) { *arg++ = '\0'; if (*arg) { func = 1; /* set */ if (*arg == '(') { arg++; i = evall(&buf, &arg); if (!REAL_ERROR && i != TYPE_NUM) error=NO_NUM_VALUE_ERROR; else if (!REAL_ERROR && *arg != ')') error=MISSING_PAREN_ERROR; } else buf = strtol(arg, NULL, 0); } else buf = 0; if (REAL_ERROR) { PRINTF("#setvar: "); print_error(error); return; } } i = strlen(name); if (i && !strncmp(name, "timer", i)) { vtime t; update_now(); if (func == 0) sprintf(inserted_next, "#setvar timer=%ld", diff_vtime(&now, &ref_time)); else { t.tv_usec = ((-buf) % mSEC_PER_SEC) * uSEC_PER_mSEC; t.tv_sec = (-buf) / mSEC_PER_SEC; ref_time.tv_usec = now.tv_usec; ref_time.tv_sec = now.tv_sec; add_vtime(&ref_time, &t); } } else if (i && !strncmp(name, "lines", i)) { if (func == 0) sprintf(inserted_next, "#setvar lines=%d", lines); else { if (buf > 0) lines = (int)buf; if (opt_info) { PRINTF("#setvar: lines=%d\n", lines); } } } else if (i && !strncmp(name, "mem", i)) { if (func == 0) sprintf(inserted_next, "#setvar mem=%d", limit_mem); else { if (buf == 0 || buf >= PARAMLEN) limit_mem = buf <= INT_MAX ? (int)buf : INT_MAX; if (opt_info) { PRINTF("#setvar: mem=%d%s\n", limit_mem, limit_mem ? "" : " (unlimited)"); } } } else if (i && !strncmp(name, "buffer", i)) { if (func == 0) sprintf(inserted_next, "#setvar buffer=%d", log_getsize()); else log_resize(buf); } else { update_now(); PRINTF("#setvar buffer=%d\n#setvar lines=%d\n#setvar mem=%d\n#setvar timer=%ld\n", log_getsize(), lines, limit_mem, diff_vtime(&now, &ref_time)); } } static void cmd_if(char *arg) { long buf; int type; arg = skipspace(arg); if (*arg!='(') { PRINTF("#if: "); print_error(error=MISMATCH_PAREN_ERROR); return; } arg++; /* skip the '(' */ type = evall(&buf, &arg); if (!REAL_ERROR) { if (type!=TYPE_NUM) error=NO_NUM_VALUE_ERROR; if (*arg != ')') error=MISSING_PAREN_ERROR; else { /* skip the ')' */ if (*++arg == ' ') arg++; } } if (REAL_ERROR) { PRINTF("#if: "); print_error(error); return; } if (buf) (void)parse_instruction(arg, 0, 0, 1); else { arg = get_next_instr(arg); if (!strncmp(arg = skipspace(arg), "#else ", 6)) (void)parse_instruction(arg + 6, 0, 0, 1); } } static void cmd_for(char *arg) { int type = TYPE_NUM, loop=MAX_LOOP; long buf; char *check, *tmp, *increm = 0; arg = skipspace(arg); if (*arg != '(') { PRINTF("#for: "); print_error(error=MISMATCH_PAREN_ERROR); return; } push_params(); if (REAL_ERROR) return; arg = skipspace(arg + 1); /* skip the '(' */ if (*arg != CMDSEP) (void)evaln(&arg); /* execute */ check = arg + 1; if (REAL_ERROR) ; else if (*arg != CMDSEP) { PRINTF("#for: "); print_error(error=MISSING_SEPARATOR_ERROR); } else while (!error && loop && (increm=check, (type = evall(&buf, &increm)) == TYPE_NUM && !error && *increm == CMDSEP && buf)) { tmp = first_regular(increm + 1, ')'); if (*tmp) (void)parse_instruction(tmp + 1, 1, 1, 1); else { PRINTF("#for: "); print_error(error=MISSING_PAREN_ERROR); } if (!error) { tmp = increm + 1; if (*tmp != ')') (void)evaln(&tmp); } loop--; } if (REAL_ERROR) ; else if (increm && *increm != CMDSEP) error=MISSING_SEPARATOR_ERROR; else if (!loop) error=MAX_LOOP_ERROR; else if (type != TYPE_NUM) error=NO_NUM_VALUE_ERROR; if (REAL_ERROR) { PRINTF("#for: "); print_error(error); } if (error!=DYN_STACK_UND_ERROR && error!=DYN_STACK_OV_ERROR) pop_params(); } static void cmd_while(char *arg) { int type = TYPE_NUM, loop=MAX_LOOP; long buf; char *check, *tmp; arg = skipspace(arg); if (!*arg) { PRINTF("#while: "); print_error(error=MISMATCH_PAREN_ERROR); return; } push_params(); check = ++arg; /* skip the '(' */ while (!error && loop && (arg=check, (type = evall(&buf, &arg)) == TYPE_NUM && !error && *arg == ')' && buf)) { if (*(tmp = arg + 1) == ' ') /* skip the ')' */ tmp++; if (*tmp) (void)parse_instruction(tmp, 1, 1, 1); loop--; } if (REAL_ERROR) ; else if (*arg != ')') error=MISSING_PAREN_ERROR; else if (!loop) error=MAX_LOOP_ERROR; else if (type != TYPE_NUM) error=NO_NUM_VALUE_ERROR; if (REAL_ERROR) { PRINTF("#while: "); print_error(error); } if (error!=DYN_STACK_UND_ERROR && error!=DYN_STACK_OV_ERROR) pop_params(); } static void cmd_capture(char *arg) { arg = skipspace(arg); if (!*arg) { if (capturefile) { log_flush(); fclose(capturefile); capturefile = NULL; if (opt_info) { PRINTF("#end of capture to file.\n"); } } else { PRINTF("#capture to what file?\n"); } } else { if (capturefile) { PRINTF("#capture already active.\n"); } else { short append = 0; /* Append to log file, if the name starts with '>' */ if (*arg == '>') { arg++; append = 1; } if ((capturefile = fopen(arg, (append) ? "a" : "w")) == NULL) { PRINTF("#error writing file \"%s\"\n", arg); } else if (opt_info) { PRINTF("#capture to \"%s\" active, \"#capture\" ends.\n", arg); } } } } static void cmd_movie(char *arg) { arg = skipspace(arg); if (!*arg) { if (moviefile) { log_flush(); fclose(moviefile); moviefile = NULL; if (opt_info) { PRINTF("#end of movie to file.\n"); } } else { PRINTF("#movie to what file?\n"); } } else { if (moviefile) { PRINTF("#movie already active.\n"); } else { if ((moviefile = fopen(arg, "w")) == NULL) { PRINTF("#error writing file \"%s\"\n", arg); } else { if (opt_info) { PRINTF("#movie to \"%s\" active, \"#movie\" ends.\n", arg); } update_now(); movie_last = now; log_clearsleep(); } } } } static void cmd_record(char *arg) { arg = skipspace(arg); if (!*arg) { if (recordfile) { fclose(recordfile); recordfile = NULL; if (opt_info) { PRINTF("#end of record to file.\n"); } } else { PRINTF("#record to what file?\n"); } } else { if (recordfile) { PRINTF("#record already active.\n"); } else { if ((recordfile = fopen(arg, "w")) == NULL) { PRINTF("#error writing file \"%s\"\n", arg); } else if (opt_info) { PRINTF("#record to \"%s\" active, \"#record\" ends.\n", arg); } } } } static void cmd_edit(char *arg) { editsess *sp; if (edit_sess) { for (sp = edit_sess; sp; sp = sp->next) { PRINTF("# %s (%u)\n", sp->descr, sp->key); } } else { PRINTF("#no active editors.\n"); } } static void cmd_cancel(char *arg) { editsess *sp; if (!edit_sess) { PRINTF("#no editing sessions to cancel.\n"); } else { if (*arg) { for (sp = edit_sess; sp; sp = sp->next) if (strtoul(arg, NULL, 10) == sp->key) { cancel_edit(sp); break; } if (!sp) { PRINTF("#unknown editing session %d\n", atoi(arg)); } } else { if (edit_sess->next) { PRINTF("#several editing sessions active, use #cancel \n"); } else cancel_edit(edit_sess); } } } static void cmd_net(char *arg) { PRINTF("#received from host: %ld chars, sent to host: %ld chars.\n", received, sent); } #ifndef CLOCKS_PER_SEC # define CLOCKS_PER_SEC uSEC_PER_SEC #endif /* hope it works.... */ static void cmd_cpu(char *arg) { float f, l; update_now(); f = (float)((cpu_clock = clock()) - start_clock) / (float)CLOCKS_PER_SEC; l = (float)(diff_vtime(&now, &start_time)) / (float)mSEC_PER_SEC; PRINTF("#CPU time used: %.3f sec. (%.2f%%)\n", f, (l > 0 && l > f) ? f * 100.0 / l : 100.0); } void show_stat(void) { cmd_net(NULL); cmd_cpu(NULL); } #ifdef BUG_TELNET static void cmd_color(char *arg) { int attrcode; arg = skipspace(arg); if (!*arg) { strcpy(tty_modenorm, tty_modenormbackup); tty_puts(tty_modenorm); if (opt_info) { PRINTF("#standard color cleared.\n"); } return; } attrcode = parse_attributes(arg); if (attrcode == -1) { PRINTF("#invalid attribute syntax.\n"); if (opt_info) show_attr_syntax(); } else { int bg = BACKGROUND(attrcode), fg = FOREGROUND(attrcode); if (fg >= COLORS || bg >= COLORS) { PRINTF("#please specify foreground and background colors.\n"); } else { sprintf(tty_modenorm, "\033[;%c%d;%s%dm", fg= width) { prefix = "\n#option"; len = -1; } else { prefix = ""; } } if (file) { fputc('\n', file); } else { tty_putc('\n'); status(1); } return 1; } static void cmd_option(char *arg) { char buf[BUFSIZE]; int count = 0; arg = skipspace(arg); if (!*arg) { print_all_options(NULL); return; } while ((arg = skipspace(split_first_word(buf, BUFSIZE, arg))), *buf) { enum { MODE_ON, MODE_OFF, MODE_TOGGLE, MODE_REP } mode; char *varp = NULL; char *p = buf; char c = *p; int len = strlen(p); int i; switch (c) { case '=': mode = MODE_REP; p++; break; case '+': mode = MODE_ON; p++; break; case '-': mode = MODE_OFF; p++; break; default: mode = MODE_TOGGLE; break; } count++; for (i = 0; options[i].name; i++) { if (strncmp(options[i].name, p, len) == 0) { varp = options[i].option; break; } } if (varp == NULL) { if (strncmp("list", p, len) == 0) { tty_puts("#list of options:\n"); for (i = 0; options[i].name; ++i) { tty_printf("#option %c%-12s %s\n", *options[i].option ? '+' : '-', options[i].name, options[i].doc); } } else { tty_puts("#syntax: #option [[+|-|=]] | list\n"); } status(1); return; } switch (mode) { case MODE_REP: sprintf(inserted_next, "#option %c%s", *varp ? '+' : '-', p); break; case MODE_ON: *varp = 1; break; case MODE_OFF: *varp = 0; break; case MODE_TOGGLE: *varp ^= 1; break; } /* * reset the reprint buffer if changing its status */ if (varp == &opt_reprint) reprint_clear(); /* as above, but always print status if * "#option info" alone was typed */ if (mode != MODE_REP && !*arg && count==1 && (opt_info || (mode == MODE_TOGGLE && varp==&opt_info))) { PRINTF("#option %s is now o%s.\n", options[i].name, *varp ? "n" : "ff"); } } } static void cmd_file(char *arg) { arg = skipspace(arg); if (*arg == '=') { set_deffile(++arg); if (opt_info) { if (*arg) { PRINTF("#save-file set to \"%s\"\n", deffile); } else { PRINTF("#save-file is now undefined.\n"); } } } else if (*deffile) { sprintf(inserted_next, "#file =%.*s", BUFSIZE-8, deffile); } else { PRINTF("#save-file not defined.\n"); } } static void cmd_save(char *arg) { arg = skipspace(arg); if (*arg) { set_deffile(arg); if (opt_info) { PRINTF("#save-file set to \"%s\"\n", deffile); } } else if (!*deffile) { PRINTF("#save-file not defined.\n"); return; } if (*deffile && save_settings() > 0 && opt_info) { PRINTF("#settings saved to file.\n"); } } static void cmd_load(char *arg) { int res; arg = skipspace(arg); if (*arg) { set_deffile(arg); if (opt_info) { PRINTF("#save-file set to \"%s\"\n", deffile); } } else if (!*deffile) { PRINTF("#save-file not defined.\n"); return; } res = read_settings(); if (res > 0) { /* success */ if (opt_info) { PRINTF("#settings loaded from file.\n"); } } else if (res < 0) { /* critical error */ while (keydefs) delete_keynode(&keydefs); tty_add_initial_binds(); tty_add_walk_binds(); limit_mem = 1048576; PRINTF("#emergency loaded default settings.\n"); } } static char *trivial_eval(ptr *pbuf, char *arg, char *name) { char *tmp = skipspace(arg); if (!pbuf) return NULL; if (*tmp=='(') { arg = tmp + 1; (void)evalp(pbuf, &arg); if (!REAL_ERROR && *arg != ')') error=MISSING_PAREN_ERROR; if (REAL_ERROR) { PRINTF("#%s: ", name); print_error(error); return NULL; } if (*pbuf) arg = ptrdata(*pbuf); else arg = ""; } else unescape(arg); return arg; } static void do_cmd_add(char *arg, int is_static) { ptr pbuf = (ptr)0; char buf[BUFSIZE]; arg = trivial_eval(&pbuf, arg, "add"); if (!REAL_ERROR) while (*arg) { arg = split_first_word(buf, BUFSIZE, arg); if (strlen(buf) >= MIN_WORDLEN) (is_static ? put_static_word : put_word)(buf); } ptrdel(pbuf); } static void cmd_add(char *arg) { do_cmd_add(arg, 0); } static void cmd_addstatic(char *arg) { do_cmd_add(arg, 1); } static void cmd_put(char *arg) { ptr pbuf = (ptr)0; arg = trivial_eval(&pbuf, arg, "put"); if (!REAL_ERROR && *arg) put_history(arg); ptrdel(pbuf); }