/*********************************************************************/
/* file: main.c - main module - signal setup/shutdown etc */
/* TINTIN++ */
/* (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t */
/* coded by peter unold 1992 */
/*********************************************************************/
#include <string.h>
#include <signal.h>
#include "tintin.h"
#include <stdlib.h>
#if IRIX
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#endif
#ifndef BADSIG
#define BADSIG (void (*)())-1
#endif
/*************** globals ******************/
int term_echoing=TRUE;
int echo=DEFAULT_ECHO;
int speedwalk=DEFAULT_SPEEDWALK;
int togglesubs=DEFAULT_TOGGLESUBS;
int presub=DEFAULT_PRESUB;
int redraw=DEFAULT_REDRAW;
int sessionsstarted;
int puts_echoing=TRUE;
int verbose=FALSE;
int alnum=0;
int acnum=0;
int subnum=0;
int varnum=0;
int hinum=0;
int pdnum=0;
int antisubnum=0;
int verbatim=0;
char homepath[1025];
char E=27;
struct session *sessionlist, *activesession;
struct listnode *common_aliases, *common_actions, *common_subs, *common_myvars;
struct listnode *common_highs, *common_antisubs, *common_pathdirs;
char vars[10][BUFFER_SIZE]; /* the %0, %1, %2,....%9 variables */
char tintin_char=DEFAULT_TINTIN_CHAR;
char verbatim_char=DEFAULT_VERBATIM_CHAR;
char system_com[80]=SYSTEM_COMMAND_DEFAULT;
int mesvar[7];
int display_row, display_col, input_row, input_col;
int split_line, term_columns;
char k_input[BUFFER_SIZE];
char done_input[BUFFER_SIZE], prev_command[BUFFER_SIZE];
int hist_num;
int is_split;
int text_came;
void tintin();
void read_mud();
void do_one_line();
void snoop();
void tintin_puts2();
int interpret_buffer();
/************ externs *************/
extern int ticker_interrupted, time0;
extern int tick_size, sec_to_tick;
extern void check_all_actions(); /* new */
static void myquitsig();
static void tick_func();
extern struct session *newactive_session();
extern struct session *parse_input();
extern struct session *read_command();
extern struct completenode *complete_head;
extern struct listnode *init_list();
extern void term_noecho();
extern void read_complete();
extern void syserr();
extern int time();
extern void alarm();
extern int do_one_antisub();
extern void do_one_sub();
extern void do_one_high();
extern void prompt();
int read();
int select();
extern void do_history();
extern int read_buffer_mud();
extern void cleanup_session();
int write();
/* int fwrite(); */
/* int sscanf(); */
extern void term_echo();
int last_line_length;
#if defined(SYSV)
#include <termio.h>
unsigned char c_cc[NCC];
unsigned short c_lflag;
#endif
/* CHANGED to get rid of double-echoing bug when tintin++ gets suspended */
void tstphandler(sig, code, scp, addr)
int sig;
int code;
struct sigcontext *scp;
char *addr;
{
ticker_interrupted=TRUE;
#if defined(SYSV)
alarm(0);
#endif
term_echo();
kill(getpid(), SIGSTOP);
#if defined(SYSV)
alarm(1);
#endif
term_noecho();
}
/**************************************************************************/
/* main() - show title - setup signals - init lists - readcoms - tintin() */
/**************************************************************************/
void main(argc, argv, environ)
int argc;
char **argv;
char **environ;
{
struct session *ses;
char *strptr, temp[BUFFER_SIZE];
int arg_num;
#if defined(SYSV)
init_echo();
#endif
term_noecho();
is_split=FALSE;
text_came=FALSE;
read_complete();
*k_input='\0';
hist_num= -1;
ses=NULL;
tintin_puts2("##################################################", ses);
sprintf(temp,"# T I N T I N + + %12s #", VERSION_NUM);
tintin_puts2(temp, ses);
tintin_puts2("# (T)he k(I)cki(N) (T)ickin d(I)kumud clie(N)t #", ses);
tintin_puts2("# a DIKU-mud client #", ses);
tintin_puts2("# new code by Bill Reiss, David A. Wagner #", ses);
tintin_puts2("# Joann Ellsworth, Jeremy C. Jack 1994 #", ses);
tintin_puts2("# thanks to Peter Unold for original TINTIN code #", ses);
tintin_puts2("##################################################", ses);
if(signal(SIGTERM, myquitsig)==BADSIG)
syserr("signal SIGTERM");
if(signal(SIGINT, myquitsig)==BADSIG)
syserr("signal SIGINT");
if(signal(SIGALRM, tick_func)==BADSIG)
syserr("signal SIGALRM");
/* CHANGED to get rid of double-echoing bug when tintin++ gets suspended */
if(signal(SIGTSTP, tstphandler)==BADSIG)
syserr("signal SIGTSTP");
time0=time(NULL);
#if defined(DEBUG)
alarm(0);
#else
alarm(1);
#endif
common_aliases=init_list();
common_actions=init_list();
common_subs=init_list();
common_myvars=init_list();
common_highs=init_list();
common_antisubs=init_list();
common_pathdirs=init_list();
mesvar[0]=DEFAULT_ALIAS_MESS;
mesvar[1]=DEFAULT_ACTION_MESS;
mesvar[2]=DEFAULT_SUB_MESS;
mesvar[3]=DEFAULT_ANTISUB_MESS;
mesvar[4]=DEFAULT_HIGHLIGHT_MESS;
mesvar[5]=DEFAULT_VARIABLE_MESS;
mesvar[6]=DEFAULT_PATHDIR_MESS;
*homepath='\0';
if (!strcmp(DEFAULT_FILE_DIR, "HOME"))
if (strptr = getenv("HOME"))
strcpy(homepath, strptr);
else *homepath = '\0';
else strcpy(homepath, DEFAULT_FILE_DIR);
arg_num=1;
if(argc > 1 && argv[1]) {
if (*argv[1]=='-' && *(argv[1]+1)=='v') {
arg_num=2;
verbose=TRUE;
}
}
if(argc > arg_num && argv[arg_num]) {
activesession=read_command(argv[arg_num], NULL);
}
else {
strcpy(temp,homepath);
strcat(temp,"/.tintinrc");
activesession=read_command(temp, NULL);
}
tintin();
}
/***************************/
/* the main loop of tintin */
/***************************/
void tintin()
{
char buffer[BUFFER_SIZE], strng[80];
int didget;
int readfdmask, done;
struct session *sesptr;
*k_input='\0';
while(TRUE) {
readfdmask=1;
for(sesptr=sessionlist; sesptr; sesptr=sesptr->next)
readfdmask|=sesptr->socketbit;
ticker_interrupted=FALSE;
if(select(32, &readfdmask, 0, 0, 0)<0 && !ticker_interrupted)
syserr("select");
if(ticker_interrupted)
ticker_interrupted=FALSE;
else {
if(readfdmask&1) {
if((didget=read(0, buffer, sizeof(buffer)))<0)
syserr("read from fd 0");
*(buffer+didget)='\0';
done=interpret_buffer(buffer, activesession);
if (done) {
hist_num= -1;
if (term_echoing) {
if (activesession && *done_input)
if (strcmp(done_input, prev_command))
do_history(done_input,activesession);
if (is_split)
tintin_puts2(done_input, activesession);
else
tintin_puts2("", activesession);
}
if (*done_input)
strcpy(prev_command, done_input);
if (is_split) {
sprintf(strng, "[%d;1f[0J", split_line+1);
write(1, strng, strlen(strng));
}
activesession=parse_input(done_input, activesession);
}
}
for(sesptr=sessionlist; sesptr; sesptr=sesptr->next)
if(sesptr->socketbit&readfdmask) {
read_mud(sesptr);
}
}
}
}
/***************************************************************************/
/* new for 1.2...since we're in raw mode, we have control over the keys */
/* that people press as they are pressed. This routine parses that input */
/* and responds accordingly */
/***************************************************************************/
int interpret_buffer(buffer, ses)
char *buffer;
struct session *ses;
{
char *tptr, *tptr2, *cptr, *cptr2, strng[BUFFER_SIZE];
struct completenode *tcomp;
int flag, counter;
tptr=buffer;
tptr2=buffer;
if (!*k_input)
text_came=FALSE;
while (*tptr) {
/* return was pressed, get ready to send line */
if (*tptr=='\n' || *tptr=='\r') {
text_came=FALSE;
input_row=split_line+1;
input_col=1;
strcpy(done_input, k_input);
*k_input='\0';
return 1;
}
/* this is backspace (and delete is mapped onto it too) */
else if (*tptr==127 || *tptr==8) {
*tptr='\0';
if (strlen(k_input)!=0) {
if (!is_split && text_came) {
sprintf(strng, "\n\r%s", k_input);
write(1,strng,strlen(strng));
}
text_came=FALSE;
if (input_col>2 || !is_split || input_row == split_line+1) {
input_col--;
sprintf(strng, "\b \b");
}
else if (is_split) {
input_row--;
input_col=SCREEN_WIDTH;
sprintf(strng, "\b \b%c[%d;%df", E, input_row, input_col);
}
if(term_echoing) write(1, strng, strlen(strng));
*(k_input+strlen(k_input)-1)='\0';
}
tptr2=tptr+1;
}
else if (*tptr==18) {
if (!is_split) {
sprintf(strng, "\n\r");
write(1, strng, strlen(strng));
write(1, k_input, strlen(k_input));
text_came=FALSE;
}
}
/* ^D */
else if (*tptr==4) {
strcpy(k_input, prev_command);
if (is_split) {
sprintf(strng, "%c[%d;1f%c[0J", E, split_line+1, E);
write(1, strng, strlen(strng));
input_row=split_line+1;
input_col=strlen(prev_command);
cptr2=k_input;
while (input_col+strlen(cptr2) >= SCREEN_WIDTH) {
counter=SCREEN_WIDTH-input_col;
write(1, cptr2, counter);
input_row++;
input_col=1;
sprintf(strng, "%c[%d;%df", E, input_row, input_col);
write(1, strng, strlen(strng));
cptr2+=counter;
}
input_col=strlen(cptr2)+1;
write(1, cptr2, strlen(cptr2));
}
else {
sprintf(strng, "\n%s", k_input);
write(1, strng, strlen(strng));
}
}
/* ^U */
else if (*tptr==21) {
if (is_split) {
sprintf(strng, "%c[%d;1f%c[0J", E, split_line+1, E);
write(1, strng, strlen(strng));
input_row=split_line+1;
input_col=1;
}
else {
sprintf(strng, "\n");
write(1, strng, strlen(strng));
}
*k_input=0;
}
/* this is history scroll, I still have some work to do on this one */
else if (*tptr==16) {
flag=TRUE;
*tptr='\0';
if (ses) {
if (hist_num<HISTORY_SIZE-1) {
if (ses->history[hist_num+1]) {
hist_num++;
strcpy(k_input, ses->history[hist_num]);
if (is_split) {
input_row=split_line+1;
input_col=1;
sprintf(strng, "%c[%d;1f%c[0J", E, split_line+1, E);
write(1, strng, strlen(strng));
cptr2=ses->history[hist_num];
while (input_col+strlen(cptr2) >= SCREEN_WIDTH) {
counter=SCREEN_WIDTH-input_col;
write(1, cptr2, counter);
input_row++;
input_col=1;
sprintf(strng, "%c[%d;%df", E, input_row, input_col);
write(1, strng, strlen(strng));
cptr2+=counter;
}
input_col=strlen(cptr2)+1;
write(1, cptr2, strlen(cptr2));
}
else {
sprintf(strng, "\n\r");
write(1, strng, strlen(strng));
write(1, ses->history[hist_num], strlen(ses->history[hist_num]));
}
flag=FALSE;
}
}
}
if (!flag)
return 0;
tptr2=tptr+1;
}
/* also history scroll */
else if (*tptr==14) {
flag=TRUE;
*tptr='\0';
if (ses) {
if (hist_num>0) {
if (ses->history[hist_num-1]) {
hist_num--;
strcpy(k_input, ses->history[hist_num]);
if (is_split) {
input_row=split_line+1;
input_col=1;
sprintf(strng, "%c[%d;1f%c[0J", E, split_line+1, E);
write(1, strng, strlen(strng));
cptr2=ses->history[hist_num];
while (input_col+strlen(cptr2) >= SCREEN_WIDTH) {
counter=SCREEN_WIDTH-input_col;
write(1, cptr2, counter);
input_row++;
input_col=1;
sprintf(strng, "%c[%d;%df", E, input_row, input_col);
write(1, strng, strlen(strng));
cptr2+=counter;
}
input_col=strlen(cptr2)+1;
write(1, cptr2, strlen(cptr2));
}
else {
sprintf(strng, "\n\r");
write(1, strng, strlen(strng));
write(1, ses->history[hist_num], strlen(ses->history[hist_num]));
}
flag=FALSE;
}
}
else if (hist_num==0) {
hist_num--;
strcpy(k_input, "");
if (is_split) {
sprintf(strng, "%c[%d;1f%c[0J", E, split_line+1, E);
write(1,strng,strlen(strng));
}
else tintin_puts2("", ses);
flag=FALSE;
}
}
if (!flag)
return 0;
tptr2=tptr+1;
}
/* tab key pressed, this is tab completion */
else if (*tptr==9) {
*tptr='\0';
cptr=k_input+strlen(k_input)-1;
counter=0;
while (*cptr!=' ' && cptr!=k_input) {
cptr--;
counter++;
}
if (*cptr==' ')
cptr++;
tcomp=complete_head;
flag=TRUE;
while ((tcomp=tcomp->next) && flag) {
if (!strncmp(tcomp->strng, cptr, strlen(cptr))) {
cptr2=tcomp->strng+strlen(cptr);
strcat(k_input, cptr2);
if (input_col+strlen(cptr2) >= SCREEN_WIDTH) {
while (input_col+strlen(cptr2) >= SCREEN_WIDTH) {
counter=SCREEN_WIDTH-input_col;
write(1, cptr2, counter);
input_row++;
input_col=1;
sprintf(strng, "%c[%d;%df", E, input_row, input_col);
write(1, strng, strlen(strng));
cptr2+=counter;
}
input_col=strlen(cptr2)+1;
write(1, cptr2, strlen(cptr2));
}
else {
write(1, cptr2, strlen(cptr2));
input_col+=strlen(cptr2);
}
flag=FALSE;
}
}
tptr2=tptr+1;
}
else {
if (*tptr==27)
*tptr='^';
if (input_col>=SCREEN_WIDTH && is_split && term_echoing) {
input_row++;
input_col=1;
sprintf(strng, "%c[%d;1f", E, input_row);
write(1, strng, strlen(strng));
}
if (term_echoing) {
write(1, tptr, 1);
display_col++;
}
input_col++;
*(k_input+strlen(k_input)+1)='\0';
*(k_input+strlen(k_input))= *tptr;
}
tptr++;
}
return 0;
}
/*************************************************************/
/* read text from mud and test for actions/snoop/substitutes */
/*************************************************************/
void read_mud(ses)
struct session *ses;
{
char buffer[BUFFER_SIZE], linebuffer[BUFFER_SIZE], *cpsource, *cpdest;
char temp[BUFFER_SIZE];
char *result2, result[BUFFER_SIZE];
int didget,n, length, count;
if(!(didget=read_buffer_mud(buffer, ses))) {
cleanup_session(ses);
if(ses==activesession)
activesession=newactive_session();
}
else {
if(ses->logfile) {
if (!OLD_LOG) {
count=0;
for(n=0;n<=didget;n++)
if (buffer[n]!='\r') {
temp[count]=buffer[n];
count++;
}
fwrite(temp, count, 1, ses->logfile);
} /* then */
else
fwrite(buffer, didget, 1, ses->logfile);
}
cpsource=buffer;
cpdest=linebuffer;
if (ses->old_more_coming==1) {
strcpy(linebuffer,ses->last_line);
cpdest+=strlen(linebuffer);
}
result2=result;
if (is_split) {
sprintf(result, "%c8", E);
result2+=strlen(result);
}
length=0;
while(*cpsource) { /*cut out each of the lines and process*/
length++;
if(*cpsource=='\n' || *cpsource=='\r') {
length=0;
*cpdest='\0';
do_one_line(linebuffer, ses);
if(!(*linebuffer=='.' && !*(linebuffer+1))) {
n=strlen(linebuffer);
memcpy(result2, linebuffer, n);
result2+=n;
*result2++= *cpsource++;
if(*cpsource=='\n' || *cpsource=='\r')
*result2++= *cpsource++;
}
else
if(*++cpsource=='\n' || *cpsource=='\r')
cpsource++;
cpdest=linebuffer;
}
else
*cpdest++= *cpsource++;
}
*cpdest='\0';
if (ses->more_coming==1) {
strcpy(ses->last_line,linebuffer);
length=0;
}
else {
do_one_line(linebuffer,ses);
n=strlen(linebuffer);
memcpy(result2, linebuffer, n);
result2+=n;
}
*result2='\0';
if (ses==activesession) {
if (strlen(result))
text_came=TRUE;
if (is_split) {
sprintf(temp, "%c7%c[%d;%df", E, E, input_row, input_col);
strcat(result, temp);
}
write(1, result,strlen(result));
display_col=length+1;
}
else if (ses->snoopstatus) {
if (strlen(result))
text_came=TRUE;
snoop(result,ses);
}
if (!is_split && redraw && text_came==TRUE && term_echoing) {
write(1, k_input, strlen(k_input));
text_came=FALSE;
display_col=strlen(k_input);
}
}
}
/**********************************************************/
/* do all of the functions to one line of buffer */
/**********************************************************/
void do_one_line(line, ses)
char *line;
struct session *ses;
{
if (!presub && !ses->ignore)
check_all_actions(line,ses);
if (!togglesubs)
if(!do_one_antisub(line,ses))
do_one_sub(line,ses);
if (presub && !ses->ignore)
check_all_actions(line,ses);
do_one_high(line, ses);
}
/**********************************************************/
/* snoop session ses - chop up lines and put'em in buffer */
/**********************************************************/
void snoop(buffer, ses)
char *buffer;
struct session *ses;
{
/* int n; */
char *cpsource, *cpdest, line[BUFFER_SIZE], linebuffer[BUFFER_SIZE],
header[BUFFER_SIZE];
*linebuffer='\0';
if (is_split)
sprintf(linebuffer, "%c8", E);
if (display_col!=1) {
sprintf(line, "\n\r");
strcat(linebuffer, line);
}
cpsource=buffer;
sprintf(header,"%s%% ", ses->name);
strcpy(line,header);
cpdest=line+strlen(line);
while(*cpsource) {
if(*cpsource=='\n' || *cpsource=='\r') {
*cpdest++= *cpsource++;
if(*cpsource=='\n' || *cpsource=='\r')
*cpdest++= *cpsource++;
*cpdest='\0';
strcat(linebuffer,line);
cpdest=line+strlen(header);
}
else
*cpdest++= *cpsource++;
}
if (cpdest != line+strlen(header)) {
*cpdest++='\r';
*cpdest++='\n';
*cpdest='\0';
strcat(linebuffer,line);
}
if (is_split) {
sprintf(line,"%c7%c[%d;%df", E, E, input_row, input_col);
strcat(linebuffer, line);
}
write(1,linebuffer,strlen(linebuffer));
display_col=1;
}
/*****************************************************/
/* output to screen should go throught this function */
/* text gets checked for actions */
/*****************************************************/
void tintin_puts(cptr, ses)
char *cptr;
struct session *ses;
{
tintin_puts2(cptr,ses);
if(ses)
check_all_actions(cptr, ses);
}
/*****************************************************/
/* output to screen should go throught this function */
/* not checked for actions */
/*****************************************************/
void tintin_puts2(cptr, ses)
char *cptr;
struct session *ses;
{
char strng[1024];
if((ses==activesession || ses==NULL) && puts_echoing) {
if (is_split)
sprintf(strng,"%c8%s\n\r%c7%c[%d;%df", E, cptr, E, E, input_row, input_col);
else
sprintf(strng,"%s\n\r", cptr);
write(1,strng, strlen(strng)+1);
display_col=1;
if (redraw && term_echoing && !is_split)
write(1, k_input, strlen(k_input));
text_came=TRUE;
}
}
/*****************************************************/
/* output to screen should go throught this function */
/* not checked for actions */
/*****************************************************/
void tintin_puts3(cptr, ses)
char *cptr;
struct session *ses;
{
char strng[1024];
if((ses==activesession || ses==NULL) && puts_echoing) {
if (is_split) {
if (*cptr=='^') {
cptr++;
sprintf(strng,"%c8%s\n\r%c7", E, cptr, E);
}
else {
cptr++;
sprintf(strng,"%s\n\r%c7%c[%d;%df", cptr, E, E, input_row, input_col);
}
}
else {
cptr++;
sprintf(strng,"%s\n\r", cptr);
}
write(1,strng, strlen(strng)+1);
display_col=1;
}
text_came=TRUE;
}
/****************************************/
/* alarm signal handler used for ticker */
/****************************************/
static void tick_func()
{
ticker_interrupted=TRUE;
#if defined(DEBUG)
alarm(0);
#else
alarm(1);
#endif
if(signal(SIGALRM, tick_func)==BADSIG)
syserr("signal SIGALRM");
sec_to_tick=tick_size-((time(NULL)-time0)%tick_size);
if(sec_to_tick==tick_size || sec_to_tick==10) {
struct session *sesptr;
for(sesptr=sessionlist; sesptr; sesptr=sesptr->next)
if(sesptr->tickstatus)
if(sec_to_tick==tick_size)
tintin_puts("#TICK!!!", sesptr);
else
tintin_puts("#10 SECONDS TO TICK!!!", sesptr);
}
}
/**********************************************************/
/* Here's where we go when we wanna quit TINTIN FAAAAAAST */
/**********************************************************/
static void myquitsig()
{
struct session *sesptr;
char strng[BUFFER_SIZE];
for(sesptr=sessionlist; sesptr; sesptr=sesptr->next)
cleanup_session(sesptr);
sesptr=NULL;
sprintf(strng, "%cc%c[2J", E, E);
if (is_split)
write(1, strng, strlen(strng));
printf("\n\rYour fireball hits TINTIN with full force, causing an immediate death.\n\r");
printf("TINTIN is dead! R.I.P.\n\r");
printf("Your blood freezes as you hear TINTIN's death cry.\n\r");
term_echo();
exit(0);
}
void split_command(arg)
char *arg;
{
int temp;
char fn[BUFFER_SIZE];
sscanf(arg, "%d", &temp);
if (temp>0)
split_line=temp;
else
split_line=21;
display_row=split_line-1;
input_row=split_line+1;
input_col=1;
sprintf(fn, "%c[2J%c[1;%dr%c[%d;1f", E, E, display_row, E, split_line);
write(1,fn, strlen(fn));
write(1,"-------------------------------------------------------------------------------", 80);
sprintf(fn, "%c[%d;1f%c7", E, split_line-1, E);
write(1,fn,strlen(fn));
sprintf(fn, "%c[%d;1f", E, input_row);
write(1,fn, strlen(fn));
is_split=TRUE;
tintin_puts2("#Ok, split screen activated.", (struct session *)NULL);
prompt(NULL);
}
void unsplit_command()
{
char temp[15];
sprintf(temp, "%cc%c[2J", E, E);
write(1, temp, strlen(temp));
is_split=FALSE;
prompt(NULL);
}