/* New for v2.0: readline support -- daw */ /* this is the main bunch of code for readline; lots of misc stuff here */ #ifdef HAVE_STRING_H #include <string.h> #else #ifdef HAVE_STRINGS_H #include <strings.h> #endif #endif #include <sys/types.h> #include <sys/time.h> #include <stdio.h> #include <errno.h> #include <readline/readline.h> #include "tintin.h" /* this gets set when user hits control-z, so we don't worry about EINTR */ int ignore_interrupt; /* should we display the user's commands in the top window, too? */ int am_purist = DEFAULT_PURIST_MODE; /* the user requested this screen size -- kill autodetection code if set */ int requested_scrsize = -1; /* essentially the size of the screen; only for split mode */ static int splitline, cols; extern void scrsize(); /* what column we were at in the top window; only for split mode */ static int topwin_col = 0; #define ISAPROMPT(x) (strchr((x), '>')) /* some readline stuff */ int (*rl_event_hook)(); int (*rl_completion_entry_function)(); extern char E; /* @@@added -- perry */ extern int _rl_last_c_pos; extern int _rl_eof_char; extern int readline_echoing_p; extern int history_expand(); extern int errno; extern struct session *sessionlist, *activesession; extern int is_split; extern int redraw; extern int puts_echoing; extern struct session *parse_input(); extern struct session *newactive_session(); extern char *rltab_generate(); extern char *rlhist_expand(); void tintin_puts(); extern char *get_arg_in_braces(); static void ctrl_r_redraw(count, key) int count; int key; { if (is_split) { goto_rowcol(splitline+1, 0); erase_toeol(); } else printf("\n"); fflush(stdout); _rl_last_c_pos = 0; rl_forced_update_display(); } static void readmud(); /* for use with rl_event_hook */ static void bait(/* void */) { fd_set readfds; struct session *s, *t; struct timeval to; int rv, maybe_redraw; FD_ZERO(&readfds); while (!FD_ISSET(0, &readfds)) { FD_ZERO(&readfds); FD_SET(0, &readfds); /* stdin */ for (s=sessionlist; s; s=s->next) FD_SET(s->socket, &readfds); to.tv_sec = checktick(); /* 100 */ to.tv_usec = 0; ignore_interrupt = 0; rv = select(FD_SETSIZE, &readfds, NULL, NULL, &to); if (rv == 0) continue; /* timeout */ else if (rv < 0 && errno == EINTR && ignore_interrupt) continue; /* don't worry, be happy */ else if (rv < 0) syserr("select"); maybe_redraw = 0; for (s=sessionlist; s; s=t) { t = s->next; /* s may be free()d out from under us */ if (FD_ISSET(s->socket, &readfds)) { readmud(s); maybe_redraw = 1; } } if (maybe_redraw && redraw && !is_split) ctrl_r_redraw(); } } void initrl(/* void */) { rl_readline_name = "tintin++"; rl_completion_entry_function = (int (*)()) rltab_generate; using_history(); stifle_history(HISTORY_SIZE); rl_variable_bind("horizontal-scroll-mode", "on"); rl_bind_key('\022', ctrl_r_redraw); /* control-r */ rl_bind_key_in_map('\022', ctrl_r_redraw, vi_movement_keymap); rl_bind_key_in_map('\022', ctrl_r_redraw, vi_insertion_keymap); rl_event_hook = (int (*)()) bait; /* * a hack to let ^d work like it used to in old tt++: * by default, readline won't let you rebind the eof * character when it's at the start of the line, so * let's pull the wool over readline. yeeeeeehaw! * bug: remember to undo the stty! check portability! */ _rl_eof_char = '\001'; /* control-b : something innocuous */ system("stty eof '^b'"); rl_bind_key('\004', rl_get_previous_history); /* control-d */ rl_bind_key_in_map('\004', rl_get_previous_history, vi_movement_keymap); rl_bind_key_in_map('\004', rl_get_previous_history,vi_insertion_keymap); } /* turn on split mode */ void initsplit(arg) int arg; /* @@@added -- perry */ { char fn[256]; char *p; /* notice the screen size and remember it */ scrsize(&splitline, &cols); if (requested_scrsize > 0) splitline = requested_scrsize; splitline--; is_split = 1; topwin_col = 0; reset(); erase_screen(); scroll_region(1, splitline-1); goto_rowcol(splitline, 0); /* @@@changed -- perry */ /* bug: sometimes this isn't called after a change in activesession, */ /* and the split bar has an incorrect session. */ if (arg && activesession) { sprintf(fn, "%c[7;37m%-9s%%-%ds%20s%c[0m", E, "Session: ", cols - 29, "Tintin" VERSION_NUM, E); p = activesession->name; } else { sprintf(fn, "%c[7;37m%%-%ds%20s%c[0m", E, cols - 20, "Tintin" VERSION_NUM, E); p = "No Session active"; } printf(fn, p); goto_rowcol(splitline+1, 0); save_pos(); fflush(stdout); } /* * ahh, gotta love coding around hardware bugs :-( * [this beauty does linewrapping for terminals which * are too dumb to do it themselves -- only needed * for split mode, i think.] * * 's' better not contain any \n or \r's. * if 'isaprompt' is non-zero, then we won't tack * a \n to the end of 's', and try to remember that. * * bug: this doesn't work right for muds that send ansi * escape sequences. (yes, servers that do that are * arguably broken, but unfortunately, I don't get a * chance to argue). perhaps the linewrapping should * be disabled. <sigh> */ /* @@@changed -- perry (non-static) */ void printline(s, isaprompt) char *s; { int i, n = cols - topwin_col; if (is_split) for (i=strlen(s); i > n; i-=n, s+=n, n=cols) { printf("%.*s\n", n, s); topwin_col = 0; } if (isaprompt && is_split) { topwin_col += strlen(s) + 1; printf("%s", s); /* don't append \n */ } else { topwin_col = 0; puts(s); } } void mainloop(/* void */) { char *line; initrl(); for (;;) { line = readline(""); if (line == NULL) syserr("readline() == NULL"); line = rlhist_expand(line); if (line == NULL) continue; if (is_split) goto_rowcol(splitline-1, topwin_col); /* commands will echo to the top screen, except for purists */ if (is_split) { if (!am_purist) printline(line, 0); else { /* should I echo newlines after prompts? */ /* printline("", 0); */ ; } } activesession = parse_input(line, activesession); if (is_split) { goto_rowcol(splitline+1, 0); erase_toeol(); fflush(stdout); } free(line); } } /* an uncalled noop -- for now */ static void see_prompt(b, l) char *b; int l; { /* soon this will try to recognize prompts and process them */ /* maybe use IACGA */ } /* data waiting on this mud session; read & display it; do the dirty work */ static void readmud(s) struct session *s; { char thebuffer[2*BUFFER_SIZE+1], *buf, *p, *q; char linebuf[BUFFER_SIZE], header[BUFFER_SIZE]; int rv, i, headerlen; buf = thebuffer + BUFFER_SIZE; rv = read_buffer_mud(buf, s); if (rv == 0) { cleanup_session(s); if (s == activesession) activesession = newactive_session(); return; } else if (rv < 0) syserr("readmud: read"); buf[++rv] = '\0'; if (s == activesession) header[0] = '\0'; else if (s->snoopstatus) sprintf(header, "%s%% ", s->name); else return; headerlen = strlen(header); if (s->old_more_coming) { p = s->last_line; buf -= strlen(p); while (*p) *buf++ = *p++; buf -= strlen(s->last_line); s->last_line[0] = '\0'; } logit(s, buf); if (is_split) { save_pos(); goto_rowcol(splitline-1, topwin_col); } /* separate into lines and print away */ for (p=buf; p && *p; p=q) { if ((q = strchr(p, '\n')) == NULL && s->more_coming) break; if (q) { *q++ = '\0'; if (*q == '\r') /* ignore \r's */ q++; } strcpy(linebuf, p); do_one_line(linebuf, s); /* changes linebuf */ if (strcmp(linebuf, ".") != 0) { strcat(header, linebuf); printline(header, (q == NULL) && ISAPROMPT(header)); header[headerlen] = '\0'; } } if (p && *p) strcpy(s->last_line, p); if (is_split) restore_pos(); fflush(stdout); /* q: do we need to do some sort of redraw here? i don't think so */ /* a: no, i do it at the end of mainloop() [for now] */ } /* * output to screen should go through this function * the output is NOT checked for actions or anything */ void tintin_puts2(cptr, ses) char *cptr; struct session *ses; { char *p; if ((ses != activesession && ses != 0) || !puts_echoing) return; if (is_split) { save_pos(); goto_rowcol(splitline-1, topwin_col); } printline(cptr, 0); if (is_split) restore_pos(); fflush(stdout); /* q: do we need to do some sort of redraw here? i don't think so */ /* a: right now, i think so */ if (redraw && !is_split) ctrl_r_redraw(); } /* * output to screen should go through this function * the output IS treated as though it came from the mud */ void tintin_puts(cptr, ses) char *cptr; struct session *ses; { /* bug! doesn't do_one_line() sometimes send output to stdout? */ /* bug: doesn't do_one_line() modify it's input? are we ok here? */ if (ses) do_one_line(cptr, ses); tintin_puts2(cptr, ses); } /* nobody needs tintin_puts3 anymore*/ /* get a clean screen without the split crap; useful for ^Z, quitting, etc */ void cleanscreen(/* void */) { /* i think term_echo() was unportable. try to avoid using echo.c */ /* term_echo(); */ system("stty echo"); /* a hack, admittedly */ /* undo the "stty eof '^b'" from initrl() */ system("stty eof '^d'"); if (!is_split) return; scroll_region(1, splitline+1); erase_screen(); reset(); fflush(stdout); } /* undo cleanscreen(); useful after ^Z */ void dirtyscreen(/* void */) { if (is_split) initsplit(1); /* @@@changed -- perry */ /* put the eof = ^b back; see cleanscreen() and initrl() */ system("stty eof '^b'"); } /* quit tintin++ and print a message */ void quitmsg(m) char *m; { struct session *s; for (s=sessionlist; s; s=s->next) cleanup_session(s); cleanscreen(); if (m) printf("%s\n", m); printf("Goodbye from tintin++.\n"); exit(0); } /* quit tintin++ fast! for use with signal() */ void myquitsig(/* void */) { quitmsg(NULL); } /* * if the user requests a certain screen size, who are we * to autodetect things and ignore his request? :-) */ void split_command(arg, ses) char *arg; struct session *ses; { char left[BUFFER_SIZE]; if (is_split) { cleanscreen(); is_split = 0; } arg = get_arg_in_braces(arg, left, 0); if (!*left) requested_scrsize = -1; else if ((requested_scrsize = atoi(left)) <= 0) requested_scrsize = -1; initsplit(1); /* @@@changed -- perry */ } void unsplit_command(/* void */) { requested_scrsize = -1; cleanscreen(); is_split = 0; } void purist_command(/* void */) { tintin_puts("#Ok, purist mode enabled.", 0); am_purist = 1; } void unpurist_command(/* void */) { tintin_puts("#Ok, purist mode disabled.", 0); am_purist = 0; }