/* 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 statusbar[256];
char *p;
int x = 0;
/* 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 */
if (activesession && arg) {
sprintf(statusbar, "%c[7;37m%-9s%%-%ds%20s%c[0m",
E, "Session: ", cols - 29, "Tintin" VERSION_NUM, E);
p = activesession->name;
x = 1;
}
else {
sprintf(statusbar, "%c[7;37m%%-%ds%20s%c[0m",
E, cols - 20, "Tintin" VERSION_NUM, E);
p = "No Session active";
}
printf(statusbar, 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;
}