diff options
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 2084 |
1 files changed, 0 insertions, 2084 deletions
@@ -1,2084 +0,0 @@ -/* - * powwow -- mud client with telnet protocol - * - * Copyright (C) 1998,2000,2002 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. - * - * - * History: - * - * Initially inspired to the Tintin client by Peter Unold, - * Powwow contains no Tintin code. - * The original program Cancan, written by Mattias Engdegård (Yorick) - * (f91-men@nada.kth.se) 1992-94, - * was greatly improved upon by Vivriel, Thuzzle and Ilie and then - * transformed from Cancan into Powwow by Cosmos who worked - * to make it yet more powerful. - * AmigaDOS porting attempt by Fror. - * Many new features added by Dain. - * As usual, all the developers are in debt to countless users - * for suggestions and debugging. - * Maintance was taken over by Steve Slaven (bpk@hoopajoo.net) in 2005 - */ - -/* - * Set this to whatever you like - * - * #define POWWOW_DIR "/home/gustav/powwow" - */ - -#ifdef USE_LOCALE - #define POWWOW_HACKERS "Yorick, Vivriel, Thuzzle, Ilie, Fr\363r, D\341in" - #define COPYRIGHT "\251" -#else - #define POWWOW_HACKERS "Yorick, Vivriel, Thuzzle, Ilie, Fror, Dain" - #define COPYRIGHT "Copyright" -#endif - -#define POWWOW_VERSION VERSION \ - ", " COPYRIGHT " 2000-2005 by Cosmos\n" \ - COPYRIGHT " 2005 by bpk - http://hoopajoo.net\n" \ - "(contributions by " POWWOW_HACKERS ")\n" - -#define HELPNAME "powwow.help" -#define COPYNAME "COPYING" - -#ifndef POWWOW_DIR -# define POWWOW_DIR "./" -#endif - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <ctype.h> -#include <errno.h> -#include <time.h> - -#include <sys/param.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <memory.h> -#include <unistd.h> - -#ifdef USE_LOCALE -#include <locale.h> -#endif - -/* are these really needed? */ -extern int errno; -extern int select(); - -#include "defines.h" -#include "main.h" -#include "feature/regex.h" -#include "utils.h" -#include "beam.h" -#include "cmd.h" -#include "cmd2.h" -#include "edit.h" -#include "map.h" -#include "list.h" -#include "tcp.h" -#include "tty.h" -#include "eval.h" -#include "log.h" - -/* local function declarations */ -#ifdef MOTDFILE -static void printmotd(void); -#endif -static void mainloop(void); -static void exec_delays(void); -static void prompt_reset_iac(void); -static void get_remote_input(void); -static void get_user_input(void); - -static int search_action_or_prompt(char *line, char clearline, char copyprompt); -#define search_action(line, clearline) search_action_or_prompt((line), (clearline), 0) -#define search_prompt(line, copyprompt) search_action_or_prompt((line), 0, (copyprompt)+1) - -static void set_params(char *line, int *match_s, int *match_e); -static void parse_commands(char *command, char *arg); -static int subst_param(ptr *buf, char *src); -static int jit_subst_vars(ptr *buf, char *src); - - -/* GLOBALS */ -static char *helpname = HELPNAME; -static char *copyname = COPYNAME; - -long received = 0; /* amount of data received from remote host */ -long sent = 0; /* amount of data sent to remote host */ - -volatile char confirm = 0; /* 1 if just tried to quit */ -int history_done = 0; /* number of recursive #history commands */ -int prompt_status = 0; /* prompt status: 0 = ready -> nothing to do; - * 1 if internal echo -> must redraw; - * -1 if something sent to MUD -> waiting for it. - */ -int line_status = 0; /* input line status: 0 = ready -> nothing to do; - * 1 if printed something -> must redraw. - */ - -int limit_mem = 0; /* if !=0, max len of a string or text */ - -char opt_echo = 1; /* 1 if text sent to MUD must be echoed */ -char opt_keyecho = 1; /* 1 if binds must be echoed */ -char opt_info = 1; /* 0 if internal messages are suppressed */ -char opt_exit = 0; /* 1 to autoquit when closing last conn. */ -char opt_history; /* 1 if to save also history */ -char opt_words = 0; /* 1 if to save also word completion list */ -char opt_compact = 0; /* 1 if to clear prompt between remote messages */ -char opt_debug = 0; /* 1 if to echo every line before executing it */ -char opt_speedwalk = 0; /* 1 = speedwalk on */ -char opt_wrap = 0; /* 1 = word wrap active */ -char opt_autoprint = 0; /* 1 = automatically #print lines matched by actions */ -char opt_reprint = 0; /* 1 = reprint sent commands when we get a prompt */ -char opt_sendsize = 0; /* 1 = send term size upon connect */ -char opt_autoclear = 1; /* 1 = clear input line before executing commands - * from spawned programs. - * if 0, spawned progs must #clear before printing - */ - -char hostname[BUFSIZE]; -int portnumber; -static char powwow_dir[BUFSIZE]; /* default path to definition files */ -char deffile[BUFSIZE]; /* name and path of definition file */ -char helpfile[BUFSIZE]; /* name and path of help file */ -char copyfile[BUFSIZE]; /* name and path of copyright file */ -aliasnode *aliases[MAX_HASH]; /* head of alias hash list */ -aliasnode *sortedaliases; /* head of (ASCII) sorted alias list */ -actionnode *actions; /* head of action list */ -promptnode *prompts; /* head of prompt list */ -marknode *markers; /* head of mark list */ -int a_nice = 0; /* default priority of new actions/marks */ -keynode *keydefs; /* head of key binding list */ -delaynode *delays; /* head of delayed commands list */ -delaynode *dead_delays; /* head of dead-delayed commands list */ - -varnode *named_vars[2][MAX_HASH]; /* head of named variables hash list */ -varnode *sortednamed_vars[2]; /* head of (ASCII) sorted named variables list */ -int max_named_vars = 100; /* max number of named vars (grows as needed) */ -int num_named_vars[2]; /* number of named variables actually used */ - -static param_stack paramstk; /* stack of local unnamed vars */ -static unnamedvar global_var[NUMTOT]; /* global unnamed vars */ - -vars *var; /* vector of all vars */ - -ptr globptr[2]; /* global ptr buffer */ -char globptrok = 1|2; /* x&i = 0 if globptr[i] is in use */ - -varnode *prompt; /* $prompt is always set */ -ptr marked_prompt; /* $prompt with marks added */ -static varnode *last_line; /* $line is always set to - * the last line processed */ - -vtime now; /* current time */ -int now_updated; /* current time is up to date */ -vtime start_time; /* time of powwow timer startup */ -vtime ref_time; /* time corresponding to timer == 0 */ - -function_any last_edit_cmd; /* GH: keep track of for repeated cmds */ - -clock_t start_clock, cpu_clock; - -char initstr[BUFSIZE]; /* initial string to send on connect */ - -int linemode = 0; /* line mode flags (LM_* in main.h) */ - -/* for line editing */ -int cols=80, lines=24; /* screen size */ -int cols_1=79; /* == cols if tty_wrapglitch, == cols-1 otherwise */ -int olines; /* previous screen size */ -int col0; /* input line offset (= printstrlen of prompt) */ -int line0; /* screen line where the input line starts */ -char edbuf[BUFSIZE]; /* line editing buffer */ -int edlen; /* length of current input line */ -int pos = 0; /* cursor position in line */ -char surely_isprompt = 0; /* !=0 if last #prompt set #isprompt */ -char verbatim = 0; /* 1 = don't expand aliases or process semicolons */ -char prefixstr[BUFSIZE]; /* inserted in the editing buffer each time */ -char inserted_next[BUFSIZE];/* inserted in buffer just once */ -char flashback = 0; /* cursor is on excursion and should be put back */ -int excursion; /* where the excursion is */ -char edattrbeg[CAPLEN]; /* starting input line attributes */ -char edattrend[CAPLEN]; /* ending input line attributes */ -int edattrbg; /* input line attributes do change bg color */ - -/* signals handling */ -volatile int sig_pending, sig_winch_got, sig_chld_got; - - -/* GH: different ID characters for different action types */ -/* - * Cosmos: they are hardcoded in cmd2.c, function parse_action() - * so don't change them. - */ -char action_chars[ACTION_TYPES] = { '>', '%' }; - -/* GH: different delimeter modes */ -char *delim_list[] = { " ;", " <>!=(),;\"'{}[]+-/*%", 0 }; -int delim_len [] = { 2 , 21 , 0 }; -char *delim_name[] = { "normal", "program", "custom" }; -int delim_mode = DELIM_NORMAL; - -/* Group delimiter */ -char *group_delim; - -int main(int argc, char **argv) -{ - char *p; - int i; - int read_file = 0; /* GH: if true, powwow was started with - * a file argument, and initstr shall be ran */ - -#ifdef USE_LOCALE - if (!setlocale(LC_ALL, "")) { - fprintf(stderr, "Failed setlocale(LC_ALL, \"\")\n"); - } -#endif - - /* initializations */ - initstr[0] = 0; - memzero(conn_list, sizeof(conn_list)); - group_delim = my_strdup( "@" ); - - update_now(); - ref_time = start_time = movie_last = now; - - start_clock = cpu_clock = clock(); -#ifndef NO_RANDOM - init_random((int)now.tv_sec); -#endif - - initialize_cmd(); - - if ((p = getenv("POWWOWDIR"))) { - strcpy(powwow_dir, p); - if (powwow_dir[strlen(powwow_dir) - 1] != '/') - strcat(powwow_dir, "/"); - } else - powwow_dir[0] = '\0'; - - if ((p = getenv("POWWOWHELP"))) - strcpy(helpfile, p); - else if (powwow_dir[0]) - strcpy(helpfile, powwow_dir); - else - strcpy(helpfile, POWWOW_DIR); - - if (helpfile[strlen(helpfile) - 1] != '/') - strcat(helpfile, "/"); - strcat(helpfile, helpname); - if (access(helpfile, R_OK) == -1 && !access(helpname, R_OK)) - strcpy(helpfile, helpname); - - if (powwow_dir[0]) - strcpy(copyfile, powwow_dir); - else - strcpy(copyfile, POWWOW_DIR); - if (copyfile[strlen(copyfile) - 1] != '/') - strcat(copyfile, "/"); - strcat(copyfile, copyname); - if (access(copyfile, R_OK) == -1 && !access(copyname, R_OK)) - strcpy(copyfile, copyname); - - /* initialize variables */ - if ((var = (vars *)malloc(sizeof(vars)*(NUMTOT+max_named_vars)))) { - for (i=0; i<NUMTOT; i++) { - var[i].num = &global_var[i].num; - var[i].str = &global_var[i].str; - } - } else - syserr("malloc"); - - /* stack is empty */ - paramstk.curr = 0; - - /* allocate permanent variables */ - if ((prompt = add_varnode("prompt", 1)) - && (prompt->str = ptrnew(PARAMLEN)) - && (marked_prompt = ptrnew(PARAMLEN)) - && (last_line = add_varnode("last_line", 1)) - && (last_line->str = ptrnew(PARAMLEN)) - && (globptr[0] = ptrnew(PARAMLEN)) - && (globptr[1] = ptrnew(PARAMLEN)) - && !MEM_ERROR) - ; - else - syserr("malloc"); - - - /* - ptr_bootstrap(); - utils_bootstrap(); - beam_bootstrap(); - cmd_bootstrap(); - map_bootstrap(); - eval_bootstrap(); - list_bootstrap(); - tcp_bootstrap(); - */ - edit_bootstrap(); - tty_bootstrap(); - -#ifdef MOTDFILE - printmotd(); -#endif - - printver(); - - if (argc == 1) { - tty_printf( -"\nPowwow comes with ABSOLUTELY NO WARRANTY; for details type \"#help warranty\".\n\ -This is free software, and you are welcome to redistribute it\n\ -under certain conditions; type \"#help copyright\" for details.\n" - ); - } - - if (argc == 1 || argc == 3) { - tty_add_initial_binds(); - tty_add_walk_binds(); - } else if (argc == 2 || argc == 4) { - /* - * assuming first arg is definition file name - * If three args, first is definition file name, - * second and third are hostname and port number - * (they overwrite the ones in definition file) - */ - set_deffile(argv[1]); - - if (access(deffile,R_OK) == -1 || access(deffile,W_OK) == -1) { - char portnum[INTLEN]; - tty_printf("Creating %s\nHost name :", deffile); - tty_flush(); - tty_gets(hostname, BUFSIZE); - if (hostname[0] == '\n') - hostname[0] = '\0'; - else - strtok(hostname, "\n"); - tty_puts("Port number:"); - tty_flush(); - tty_gets(portnum, INTLEN); - portnumber = atoi(portnum); - tty_add_initial_binds(); - tty_add_walk_binds(); - limit_mem = 1048576; - if (save_settings() < 0) - exit(0); - } else if (read_settings() < 0) - exit(0); - else - read_file = 1; - } - if (argc == 3 || argc == 4) { - /* assume last two args are hostname and port number */ - my_strncpy(hostname, argv[argc - 2], BUFSIZE-1); - portnumber = atoi(argv[argc - 1]); - } - - signal_start(); - tty_start(); - - tty_puts(tty_clreoscr); - tty_putc('\n'); - tty_gotoxy(col0 = 0, lines - 2); - tty_puts("Type #help for help.\n"); - line0 = lines - 1; - - FD_ZERO(&fdset); - FD_SET(tty_read_fd, &fdset); - - if (*hostname) - tcp_open("main", (*initstr ? initstr : NULL), hostname, portnumber); - - if (read_file && !*hostname && *initstr) { - parse_instruction(initstr, 0, 0, 1); - history_done = 0; - } - - confirm = 0; - - mainloop(); - - /* NOTREACHED */ - return 0; -} - -/* - * show current version - */ -void printver(void) -{ - tty_printf("Powwow version %s\nOptions: %s%s\n", POWWOW_VERSION, -#ifdef USE_VT100 - "vt100-only," -#else - "termcaps," -#endif -#ifdef USE_SGTTY - " BSD sgtty," -#else - " termios," -#endif -#ifdef USE_REGEXP - " regexp " - #ifdef USE_REGEXP_PCREPOSIX - "(pcreposix)" - #else - "(libc)" - #endif - "," -#else - " no regexp," -#endif -#ifdef USE_LOCALE - " locale," -#endif -#ifdef HAVE_LIBDL - " modules," -#endif - , -#if __STDC__ - " compiled " __TIME__ " " __DATE__ -#else - " uknown compile date" -#endif - ); -} - -#ifdef MOTDFILE -/* - * print the message of the day if present - */ -static void printmotd(void) -{ - char line[BUFSIZE]; - FILE *f = fopen(MOTDFILE, "r"); - if (f) { - while (fgets(line, BUFSIZE, f)) - tty_puts(line); - fclose(f); - } -} -#endif - -static void redraw_everything(void) -{ - if (prompt_status == 1 && line_status == 0) - line_status = 1; - if (prompt_status == 1) - draw_prompt(); - else if (prompt_status == -1) { - promptzero(); - col0 = surely_isprompt = '\0'; - } - if (line_status == 1) - draw_input_line(); -} - -/* how much can we sleep in select() ? */ -static void compute_sleeptime(vtime **timeout) -{ - static vtime tbuf; - int sleeptime = 0; - - if (delays) { - update_now(); - sleeptime = diff_vtime(&delays->when, &now); - if (!sleeptime) - sleeptime = 1; /* if sleeptime is less than 1 millisec, - * set to 1 millisec */ - } - if (flashback && (!sleeptime || sleeptime > FLASHDELAY)) - sleeptime = FLASHDELAY; - - if (sleeptime) { - tbuf.tv_sec = sleeptime / mSEC_PER_SEC; - tbuf.tv_usec = (sleeptime % mSEC_PER_SEC) * uSEC_PER_mSEC; - *timeout = &tbuf; - } else - *timeout = (vtime *)NULL; -} - -/* - * main loop. - */ -static void mainloop(void) -{ - fd_set readfds; - int i, err; - vtime *timeout; - - for (;;) { - tcp_fd = tcp_main_fd; - exec_delays(); - - do { - if (sig_pending) - sig_bottomhalf(); /* this might set errno... */ - - tcp_flush(); - - if (!(pos <= edlen)) { - PRINTF("\n#*ARGH* assertion failed (pos <= edlen): mail bpk@hoopajoo.net\n"); - pos = edlen; - } - - redraw_everything(); - tty_flush(); - - compute_sleeptime(&timeout); - - error = now_updated = 0; - - readfds = fdset; - err = select(tcp_max_fd+1, &readfds, NULL, NULL, timeout); - - prompt_reset_iac(); - - } while (err < 0 && errno == EINTR); - - if (err < 0 && errno != EINTR) - syserr("select"); - - if (flashback) putbackcursor(); - - /* process subsidiary and spawned connections first */ - if (tcp_count > 1 || tcp_attachcount) { - for (i=0; err && i<conn_max_index; i++) { - if (CONN_INDEX(i).id && CONN_INDEX(i).fd != tcp_main_fd) { - tcp_fd = CONN_INDEX(i).fd; - if (FD_ISSET(tcp_fd, &readfds)) { - err--; - get_remote_input(); - } - } - } - } - /* and main connection last */ - if (tcp_main_fd != -1 && FD_ISSET(tcp_main_fd, &readfds)) { - tcp_fd = tcp_main_fd; - get_remote_input(); - } - if (FD_ISSET(tty_read_fd, &readfds)) { - tcp_fd = tcp_main_fd; - confirm = 0; - get_user_input(); - } - - } -} - -/* - * set the new prompt / input line status - */ -void status(int s) -{ - if (s < 0) { - /* waiting prompt from the MUD */ - prompt_status = s; - line_status = 1; - } else { - if (prompt_status >= 0) - prompt_status = s; - if (line_status >= 0) - line_status = s; - } -} - -/* - * execute the delayed labels that have expired - * and place them in the disabled delays list - */ -static void exec_delays(void) -{ - delaynode *dying; - ptr *pbuf, buf = (ptr)0; - - if (!delays) - return; - - update_now(); - - if (cmp_vtime(&delays->when, &now) > 0) - return; - - /* remember delayed command may modify the prompt and/or input line! */ - if (prompt_status == 0) { - clear_input_line(opt_compact || !opt_info); - if (!opt_compact && opt_info && prompt_status == 0 && promptlen) { - tty_putc('\n'); - col0 = 0; - status(1); - } - } - - TAKE_PTR(pbuf, buf); - - while (delays && cmp_vtime(&delays->when, &now) <= 0) { - dying = delays; /* remove delayed command from active list */ - delays = dying->next; /* and put it in the dead one */ - - add_node((defnode *)dying, (defnode **)&dead_delays, rev_time_sort); - - /* must be moved before executing delay->command - * and command must be copied in a buffer - * (can't you imagine why? The command may edit itself...) - */ - - if (opt_info) - tty_printf("#now [%s]\n", dying->command); - - if (*dying->command) { - - error = 0; - - *pbuf = ptrmcpy(*pbuf, dying->command, strlen(dying->command)); - if (MEM_ERROR) - errmsg("malloc (#in/#at)"); - else { - parse_instruction(ptrdata(*pbuf), 0, 0, 1); - history_done = 0; - } - } - } - DROP_PTR(pbuf); -} - - -#define IAC_N 1024 -static char *iac_v[IAC_N]; -static int iac_f, iac_l; - -static void prompt_reset_iac(void) -{ - iac_f = iac_l = 0; -} - -void prompt_set_iac(char *p) -{ - if (iac_f == iac_l) - iac_f = iac_l = 0; - - if (iac_l < IAC_N) - iac_v[iac_l++] = p; -} - -static char *prompt_get_iac(void) -{ - return iac_l > iac_f ? iac_v[iac_f] : NULL; -} - - -/* compute the effective prompt string; may end in \b* or \r */ -static void effective_prompt(void) -{ - char *const pstr = promptstr; - char *dst = pstr; - const size_t len = promptlen; - size_t pos = 0, maxpos = 0, p; - for (p = 0; p < len; ++p) { - char c = pstr[p]; - if (c == '\b') { - if (pos > 0) - --pos; - continue; - } - if (c == '\r') { - pos = 0; - continue; - } - if (c == '\033' - && len - p > 2 && pstr[p + 1] == '[' && pstr[p + 2] == 'K') { - if (dst == pstr) - dst = strdup(pstr); - maxpos = pos; - memset(dst + pos, 0, len - pos); - p += 2; - continue; - } - dst[pos++] = c; - if (pos > maxpos) - maxpos = pos; - } - if (dst != pstr) { - memcpy(pstr, dst, pos); - free(dst); - } - size_t nbs = maxpos - pos; - if (nbs == 0) - ; - else if (pos == 0) - pstr[maxpos++] = '\r'; - else { - memset(pstr + maxpos, '\b', nbs); - maxpos += nbs; - } - ptrsetlen(prompt->str, maxpos); -} - -static int grab_prompt(char *linestart, int len, int islast) -{ - char *p; - int is_iac_prompt = surely_isprompt = 0; - - /* recognize IAC GA as end-of-prompt marker */ - if ((CONN_LIST(tcp_fd).flags & IDPROMPT)) { - if ((p = prompt_get_iac()) && p > linestart && p <= linestart+len) - iac_f++, is_iac_prompt = len = p - linestart; - else if (!islast) - return 0; - } - - /* - * We may get a prompt in the middle of a bunch of lines, so - * match #prompts. They usually have no #print, so we print manually - * if islast is not set and a #prompt matches. - */ - if ((is_iac_prompt || islast || printstrlen(linestart) < cols) && - ((search_prompt(linestart, 1) && surely_isprompt) || is_iac_prompt)) { - - char *reprint; - /* - * the line starts with a prompt. - * #isprompt placed the actual prompt in $prompt, - * we must still process the rest of the line. - */ - if (surely_isprompt > 0 && surely_isprompt <= len) { - len = surely_isprompt; - prompt_status = 1; - } else if (!surely_isprompt && is_iac_prompt) { - len = surely_isprompt = is_iac_prompt; - prompt->str = ptrmcpy(prompt->str, linestart, len); - effective_prompt(); - if (MEM_ERROR) { promptzero(); errmsg("malloc(prompt)"); return 0; } - prompt_status = 1; - } - - /* - * following data may be the reply to a previously sent command, - * so we may have to reprint that command. - */ - if ((reprint = reprint_getline()) && *reprint) { - smart_print(promptstr, 0); - status(-1); - tty_printf("(%s)\n", reprint); - } else if (!islast) - smart_print(promptstr, 1); - } else if (islast) { - prompt->str = ptrmcpy(prompt->str, linestart, len); - if (MEM_ERROR) { promptzero(); errmsg("malloc(prompt)"); return 0; } - effective_prompt(); - prompt_status = 1; /* good, we got what to redraw */ - } else - len = 0; - - return len; -} - - -/* - * process remote input one line at time. stop at "\n". - */ -static void process_singleline(char **pbuf, int *psize) -{ - int size, len = 0; - char *wasn = 0, *buf, *linestart = *pbuf, *lineend, *end = *pbuf + *psize; - - if ((lineend = memchr(linestart, '\n', *psize))) { - /* ok, there is a newline */ - - *(wasn = lineend) = '\0'; - buf = lineend + 1; /* start of next line */ - } - - if (!lineend) - /* line continues till end of buffer, no trailing \n */ - buf = lineend = end; - - size = buf - linestart; - -#ifdef DEBUGCODE_2 - /* debug code to see in detail what codes come from the server */ - { - char c; - char *t; - tty_putc('{'); - for (t = linestart; t < lineend && (c = *t); t++) { - if (c < ' ' || c > '~') - tty_printf("[%d]", (int)c); - else - tty_putc(c); - } - tty_puts("}\n"); - } -#endif - - /* - * Try to guess where is the prompt... really not much more than - * a guess. Again, do it only on main connection: we do not want - * output from other connections to mess with the prompt - * of main connection :) - * - * Since we now have #prompt, behave more restrictively: - * if no #prompts match or a #prompt matches but does not set #isprompt - * (i.e. recognize it for not being a prompt), - * we check for #actions on it when the \n arrives. - * if a #prompt matches and sets #isprompt, then it is REALLY a prompt - * so never match #actions on it. - */ - if (lineend == end && tcp_fd == tcp_main_fd) { - /* - * The last line in the chunk we received has no trailing \n - * Assume it is a prompt. - */ - if (surely_isprompt && promptlen && prompt_status == 1) { - draw_prompt(); tty_putc('\n'); col0 = 0; - } - surely_isprompt = 0; - promptzero(); - if (lineend > linestart && (len = grab_prompt(linestart, lineend-linestart, 1))) - size = len; - } else { - if (tcp_fd == tcp_main_fd) { - surely_isprompt = 0; - promptzero(); - - if (linestart[0]) { - /* set $last_line */ - last_line->str = ptrmcpy(last_line->str, linestart, strlen(linestart)); - if (MEM_ERROR) { print_error(error); return; } - - if (lineend > linestart && (len = grab_prompt(linestart, lineend-linestart, 0))) - size = len; - } - } - if (!len && ((!search_action(linestart, 0) || opt_autoprint))) { - if (line0 < lines - 1) - line0++; - if (tcp_fd != tcp_main_fd) /* sub connection */ - tty_printf("##%s> ", CONN_LIST(tcp_fd).id); - - smart_print(linestart, 1); - } - } - - /* - * search_prompt and search_action above - * might set error: clear it to avoid troubles. - */ - error = 0; - if (wasn) *wasn = '\n'; - *pbuf += size; - *psize -= size; -} - -/* - * Code to merge lines from host that were splitted - * into different packets: it is a horrible kludge (sigh) - * and can be used only on one connection at time. - * We currently do it on main connection. - * - * Note that this code also works for _prompts_ splitted into - * different packets, as long as no #prompts execute #isprompt - * on an incomplete prompt (as stated in the docs). - */ -static int process_first_fragment(char *buf, int got) -{ - int processed = 0; - - /* - * Don't merge if the first part of the line was intercepted - * by a #prompt action which executed #isprompt - * (to avoid intercepting it twice) - */ - if (*buf == '\n') { - char deleteprompt = 0, matched = 0; - - if (opt_compact) { - /* in compact mode, skip the first \n */ - deleteprompt = 1; - processed++; - } - - /* - * the prompt was actually a complete line. - * no need to put it on the top of received data. - * unless #isprompt was executed, demote it to a regular line, - * match #actions on it, copy it in last_line. - */ - if (!surely_isprompt) { - last_line->str = ptrcpy(last_line->str, prompt->str); - if (MEM_ERROR) { print_error(error); return 0; } - - /* - * Kludge for kludge: don't delete the old prompt immediately. - * Instead, match actions on it first. - * If it matches, clear the line before running the action - * (done by the "1" in search_action() ) - * If it doesn't match, delete it only if opt_compact != 0 - */ - - matched = search_action(promptstr, 1); - } - if (!matched) - clear_input_line(deleteprompt); - status(-1); - } else { - /* - * try to merge the prompt with the first line in buf - * (assuming we have a line splitted into those parts) - * then clear the prompt. - */ - char *lineend, *spinning = NULL; - - /* find the end of the first line. include the final newline. */ - if ((lineend = strchr(buf, '\n'))) - lineend++; - else - lineend = buf + got; - - if (surely_isprompt) { - /* - * either #isprompt _was_ executed, - * or we got a MUME spinning bar. - * in both cases, don't try to merge. - * - * print a newline (to keep the old prompt on screen) - * only if !opt_compact and we didn't get a MUME spinning bar. - */ - clear_input_line(opt_compact); - if (!spinning && !opt_compact) - tty_putc('\n'), col0 = 0; - promptzero(); - } else { - ptr *pp, p = (ptr)0; - char *dummy; - int dummyint; - - /* ok, merge this junk with the prompt */ - TAKE_PTR(pp, p); - *pp = ptrcpy(*pp, prompt->str); - *pp = ptrmcat(*pp, buf, lineend - buf); - - if (MEM_ERROR) { print_error(error); return 0; } - if (!*pp) - return 0; - dummy = ptrdata(*pp); - dummyint = ptrlen(*pp); - /* this also sets last_line or prompt->str : */ - clear_input_line(1); - process_singleline(&dummy, &dummyint); - - processed = lineend - buf; - } - } - return processed; -} - -/* - * process input from remote host: - * detect special sequences, trigger actions, locate prompt, - * word-wrap, print to tty - */ -void process_remote_input(char *buf, int size) -{ - - if (promptlen && tcp_fd == tcp_main_fd) - promptzero(); /* discard the prompt, we look for another one */ - - status(1); - - do { - process_singleline(&buf, &size); - } while (size > 0); -} - -static void common_clear(int newline) -{ - clear_input_line(opt_compact); - if (newline) { - tty_putc('\n'); col0 = 0; status(1); - } -} - -/* - * get data from the socket and process/display it. - */ -static void get_remote_input(void) -{ - char buffer[BUFSIZE + 2]; /* allow for a terminating \0 later */ - char *buf = buffer, *newline; - int got, otcp_fd, i; - - if (CONN_LIST(tcp_fd).fragment) { - if ((i = strlen(CONN_LIST(tcp_fd).fragment)) >= BUFSIZE-1) { - i = 0; - common_clear(promptlen && !opt_compact); - tty_printf("#error: ##%s : line too long, discarded\n", CONN_LIST(tcp_fd).id); - } else { - buf += i; - memcpy(buffer, CONN_LIST(tcp_fd).fragment, i); - } - free(CONN_LIST(tcp_fd).fragment); - CONN_LIST(tcp_fd).fragment = 0; - } else - i = 0; - - got = tcp_read(tcp_fd, buf, BUFSIZE - i); - if (!got) - return; - - buf[got]='\0'; /* Safe, there is space. Do it now not to forget it later */ - received += got; - -#ifdef DEBUGCODE - /* debug code to see in detail what strange codes come from the server */ - { - char c, *t; - newline = buf + got; - tty_printf("%s{", edattrend); - for (t = buf; t < newline; t++) { - if ((c = *t) < ' ' || c > '~') - tty_printf("[%d]", c); - else tty_putc(c); - } - tty_puts("}\n"); - } -#endif - - if (!(CONN_LIST(tcp_fd).flags & ACTIVE)) - return; /* process only active connections */ - - got += (buf - buffer); - buf = buffer; - - if (CONN_LIST(tcp_fd).flags & SPAWN) { - /* this is data from a spawned child or an attached program. - * execute as if typed */ - otcp_fd = tcp_fd; - tcp_fd = tcp_main_fd; - - if ((newline = strchr(buf, '\n'))) { - /* instead of newline = strtok(buf, "\n") */ - *newline = '\0'; - - if (opt_autoclear && line_status == 0) { - common_clear(!opt_compact); - } - do { - if (opt_info) { - if (line_status == 0) { - common_clear(!opt_compact); - } - tty_printf("##%s [%s]\n", CONN_LIST(otcp_fd).id, buf); - } - parse_user_input(buf, 0); - /* - * strtok() may have been used in parse_user_input()... - * cannot rely it refers on what we may have set above. - * (it causes a bug in #spawned commands if they - * evaluate (attr(), noattr) or they #connect ... ) - * so do it manually. - */ - /* - * buf = strtok(NULL, "\n"); - */ - if ((buf = newline) && - (newline = strchr(++buf, '\n'))) - *newline = '\0'; - } while (buf && newline); - } - - if (buf && *buf && !newline) { - /* - * save last fragment for later, when spawned command will - * (hopefully) send the rest of the text - */ - CONN_LIST(otcp_fd).fragment = my_strdup(buf); - - if (opt_info) { - if (line_status == 0) { - common_clear(!opt_compact); - } - tty_printf("#warning: ##%s : unterminated [%s]\n", CONN_LIST(otcp_fd).id, buf); - } - } - tcp_fd = otcp_fd; - return; - } - - if (linemode & LM_CHAR) { - /* char-by-char mode: just display output, no fuss */ - clear_input_line(0); - tty_puts(buf); - return; - } - - /* line-at-a-time mode: process input in a number of ways */ - - if (tcp_fd == tcp_main_fd && promptlen) { - i = process_first_fragment(buf, got); - buf += i, got -= i; - } else { - common_clear(promptlen && !opt_compact); - } - - if (got > 0) - process_remote_input(buf, got); -} - - -#ifdef USE_REGEXP -/* - * GH: matches precompiled regexp, return actual params in param array - * return 1 if matched, 0 if not - */ -static int match_regexp_action(void *regexp, char *line, int *match_s, int *match_e) -{ - regmatch_t reg_match[NUMPARAM - 1]; - - if (!regexec((regex_t *)regexp, line, NUMPARAM - 1, reg_match, 0)) { - int n; - - match_s[0] = 0; - match_e[0] = strlen(line); - for (n = 1; n < NUMPARAM; n++) - match_s[n] = match_e[n] = 0; - for (n = 0; n <= (int)((regex_t *)regexp)->re_nsub && - n < NUMPARAM - 1; n++) { - if (reg_match[n].rm_so == -1) continue; - match_s[n+1] = reg_match[n].rm_so; - match_e[n+1] = reg_match[n].rm_eo; - } - return 1; - } - return 0; -} -#endif - -/* - * match action containing &1..&9 and $1..$9 and return actual params start/end - * in match_s/match_e - return 1 if matched, 0 if not - */ -static int match_weak_action(char *pat, char *line, int *match_s, int *match_e) -{ - char mpat[BUFSIZE], *npat=0, *npat2=0, *src=line, *nsrc=0, c; - ptr *pbuf, buf = (ptr)0; - char *tmp, *realpat = pat; - int mbeg = 0, mword = 0, prm = -1; - - TAKE_PTR(pbuf, buf); - - if (jit_subst_vars(pbuf, pat)) - pat = ptrdata(*pbuf); - if (REAL_ERROR) { - print_error(error); - DROP_PTR(pbuf); - return 0; - } - unescape(pat); - - { - int p; - for (p = 0; p < NUMPARAM; p++) - match_s[p] = match_e[p] = 0; - } - - if (*pat == '^') { - pat++; - mbeg = 1; /* anchor match at line start */ - } - if (*pat == '&' || *pat == '$') - mbeg = - mbeg - 1; /* pattern starts with '&' or '$' */ - - while (pat && *pat) { - if (((c=*pat) == '&' || c == '$')) { - /* &x matches a string */ - /* $x matches a single word */ - tmp = pat + 1; - if (isdigit(*tmp)) { - int p = 0; - while (isdigit(*tmp) && p < NUMPARAM) { - p *= 10; - p += *tmp++ - '0'; - } - if (p <= 0 || p >= NUMPARAM) { - DROP_PTR(pbuf); - return 0; - } - prm = p; - pat = tmp; - if (c == '$') - mword = 1; - } else { - PRINTF("#error: bad action pattern \"%s\"\n#missing digit after \"%s\"\n", - realpat, pat); - DROP_PTR(pbuf); - return 0; - } - } - - npat = first_valid(pat, '&'); - npat2 = first_valid(pat, '$'); - if (npat2 < npat) npat = npat2; - if (!*npat) npat = 0; - - if (npat) { - my_strncpy(mpat, pat, npat-pat); - /* mpat[npat - pat] = 0; */ - } else - strcpy(mpat, pat); - - if (*mpat) { - nsrc = strstr(src, mpat); - if (!nsrc) { - DROP_PTR(pbuf); - return 0; - } - if (mbeg > 0) { - if (nsrc != src) { - DROP_PTR(pbuf); - return 0; - } - mbeg = 0; /* reset mbeg to stop further start match */ - } - if (prm != -1) { - match_s[prm] = src - line; - match_e[prm] = nsrc - line; - } - } else if (prm != -1) { - /* end of pattern space */ - match_s[prm] = src - line; - match_e[prm] = strlen(line); - } - - /* post-processing of param */ - if (prm != -1 && match_e[prm] && mword) { - if (mbeg == -1) { - /* unanchored '$' start, take last word */ - if ((tmp = memrchrs(line + match_s[prm], - match_e[prm] - match_s[prm], - DELIM, DELIM_LEN))) { - match_s[prm] = tmp - line + 1; - } - } else if (!*pat) { - /* '$' at end of pattern, take first word */ - if ((tmp = memchrs(line + match_s[prm], - match_e[prm] - match_s[prm], - DELIM, DELIM_LEN))) - match_e[prm] = tmp - line; - } else { - /* match only if param is single-worded */ - if (memchrs(line + match_s[prm], - match_e[prm] - match_s[prm], - DELIM, DELIM_LEN)) { - DROP_PTR(pbuf); - return 0; - } - } - } - if (prm != -1 && match_e[prm]) - mbeg = mword = 0; /* reset match flags */ - src = nsrc + strlen(mpat); - pat = npat; - } - DROP_PTR(pbuf); - - match_s[0] = 0; match_e[0] = strlen(line); - return 1; -} - -/* - * Search for #actions or #prompts to trigger on an input line. - * The line can't be trashed since we want to print it on the screen later. - * Return 1 if line matched to some #action, 0 otherwise - * - * Optionally clear the input line before running the trigger command. - */ -static int search_action_or_prompt(char *line, char clearline, char onprompt) -{ - /* - * we need actionnode and promptnode to be the same "triggernode" type - */ - triggernode *p; - int ret = 0; - int match_s[NUMPARAM], match_e[NUMPARAM]; - - for (p = onprompt ? prompts : actions; p; p = p->next) { -#ifdef USE_REGEXP - if (p->active && - ((p->type == ACTION_WEAK && match_weak_action(p->pattern, line, match_s, match_e)) - || (p->type == ACTION_REGEXP && match_regexp_action(p->regexp, line, match_s, match_e)) - )) -#else - if (p->active && - ((p->type == ACTION_WEAK && match_weak_action(p->pattern, line, match_s, match_e)) - )) -#endif - { - push_params(); if (error) return 0; - ret = 1; error = 0; - set_params(line, match_s, match_e); if (error) return 0; - if (onprompt == 2) { - prompt->str = ptrmcpy(prompt->str, line, strlen(line)); - if (MEM_ERROR) { promptzero(); errmsg("malloc(prompt)"); return 0; } - } - if (clearline) - clear_input_line(1); - parse_instruction(p->command, 0, 1, 1); - history_done = 0; - if (error!=DYN_STACK_UND_ERROR && error!=DYN_STACK_OV_ERROR) - pop_params(); - break; - } - } - if (error) return 0; - return ret; -} - -/* - * read terminal input and send to parser. - * decode keys that send escape sequences - */ -static void get_user_input(void) -{ - int i, j, chunk = 1; - static char buf[BUFSIZE+1]; /* allow for terminating \0 */ - char *c = buf; - static char typed[CAPLEN]; /* chars typed so far (with partial match) */ - static int nchars = 0; /* number of them */ - - /* We have 4 possible line modes: - * line mode, local echo: line editing functions in effect - * line mode, no echo: sometimes used for passwords, no line editing - * char mode, no echo: send a character directly, no local processing - * char mode, local echo: extremely rare, do as above. - */ - if (!(linemode & (LM_NOECHO | LM_CHAR))) /* line mode, local echo */ - chunk = BUFSIZE; - - while ((j = tty_read(c, chunk)) < 0 && errno == EINTR) - ; - if (j == 0) - return; - - if (j < 0 || (chunk == 1 && j != chunk)) - syserr("read from tty"); - - c[chunk] = '\0'; - - if (linemode & LM_CHAR) { - /* char mode. chunk == 1 */ - while ((i = write(tcp_fd, c, 1)) < 0 && errno == EINTR) - ; - if (i != 1) - syserr("write to socket"); - if (!(linemode & LM_NOECHO)) - tty_putc(*c); - last_edit_cmd = (function_any)0; - } else if (linemode & LM_NOECHO) { - /* sending password (line mode, no echo). chunk == 1 */ - if ((*c != '\n' && *c != '\r') && edlen < BUFSIZE - 2) - edbuf[edlen++] = *c; - else { - edbuf[edlen] = '\0'; -#ifdef BUG_ANSI - if (edattrbg) - tty_printf("%s\n", edattrend); - else -#endif - tty_putc('\n'); - - tcp_write(tcp_fd, edbuf); - edlen = 0; - typed[nchars = 0] = 0; - } - edbuf[pos = edlen] = '\0'; - last_edit_cmd = (function_any)0; - } else { - /* normal mode (line mode, echo). chunk == BUFSIZE */ - int done = 0; - keynode *p; - - for (; j > 0; c++, j--) { - - /* search function key strings for match */ - /* GH: support for \0 in sequence */ - done = 0; - typed[nchars++] = *c; - - while (!done) { - done = 1; - /* - * shortcut: - * an initial single ASCII char cannot match any #bind - */ - if (nchars == 1 && *c >= ' ' && *c <= '~') - p = NULL; - else { - for (p = keydefs; (p && (p->seqlen < nchars || - memcmp(typed, p->sequence, nchars))); - p = p->next) - ; - } - - if (!p) { - /* - * GH: type the first character and keep processing - * the rest in the input buffer - */ - i = 1; - last_edit_cmd = (function_any)0; - insert_char(typed[0]); - while (i < nchars) { - typed[i - 1] = typed[i]; - i++; - } - if (--nchars) - done = 0; - } else if (p->seqlen == nchars) { - if (flashback) - putbackcursor(); - p->funct(p->call_data); - last_edit_cmd = (function_any)p->funct; /* GH: keep track of last command */ - nchars = 0; - } - } - } - } -} - -/* - * split str into words separated by DELIM, and place in - * VAR[1].str ... VAR[9].str - - * the whole str is put in VAR[0].str - */ -static char *split_words(char *str) -{ - int i; - char *end; - ptr *prm; - - *VAR[0].str = ptrmcpy(*VAR[0].str, str, strlen(str)); - for (i = 1; i < NUMPARAM; i++) { - *VAR[i].num = 0; - prm = VAR[i].str; - /* skip multiple span of DELIM */ - while (*str && strchr(DELIM, *str)) - str++; - end = str; - while (*end && !strchr(DELIM, *end)) - end++; - *prm = ptrmcpy(*prm, str, end-str); - str = end; - if (MEM_ERROR) { print_error(error); return NULL; } - } - return str; -} - -/* - * free the whole stack and reset it to empty - */ -static void free_allparams(void) -{ - int i,j; - - paramstk.curr = 0; /* reset stack to empty */ - - for (i=1; i<MAX_STACK; i++) { - for (j=0; j<NUMPARAM; j++) { - ptrdel(paramstk.p[i][j].str); - paramstk.p[i][j].str = (ptr)0; - } - } - for (j=0; j<NUMPARAM; j++) { - VAR[j].num = ¶mstk.p[0][j].num; - VAR[j].str = ¶mstk.p[0][j].str; - } -} - -void push_params(void) -{ - int i; - - if (paramstk.curr < MAX_STACK - 1) - paramstk.curr++; - else { - print_error(error=DYN_STACK_OV_ERROR); - free_allparams(); - return; - } - for (i=0; i<NUMPARAM; i++) { - *(VAR[i].num = ¶mstk.p[paramstk.curr][i].num) = 0; - ptrzero(*(VAR[i].str = ¶mstk.p[paramstk.curr][i].str)); - } -} - -void pop_params(void) -{ - int i; - - if (paramstk.curr > 0) - paramstk.curr--; - else { - print_error(error=DYN_STACK_UND_ERROR); - free_allparams(); - return; - } - for (i=0; i<NUMPARAM; i++) { - ptrdel(*VAR[i].str); *VAR[i].str = (ptr)0; - VAR[i].num = ¶mstk.p[paramstk.curr][i].num; - VAR[i].str = ¶mstk.p[paramstk.curr][i].str; - } -} - -static void set_params(char *line, int *match_s, int *match_e) -{ - int i; - - for (i=0; i<NUMPARAM; i++) { - *VAR[i].num = 0; - if (match_e[i] > match_s[i]) { - *VAR[i].str = ptrmcpy(*VAR[i].str, line + match_s[i], - match_e[i] - match_s[i]); - if (MEM_ERROR) { - print_error(error); - return; - } - } else - ptrzero(*VAR[i].str); - } -} - -char *get_next_instr(char *p) -{ - int count, is_if; - char *sep, *q; - - p = skipspace(p); - - if (!*p) - return p; - - count = is_if = !strncmp(p, "#if", 3); - - do { - sep = first_regular(p, CMDSEP); - - q = p; - if (*q) do { - if (*q == '#') q++; - - q = first_regular(q, '#'); - } while (*q && strncmp(q, "#if", 3)); - - if (sep<=q) { - if (*(p = sep)) - p++; - } - else - if (*q) - p = get_next_instr(q); - else { - print_error(error=SYNTAX_ERROR); - return NULL; - } - sep = skipspace(p); - } while (*p && count-- && - (!is_if || (!strncmp(sep, "#else", 5) && - (*(p = sep + 5))))); - - return p; -} - -static void send_line(char *line, char silent) -{ - if (!silent && opt_echo) { PRINTF("[%s]\n", line); } - tcp_write(tcp_fd, line); -} - - -/* - * Parse and exec the first instruction in 'line', and return pointer to the - * second instruction in 'line' (if any). - */ -char *parse_instruction(char *line, char silent, char subs, char jit_subs) -{ - aliasnode *np; - char *buf, *arg, *end, *ret; - char last_is_sep = 0; - int len, copied = 0, otcp_fd = -1; - ptr p1 = (ptr)0, p2 = (ptr)0; - ptr *pbuf, *pbusy, *tmp; - - if (error) return NULL; - - ret = get_next_instr(line); - - if (!ret || ret==line) /* error or empty instruction, bail out */ - return ret; - - /* - * remove the optional ';' after an instruction, - * to have an usable string, ending with \0. - * If it is escaped, DON'T remove it: it is not a separator, - * and the instruction must already end with \0, or we would not be here. - */ - if (ret[-1] == CMDSEP) { - /* instruction is not empty, ret[-1] is allowed */ - if (ret > line + 1 && ret[-2] == ESC) { - /* ';' is escaped */ - } else { - *--ret = '\0'; - last_is_sep = 1; - } - } - - /* - * using two buffers, p1 and p2, for four strings: - * result of subs_param, result of jit_subst_vars, result of - * unescape and first word of line. - * - * So care is required to avoid clashes. - */ - TAKE_PTR(pbuf, p1); - TAKE_PTR(pbusy, p2); - - if (subs && subst_param(pbuf, line)) { - line = *pbuf ? ptrdata(*pbuf) : ""; - SWAP2(pbusy, pbuf, tmp); - copied = 1; - } - if (jit_subs && jit_subst_vars(pbuf, line)) { - line = *pbuf ? ptrdata(*pbuf) : ""; - SWAP2(pbusy, pbuf, tmp); - copied = 1; - } - if (!copied) { - *pbuf = ptrmcpy(*pbuf, line, strlen(line)); - line = *pbuf ? ptrdata(*pbuf) : ""; - SWAP2(pbusy, pbuf, tmp); - } - if (subs || jit_subs) - unescape(line); - - /* now line is in (pbusy) and (pbuf) is available */ - - /* restore integrity of original line: must still put it in history */ - if (last_is_sep) - *ret++ = CMDSEP; - - if (REAL_ERROR) { - print_error(error); - DROP_PTR(pbuf); DROP_PTR(pbusy); - return NULL; - } - /* run-time debugging */ - if (opt_debug) { - PRINTF("#parsing: %s\n", line); - } - - if (!*line) - send_line(line, silent); - else do { - arg = skipspace(line); - - if (arg[0] == '#' && arg[1] == '#') { /* send to other connection */ - *pbuf = ptrsetlen(*pbuf, len = strlen(arg)); - if (REAL_ERROR) { print_error(error); break; } - buf = ptrdata(*pbuf); - line = split_first_word(buf, len+1, arg + 2); - /* now (pbuf) is used too: first word of line */ - /* line contains the rest */ - otcp_fd = tcp_fd; - if ((tcp_fd = tcp_find(buf))<0) { - error = OUT_RANGE_ERROR; - PRINTF("#no connection named \"%s\"\n", buf); - break; - } - arg = skipspace(line); - if (!*arg) { - if (CONN_LIST(tcp_fd).flags & SPAWN) { - error = OUT_RANGE_ERROR; - PRINTF("#only MUD connections can be default ones!\n"); - } else { - /* set it as main connection */ - tcp_set_main(tcp_fd); - otcp_fd = -1; - } - /* stop parsing, otherwise a newline would be sent to tcp_fd */ - break; - } - /* now we can trash (pbuf) */ - } - - if (*arg == '{') { /* instruction contains a block */ - end = first_regular(line = arg + 1, '}'); - - if (*end) { - *end = '\0'; - parse_user_input(line, silent); - *end = '}'; - } else - print_error(error=MISSING_PAREN_ERROR); - } else { - int oneword; - /* initial spaces are NOT skipped this time */ - - *pbuf = ptrsetlen(*pbuf, len = strlen(line)); - if (REAL_ERROR) { print_error(error); break; } - - buf = ptrdata(*pbuf); - arg = split_first_word(buf, len+1, line); - /* buf contains the first word, arg points to arguments */ - - /* now pbuf is used too */ - if (!*arg) oneword = 1; - else oneword = 0; - - if ((np = *lookup_alias(buf))&&np->active) { - push_params(); - if (REAL_ERROR) break; - - split_words(arg); /* split argument into words - and place them in $0 ... $9 */ - parse_instruction(np->subst, 0, 1, 1); - - if (error!=DYN_STACK_UND_ERROR && error!=DYN_STACK_OV_ERROR) - pop_params(); - - /* now check for internal commands */ - /* placed here to allow also aliases starting with "#" */ - } else if (*(end = skipspace(line)) == '#') { - - if (*(end = skipspace(end + 1)) == '(') { /* execute #() */ - end++; - (void)evaln(&end); - if (REAL_ERROR) print_error(error); - } else - parse_commands(buf + 1, arg); - /* ok, buf contains skipspace(first word) */ - } else if (!oneword || !map_walk(buf, silent, 0)) { - /* it is ok, map_walk accepts only one word */ - - if (!subs && !jit_subs) - unescape(line); - send_line(line, silent); - } - } - } while (0); - - if (otcp_fd != -1) - tcp_fd = otcp_fd; - DROP_PTR(pbuf); DROP_PTR(pbusy); - return !REAL_ERROR ? ret : NULL; -} - -/* - * parse input from user: calls parse_instruction for each instruction - * in cmd_line. - * silent = 1 if the line should not be echoed, 0 otherwise. - */ -void parse_user_input(char *line, char silent) -{ - do { - line = parse_instruction(line, silent, 0, 0); - } while (!error && line && *line); -} - -/* - * parse powwow's own commands - */ -static void parse_commands(char *command, char *arg) -{ - int i, j; - cmdstruct *c; - - /* We ALLOW special commands also on subsidiary connections ! */ - - /* assume output will be enough to make input line = last screen line */ - /* line0 = lines - 1; */ - if (isdigit(*command) && (i = atoi(command))) { - if (i >= 0) { - while (i--) - (void)parse_instruction(arg, 1, 0, 1); - } else { - PRINTF("#bogus repeat count\n"); - } - } else { - j = strlen(command); - - if( j == 0 ) { - /* comment */ - return; - } - - for( c = commands; c != NULL; c = c -> next ) - if (!strncmp(command, c -> name, j)) { - if (c -> funct) { - (*(c -> funct))(arg); - return; - } - } - - PRINTF("#unknown powwow command \"%s\"\n", command); - } -} - -/* - * substitute $0..$9 and @0..@9 in a string - * (unless $ or @ is escaped with backslash) - * - * return 0 if dst not filled. if returned 0 and not error, - * there was nothing to substitute. - */ -static int subst_param(ptr *buf, char *src) -{ - int done, i; - char *dst, *tmp, kind; - - if (!strchr(src, '$') && !strchr(src, '@')) - return 0; - - i = strlen(src); - if (!*buf || ptrlen(*buf) < i) { - *buf = ptrsetlen(*buf, i); - if (REAL_ERROR) - return 0; - } - dst = ptrdata(*buf); - - while (*src) { - while (*src && *src != '$' && *src != '@' && *src != ESC) - *dst++ = *src++; - - if (*src == ESC) { - while (*src == ESC) - *dst++ = *src++; - - if (*src) - *dst++ = *src++; - } - - done = 0; - if (*src == '$' || *src == '@') { - kind = *src == '$' ? 1 : 0; - tmp = src + 1; - if (isdigit(*tmp)) { - i = atoi(tmp); - while (isdigit(*tmp)) - tmp++; - - if (i < NUMPARAM) { - int max = 0, n; - char *data = NULL, buf2[LONGLEN]; - - done = 1; - src = tmp; - - /* now the actual substitution */ - if (kind) { - if (*VAR[i].str && (data = ptrdata(*VAR[i].str))) - max = ptrlen(*VAR[i].str); - } else { - sprintf(data = buf2, "%ld", *VAR[i].num); - max = strlen(buf2); - } - if (data && max) { - n = dst - ptrdata(*buf); - *buf = ptrpad(*buf, max); - if (REAL_ERROR) - return 0; - dst = ptrdata(*buf) + n; - memcpy(dst, data, max); - dst += max; - } - } - } - } - if (!done && (*src == '$' || *src == '@')) - *dst++ = *src++; - } - *dst = '\0'; - return 1; -} - -/* - * just-in-time substitution: - * substitute ${name}, @{name} and #{expression} in a string - * (unless "${", "@{" or "#{" are escaped with backslash) - * - * return 0 if dst not filled. if returned 0 and not error, - * there was nothing to substitute. - */ - -static int jit_subst_vars(ptr *buf, char *src) -{ - int i, done, kind; - char *tmp, *name, *dst, c; - varnode *named_var; - - if (!strstr(src, "${") && !strstr(src, "@{") && !strstr(src, "#{")) - return 0; - - i = strlen(src); - if (!*buf || ptrlen(*buf) < i) { - *buf = ptrsetlen(*buf, i); - if (REAL_ERROR) - return 0; - } - dst = ptrdata(*buf); - - while (*src) { - while (*src && *src != '$' && *src != '@' && *src != '#' && *src != ESC) - *dst++ = *src++; - - if (*src == ESC) { - while (*src == ESC) - *dst++ = *src++; - - if (*src) - *dst++ = *src++; - } - - done = 0; - if (*src == '$' || *src == '@') { - i = 0; - kind = *src == '$' ? 1 : 0; - tmp = src + 1; - if (*tmp == '{') { - tmp = skipspace(tmp+1); - if (isdigit(*tmp) || *tmp == '-') { - /* numbered variable */ - i = atoi(tmp); - if (i >= -NUMVAR && i < NUMPARAM) { - if (*tmp == '-') - tmp++; - while (isdigit(*tmp)) - tmp++; - done = 1; - } - } else if (isalpha(*tmp) || *tmp == '_') { - /* named variable */ - name = tmp++; - while (isalnum(*tmp) || *tmp == '_') - tmp++; - c = *tmp; - *tmp = '\0'; - named_var = *lookup_varnode(name, kind); - *tmp = c; - if (named_var) { - i = named_var->index; - done = 1; - } - } - tmp = skipspace(tmp); - if (done) { - int max = 0, n; - char *data = NULL, buf2[LONGLEN]; - - src = tmp + 1; /* skip the '}' */ - - /* now the actual substitution */ - if (kind == 1) { - if (*VAR[i].str && (data = ptrdata(*VAR[i].str))) - max = ptrlen(*VAR[i].str); - } else { - sprintf(data = buf2, "%ld", *VAR[i].num); - max = strlen(buf2); - } - if (data && max) { - n = dst - ptrdata(*buf); - *buf = ptrpad(*buf, max); - if (REAL_ERROR) - return 0; - dst = ptrdata(*buf) + n; - memcpy(dst, data, max); - dst += max; - } - } else if (*tmp == '}') - /* met an undefined variable, consider empty */ - src = tmp + 1; - - /* else syntax error, do nothing */ - } - } else if (src[0] == '#' && src[1] == '{') { - int max, n; - ptr pbuf = (ptr)0; - - src += 2; - (void)evalp(&pbuf, &src); - if (REAL_ERROR) { - ptrdel(pbuf); - return 0; - } - if (pbuf) { - max = ptrlen(pbuf); - n = dst - ptrdata(*buf); - *buf = ptrpad(*buf, max); - if (REAL_ERROR) { - ptrdel(pbuf); - return 0; - } - dst = ptrdata(*buf) + n; - memcpy(dst, ptrdata(pbuf), max); - dst += max; - } - ptrdel(pbuf); - - if (*src) - src = skipspace(src); - if (*src != '}') { - PRINTF("#{}: "); - print_error(error=MISSING_PAREN_ERROR); - return 0; - } - done = 1; - if (*src) - src++; - } - - if (!done && (*src == '$' || *src == '@' || *src == '#')) - /* not matched, just copy */ - *dst++ = *src++; - } - *dst = '\0'; - ptrtrunc(*buf, dst - ptrdata(*buf)); - return 1; -} - -/* - * set definition file: - * rules: if powwow_dir is set it is searched first. - * if file doesn't exist, it is created there. - * If a slash appears in the name, the powwow_dir isn't used. - */ -void set_deffile(char *arg) -{ - if (!strchr(arg, '/') && *powwow_dir) { - strcpy(deffile, powwow_dir); - strcat(deffile, arg); - if ((access(deffile, R_OK) == -1 || access(deffile, W_OK) == -1) - && !access(arg,R_OK) && !access(arg, W_OK)) - strcpy(deffile, arg); - } else - strcpy(deffile, arg); -} - -/* - * GH: return true if var is one of the permanent variables - */ -int is_permanent_variable(varnode *v) -{ - return (v == prompt || v == last_line); -} |