/* An ircII-like split-screen front end
Copyright (C) 1995 Roger Espel Llima
Started: 17 Feb 95 by orabidoo <roger.espel.llima@ens.fr>
Latest modification: 7 June 97
To compile: gcc ssfe.c -o ssfe -ltermcap
If it doesn't work, try gcc ssfe.c -o ssfe -lcurses
or try cc, acc or c89 instead of gcc, or -lncurses.
Use: ssfe [options] program arguments
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. See the file LICENSE for details.
*/
#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#ifdef USE_SGTTY
#include <sgtty.h>
#else
#include <termios.h>
#endif
#include <sys/ioctl.h>
#ifdef _AIX
#include <sys/select.h>
#endif
#define BUF_SIZE 512
#define MAX_COLS 512
unsigned char *statusline;
int ystatus, yinput; /* line number of the status line, input line */
int ttyfd;
#ifdef TIOCGWINSZ
struct winsize wsz;
#endif
#ifdef USE_SGTTY
struct sgttyb term, term0;
struct tchars tch, tch0;
struct ltchars lch, lch0;
#else
struct termios term, term0;
#endif
int pid, mypid;
int i;
int cols, lines;
int readfd, writefd, errfd;
unsigned char *t, *w;
unsigned char tmpstr[BUF_SIZE], extrainput[BUF_SIZE+20], readbuf[2*BUF_SIZE],
*input, *writebuf, o_buffer[BUF_SIZE];
int bold=0, inv=0, under=0, wherex=0, wherey=0, donl=0;
int hold_mode=0, hold_lines=0, ctrlx=0, beep=0, flow=0;
unsigned char defprompt[]="> ",
nullstring[]="",
*prompt;
int plen=0, specialprompt=0, modified=1, no_echo=0;
#define MAX_TAB_LINES 20
struct tabinfo {
unsigned char string[BUF_SIZE];
struct tabinfo *prev, *next;
};
int tablines=0;
struct tabinfo *curtabt=NULL, *curtabr=NULL, *oldest=NULL;
#define MAX_HIST_LINES 50
struct histinfo {
unsigned char string[BUF_SIZE+20];
int len, plen;
struct histinfo *prev, *next;
};
int histlines=0;
struct histinfo *histcurrent=NULL, *histoldest=NULL;
char ctrl_t[128] = "/next\n";
unsigned char id[]="`#ssfe#", *inid=id, protcmd[BUF_SIZE], *wpc=protcmd;
int idstatus=0; /* 0 looking for/in the word, 1 in the arguments */
#define ID_BACK "@ssfe@"
int rc, rrc, inputcursor, inputlast, inputofs, inarrow=0, quote=0;
int cursorwhere; /* 0 = up, 1 = down, 2 = undef */
int dispmode=1; /* 0=raw, 1=wordwrap, 2=process ^b^v^_ */
int printmode=0;
int cutline=0;
char *termtype, termcap[1024], *tc, capabilities[2048];
char *t_cm, *t_cl, *t_mr, *t_md, *t_me, *t_cs, *t_ce, *t_us;
int ansi_cs = 0;
fd_set ready, result;
extern int errno;
#ifdef __GNUC__
extern unsigned char *tgoto(unsigned char *cm, int col, int line);
#else
extern unsigned char *tgoto();
#endif
#ifdef __GNUC__
int myputchar(int c) {
#else
int myputchar(c) {
#endif
unsigned char cc=(unsigned char)c;
return(write(1, &cc, 1));
}
#ifdef __GNUC__
int addchar(int c) {
#else
int addchar(c) {
#endif
(*w++)=(unsigned char)c;
}
#ifdef __GNUC__
void putcap(unsigned char *s) {
#else
void putcap(s)
unsigned char *s; {
#endif
tputs(s, 0, myputchar);
}
#ifdef __GNUC__
int do_cs(int y1, int y2) {
#else
int do_cs(y1, y2) {
#endif
static char temp[16];
if (ansi_cs) {
sprintf(temp, "\e[%d;%dr", y1, y2);
write(1, temp, strlen(temp));
} else putcap((char *)tgoto(t_cs, y2-1, y1-1));
}
#ifdef __GNUC__
void writecap(unsigned char *s) {
#else
void writecap(s)
unsigned char *s; {
#endif
tputs(s, 0, addchar);
}
#ifdef __GNUC__
void gotoxy(int x, int y) {
#else
void gotoxy(x, y) {
#endif
/* left upper = 0, 0 */
putcap(tgoto(t_cm, x, y));
}
#define clearscreen() (putcap(t_cl))
#define cleareol() (putcap(t_ce))
#define fullscroll() (do_cs(0, 0))
#define winscroll() (do_cs(1, lines-2))
#define setbold() (putcap(t_md))
#define setunder() (putcap(t_us))
#define setinv() (putcap(t_mr))
#define normal() (putcap(t_me))
#ifdef __GNUC__
void ofsredisplay(int x);
void inschar(unsigned char t);
void dokbdchar(unsigned char t);
#else
void ofsredisplay();
void inschar();
void dokbdchar();
#endif
void displaystatus();
#ifdef __GNUC__
void cleanupexit(int n, unsigned char *error) {
#else
void cleanupexit(n, error)
int n;
unsigned char *error; {
#endif
normal();
fullscroll();
gotoxy(0, lines-1);
cleareol();
#ifdef USE_SGTTY
ioctl(ttyfd, TIOCSETP, &term0);
ioctl(ttyfd, TIOCSETC, &tch0);
ioctl(ttyfd, TIOCSLTC, &lch0);
#else
tcsetattr(ttyfd, TCSADRAIN, &term0);
#endif
close(ttyfd);
if (error!=NULL)
fprintf(stderr, "%s\n", error);
exit(n);
}
void allsigs();
void interrupted() {
cleanupexit(1, "interrupted");
}
void sigpipe() {
cleanupexit(1, "program died");
}
void sigcont() {
allsigs();
#ifdef USE_SGTTY
ioctl(ttyfd, TIOCSETP, &term);
ioctl(ttyfd, TIOCSETC, &tch);
ioctl(ttyfd, TIOCSLTC, &lch);
#else
tcsetattr(ttyfd, TCSANOW, &term);
#endif
wherex=0;
wherey=ystatus-1;
displaystatus();
ofsredisplay(0);
}
void suspend() {
normal();
fullscroll();
gotoxy(0, ystatus);
cleareol();
#ifdef USE_SGTTY
ioctl(ttyfd, TIOCSETP, &term0);
ioctl(ttyfd, TIOCSETC, &tch0);
ioctl(ttyfd, TIOCSLTC, &lch0);
#else
tcsetattr(ttyfd, TCSANOW, &term0);
#endif
kill(pid, SIGCONT);
signal(SIGTSTP, SIG_DFL);
signal(SIGCONT, sigcont);
kill(mypid, SIGTSTP);
}
void sigwinch() {
#ifdef TIOCGWINSZ
signal(SIGWINCH, sigwinch);
if (ioctl(ttyfd, TIOCGWINSZ, &wsz)>=0 && wsz.ws_row>0 && wsz.ws_col>0) {
lines=wsz.ws_row;
cols=wsz.ws_col;
cursorwhere=2;
ystatus=lines-2;
yinput=lines-1;
wherex=0;
wherey=ystatus-1;
displaystatus();
if (inputlast>cols-8) {
inputcursor=cols-9;
inputofs=inputlast-cols+9;
} else {
inputofs=0;
inputcursor=inputlast;
}
ofsredisplay(0);
}
#endif
}
void allsigs() {
signal(SIGHUP, interrupted);
signal(SIGINT, interrupted);
signal(SIGQUIT, SIG_IGN);
signal(SIGPIPE, sigpipe);
signal(SIGTSTP, suspend);
signal(SIGCONT, sigcont);
#ifdef TIOCGWINSZ
signal(SIGWINCH, sigwinch);
#endif
}
#ifdef __GNUC__
void setstatus(unsigned char *title) {
#else
void setstatus(title)
unsigned char *title; {
#endif
unsigned char *t=title;
for (;*t;t++) if (*t<' ') (*t)+='@';
memset(statusline, ' ', MAX_COLS-1);
memcpy(statusline, title, strlen(title)<MAX_COLS ? strlen(title) : MAX_COLS);
}
void displaystatus() {
normal();
fullscroll();
gotoxy(0, ystatus);
setinv();
write(1, statusline, cols-1);
if (hold_mode) {
gotoxy(cols-4, ystatus);
write(1, "(h)", 3);
}
cursorwhere=2;
normal();
cleareol();
}
#ifdef __GNUC__
int casecmp(unsigned char *s, unsigned char *t) {
#else
int casecmp(s, t)
unsigned char *s, *t; {
#endif
while (((*s>='a' && *s<='z')?(*s)-32:*s)==
((*t>='a' && *t<='z')?(*t)-32:*t)) {
if (*s=='\0') return 1;
s++; t++;
}
return 0;
}
#ifdef __GNUC__
void addtab(unsigned char *line) {
#else
void addtab(line)
unsigned char *line; {
#endif
struct tabinfo *nt;
nt=oldest;
if (tablines) do {
if (casecmp(nt->string, line)) {
strcpy(nt->string, line);
if (nt==oldest) oldest=nt->prev;
else {
nt->prev->next=nt->next;
nt->next->prev=nt->prev;
nt->prev=oldest;
nt->next=oldest->next;
oldest->next=nt;
nt->next->prev=nt;
}
curtabt=oldest->next;
curtabr=oldest;
return;
}
nt=nt->next;
} while (nt!=oldest);
if (!tablines) {
nt=(struct tabinfo *)malloc(sizeof (struct tabinfo));
nt->prev=nt->next=curtabt=curtabr=oldest=nt;
tablines++;
} else if (tablines<MAX_TAB_LINES) {
nt=(struct tabinfo *)malloc(sizeof (struct tabinfo));
nt->prev=oldest;
nt->next=oldest->next;
oldest->next=nt;
nt->next->prev=nt;
tablines++;
} else {
nt=oldest;
oldest=nt->prev;
}
strcpy(nt->string, line);
oldest=nt->prev;
curtabt=oldest->next;
curtabr=oldest;
}
void doprotcommand() {
unsigned char *tmp;
switch (protcmd[0]) {
case 'i' : dispmode=2; /* set irc mode, ack */
bold=inv=under=0;
write(writefd, "@ssfe@i\n", 8);
break;
case 'c' : dispmode=1; /* set cooked mode, ack */
write(writefd, "@ssfe@c\n", 8);
break;
case 's' : setstatus(protcmd+1); /* set status */
displaystatus();
break;
case 'T' : strncpy(ctrl_t, protcmd+1, 127); /* set ^t's text */
ctrl_t[126] = '\0';
strcat(ctrl_t, "\n");
break;
case 't' : addtab(protcmd+1); /* add tabkey entry */
break;
case 'l' : fullscroll(); /* clear screen */
normal();
clearscreen();
bold=inv=under=wherex=wherey=donl=0;
displaystatus();
ofsredisplay(0);
break;
case 'P' : no_echo = 1; /* password prompt */
case 'p' : if (strlen(protcmd+1)<=8) { /* prompt something */
fullscroll();
if (!specialprompt) {
histcurrent->len=inputlast;
histcurrent->plen=plen;
}
input=extrainput;
strcpy(input, protcmd+1);
plen=strlen(input);
inputofs=0;
modified=specialprompt=1;
inputlast=inputcursor=plen;
ofsredisplay(0);
}
break;
case 'n' : if (cursorwhere!=1) { /* type text */
normal();
fullscroll();
gotoxy(inputcursor, yinput);
cursorwhere=1;
}
for (tmp=protcmd+1; *tmp; tmp++) {
inschar(*tmp);
}
break;
case 'o' : strcpy(o_buffer, protcmd+1);
break;
}
}
void newline() {
unsigned char t;
hold_lines++;
if (hold_mode && hold_lines>lines-4) {
normal();
fullscroll();
gotoxy(cols-4, ystatus);
setinv();
write(1, "(H)", 3);
while(1) {
read(0, &t, 1);
if (t==9) break;
dokbdchar(t);
}
normal();
fullscroll();
gotoxy(cols-4, ystatus);
setinv();
write(1, "(h)", 3);
hold_lines=0;
normal();
winscroll();
gotoxy(cols-1, wherey);
if (bold) setbold();
if (under) setunder();
if (inv) setinv();
}
}
#ifdef __GNUC__
void formatter(unsigned char *readbuf, int rc) {
#else
void formatter(readbuf, rc)
unsigned char *readbuf;
int rc; {
#endif
unsigned char t, *r, *lwr, *lww;
int lwrc, lwbold, lwunder, lwinv, lwx;
if (cursorwhere!=0) {
winscroll();
gotoxy(wherex, wherey);
cursorwhere=0;
}
if (donl) {
newline();
write(1, "\r\n", 2);
normal();
wherex=0;
bold=inv=under=lwbold=lwinv=lwunder=0;
if (wherey<ystatus-1) wherey++;
} else if (dispmode>1) {
if (bold) setbold();
if (under) setunder();
if (inv) setinv();
lwbold=bold;
lwinv=inv;
lwunder=under;
}
if (rc && readbuf[rc-1]=='\n') {
rc--;
donl=1; cutline=0;
} else {
donl=0;
if (dispmode==0) cutline=1;
}
if (dispmode==0) {
if (rc) write(1, readbuf, rc);
normal();
return;
}
lww=w=writebuf;
lwr=r=readbuf;
lwrc=rc;
lwx=wherex;
while(rc-->0) {
t=(*r++);
if (t=='\r') continue;
if (wherex>cols-2 || (t==9 && wherex>(cols-2)&0xfff8)) {
if (t==' ' || t==9) ;
else if (lww>writebuf+cols/2) {
wherex=lwx; r=lwr; w=lww; rc=lwrc;
bold=lwbold; inv=lwinv; under=lwunder; wherex=lwx;
} else {
rc++; r--;
}
write(1, writebuf, w-writebuf);
newline();
write(1, "\r\n ", 13);
w=writebuf;
lwr=r; lww=w; lwrc=rc;
lwbold=bold; lwinv=inv; lwunder=under;
lwx=wherex=11;
if (wherey<ystatus-1) wherey++;
rc--; t=(*r++);
}
if (t=='\n') {
if (w!=writebuf) write(1, writebuf, w-writebuf);
newline();
write(1, "\r\n", 2);
normal();
w=writebuf;
lwr=r; lww=w; lwrc=rc;
lwbold=bold=lwinv=inv=lwunder=under=lwx=wherex=0;
if (wherey<ystatus-1) wherey++;
} else if (dispmode>1 &&
((t==2 && bold) || (t==22 && inv) || (t==31 && under))) {
writecap(t_me);
bold=under=inv=0;
} else if (dispmode>1 && t==2) {
writecap(t_md);
bold=1;
} else if (dispmode>1 && t==22) {
writecap(t_mr);
inv=1;
} else if (dispmode>1 && t==31) {
writecap(t_us);
under=1;
} else if (dispmode>1 && t==15) {
if (bold || inv || under) writecap(t_me);
bold=under=inv=0;
} else if (t==9) {
(*w++)=t;
wherex=(wherex & 0xfff8)+8;
} else if (t<' ' && (t!=7 || !beep)) {
wherex++;
if (inv) {
writecap(t_me);
(*w++)=(t+'@');
} else {
writecap(t_mr);
(*w++)=(t+'@');
writecap(t_me);
}
if (bold) writecap(t_md);
if (inv) writecap(t_mr);
if (under) writecap(t_us);
} else {
if (t!=7) wherex++;
(*w++)=t;
}
if (t==' ' || t==9) {
lwr=r; lww=w; lwrc=rc;
lwbold=bold; lwinv=inv; lwunder=under;
lwx=wherex;
}
}
if (w!=writebuf) write(1, writebuf, w-writebuf);
}
#ifdef __GNUC__
void doprogramline(unsigned char *readbuf, int rc) {
#else
void doprogramline(readbuf, rc)
unsigned char *readbuf;
int rc; {
#endif
unsigned char *w, *r, *r2, t;
if (dispmode==0) {
formatter(readbuf, rc);
return;
}
w=r=readbuf;
while(rc-->0) {
t=(*r++);
if (idstatus==0)
if (*inid=='\0') {
idstatus=1;
wpc=protcmd;
inid=id;
} else if (*inid==t && (inid!=id || r==(readbuf+1) || *(r-2)=='\n')) {
inid++;
(*wpc++)=t;
} else {
r2=protcmd;
while (r2!=wpc) (*w++)=(*r2++);
(*w++)=t;
wpc=protcmd;
inid=id;
}
if (idstatus==1)
if (t=='\n') {
*wpc='\0';
doprotcommand();
inid=id;
wpc=protcmd;
idstatus=0;
} else (*wpc++)=t;
}
if (w!=readbuf) formatter(readbuf, w-readbuf);
}
#ifdef __GNUC__
void write1(unsigned char t, int pos) {
#else
void write1(t, pos)
unsigned char t;
int pos; {
#endif
if (no_echo && pos>=plen) {
write(1, "*", 1);
} else if (t>=' ')
write(1, &t, 1);
else {
setinv();
t+='@';
write(1, &t, 1);
normal();
}
}
#ifdef __GNUC__
void ofsredisplay(int x) {
#else
void ofsredisplay(x) {
#endif
/* redisplays starting at x */
unsigned char *w;
int i;
gotoxy(x, yinput);
if (inputlast-inputofs>=x) {
i=((inputlast-inputofs>cols-1 ? cols-1-x : inputlast-inputofs-x));
for (w=input+inputofs+x; i--; w++) write1(*w, w-input);
}
cleareol();
gotoxy(inputcursor, yinput);
cursorwhere=1;
}
#ifdef __GNUC__
void delempty(struct histinfo *leavealone) {
#else
void delempty(leavealone)
struct histinfo *leavealone; {
#endif
struct histinfo *h, *h2;
int cont=0;
h=histoldest;
do {
cont=0;
if ((h->len<=h->plen) && (h!=leavealone)) {
histlines--;
h->next->prev=h->prev;
h->prev->next=h->next;
h2=h->prev;
free(h);
if (h==histoldest) {
histoldest=h2;
cont=1;
}
h=h2;
} else h=h->prev;
} while ((h!=histoldest || cont) && histlines>0);
if (!histlines) {
histoldest=NULL;
return;
}
}
struct histinfo *makenew() {
struct histinfo *nh;
if (!histlines) {
nh=(struct histinfo *)malloc(sizeof (struct histinfo));
nh->prev=nh->next=histoldest=nh;
histlines++;
} else if (histlines<MAX_HIST_LINES) {
nh=(struct histinfo *)malloc(sizeof (struct histinfo));
nh->prev=histoldest;
nh->next=histoldest->next;
histoldest->next=nh;
nh->next->prev=nh;
histlines++;
} else {
nh=histoldest;
histoldest=nh->prev;
}
return nh;
}
#ifdef __GNUC__
void sendline(int yank) {
#else
void sendline(yank) {
#endif
if (!specialprompt) {
histcurrent->len=inputlast;
histcurrent->plen=plen;
}
if (!yank) {
input[inputlast]='\n';
if (printmode) formatter(input, inputlast+1);
if (write(writefd, input+plen, inputlast+1-plen)<inputlast+1-plen)
cleanupexit(1, "write error");
}
input[inputlast]='\0';
delempty(NULL);
histcurrent=makenew();
input=histcurrent->string;
strcpy(input, prompt);
plen=strlen(prompt);
inputofs=specialprompt=0;
modified=1;
inputcursor=inputlast=plen;
ofsredisplay(0);
no_echo=0;
}
void modify() {
struct histinfo *h;
if (!modified) {
if (inputlast>plen) {
h=histcurrent;
delempty(h);
histcurrent=makenew();
strcpy(histcurrent->string, h->string);
input=histcurrent->string;
}
modified=1;
}
}
void fixpos() {
if (inputcursor<8 && inputofs>0) {
inputofs-=cols-16;
inputcursor+=cols-16;
if (inputofs<0) {
inputcursor+=inputofs;
inputofs=0;
}
ofsredisplay(0);
} else if (inputcursor>cols-8) {
inputofs+=cols-16;
inputcursor-=cols-16;
ofsredisplay(0);
}
}
void reshow() {
if (inputlast>cols-8) {
inputcursor=cols-9;
inputofs=inputlast-cols+9;
} else {
inputofs=0;
inputcursor=inputlast;
}
ofsredisplay(0);
}
#ifdef __GNUC__
void inschar(unsigned char t) {
#else
void inschar(t)
unsigned char t; {
#endif
unsigned char *tmp;
if (inputlast<BUF_SIZE-4) {
modify();
if (inputofs+inputcursor==inputlast) {
write1(t, inputlast);
input[inputlast++]=t;
input[inputlast]='\0';
inputcursor++;
} else {
tmp=input+inputlast;
while (tmp>=input+inputofs+inputcursor)
*(tmp+1)=(*tmp--);
input[inputofs+(inputcursor++)]=t;
inputlast++;
ofsredisplay(inputcursor-1);
}
fixpos();
}
}
#ifdef __GNUC__
void dokbdchar(unsigned char t) {
#else
void dokbdchar(t)
unsigned char t; {
#endif
unsigned char *tmp;
if (inarrow==1) {
if (t=='[' || t=='O') {
inarrow++;
return;
}
inarrow=0;
} else if (inarrow==2) {
inarrow=0;
if (t=='D') t=2;
else if (t=='C') t=6;
else if (t=='A') t=16;
else if (t=='B') t=14;
else return;
}
if (ctrlx && !quote) {
ctrlx=0;
t|=0x20;
if (dispmode>0 && ((t=='h' && !hold_mode) || t=='y')) {
hold_mode=1;
hold_lines=0;
if (cursorwhere!=1) fullscroll();
cursorwhere=2;
normal();
gotoxy(cols-4, ystatus);
setinv();
write(1, "(h)", 3);
normal();
} else if (dispmode>0 && ((t=='h' && hold_mode) || t=='n')) {
hold_mode=0;
if (cursorwhere!=1) fullscroll();
cursorwhere=2;
normal();
gotoxy(cols-4, ystatus);
setinv();
write(1, " ", 3);
normal();
} else if (dispmode>0 && t=='i') {
dispmode=3-dispmode;
bold=inv=under=0;
} else if (dispmode>0 && t=='b') {
beep=!beep;
} else if (t=='c') cleanupexit(1, "exiting");
return;
}
if (cutline) donl=1;
if (cursorwhere!=1) {
normal();
fullscroll();
gotoxy(inputcursor, yinput);
cursorwhere=1;
}
if (t==24 && !quote) {
ctrlx=1;
return;
} else ctrlx=0;
if (t==27 && !quote) {
inarrow=1;
} else if ((t==10 || t==13) && !quote) { /* return, newline */
sendline(0);
if (tablines) {
curtabr=oldest;
curtabt=oldest->next;
}
} else if (t==25 && !quote) { /* ^y */
if (!specialprompt) {
sendline(1);
if (tablines) {
curtabr=oldest;
curtabt=oldest->next;
}
}
} else if (t==21 && !quote) { /* ^u */
modify();
input[plen]='\0';
inputcursor=inputlast=plen;
inputofs=0;
ofsredisplay(0);
} else if ((t==8 || t==0x7f) && !quote) { /* ^h, ^? */
if (inputcursor>plen) {
modify();
tmp=input+inputcursor+inputofs;
while (tmp<input+inputlast)
*(tmp-1)=(*tmp++);
input[--inputlast]='\0';
gotoxy(--inputcursor, yinput);
ofsredisplay(inputcursor);
fixpos();
}
} else if (t==4 && !quote) { /* ^d */
if (inputcursor+inputofs<inputlast) {
modify();
tmp=input+inputcursor+inputofs+1;
while (tmp<input+inputlast)
*(tmp-1)=(*tmp++);
input[--inputlast]='\0';
gotoxy(inputcursor, yinput);
ofsredisplay(inputcursor);
}
} else if (t==11 && !quote) { /* ^k */
if (inputcursor+inputofs<inputlast) {
modify();
input[inputlast=inputofs+inputcursor]='\0';
ofsredisplay(inputcursor);
}
} else if (t==2 && !quote) { /* ^b */
if (inputcursor>0 && (inputcursor>plen || inputofs>0)) {
gotoxy(--inputcursor, yinput);
fixpos();
}
} else if (t==6 && !quote) { /* ^f */
if (inputcursor+inputofs<inputlast) {
gotoxy(++inputcursor, yinput);
fixpos();
}
} else if (t==1 && !quote) { /* ^a */
if (inputcursor+inputofs>plen) {
if (inputofs==0)
gotoxy((inputcursor=plen), yinput);
else {
inputofs=0;
inputcursor=plen;
ofsredisplay(0);
}
}
} else if (t==5 && !quote) { /* ^e */
if (inputcursor+inputofs<inputlast) {
if (inputlast-inputofs<cols-3) {
gotoxy((inputcursor=inputlast-inputofs), yinput);
} else if (inputlast>cols-8) {
inputcursor=cols-9;
inputofs=inputlast-cols+9;
ofsredisplay(0);
} else {
inputofs=0;
inputcursor=inputlast;
ofsredisplay(0);
}
}
} else if (t==12 && !quote) { /* ^l */
displaystatus();
ofsredisplay(0);
} else if (t==9 && !quote) { /* TAB */
if (tablines) {
modify();
strcpy(input+plen, curtabt->string);
curtabr=curtabt->prev;
curtabt=curtabt->next;
inputlast=strlen(input);
reshow();
}
} else if (t==18 && !quote) { /* ^r */
if (tablines) {
modify();
strcpy(input+plen, curtabr->string);
curtabt=curtabr->next;
curtabr=curtabr->prev;
inputlast=strlen(input);
reshow();
}
} else if (t==16 && !quote) { /* ^p */
if (histlines>1 && !specialprompt) {
histcurrent->plen=plen;
histcurrent->len=inputlast;
histcurrent=histcurrent->next;
plen=histcurrent->plen;
inputlast=histcurrent->len;
input=histcurrent->string;
modified=0;
reshow();
}
} else if (t==14 && !quote) { /* ^n */
if (histlines>1 && !specialprompt) {
histcurrent->plen=plen;
histcurrent->len=inputlast;
histcurrent=histcurrent->prev;
plen=histcurrent->plen;
inputlast=histcurrent->len;
input=histcurrent->string;
modified=0;
reshow();
}
} else if (t==15 &&!quote) { /* ^o */
if (strlen(o_buffer)) modify();
for (tmp=o_buffer; *tmp; tmp++) inschar(*tmp);
} else if (t==20 && !quote) { /* ^t */
write(writefd, ctrl_t, strlen(ctrl_t));
} else if (t==22 && !quote) { /* ^v */
quote++;
return;
#ifdef CONTROL_W
} else if (t==23 && !quote) { /* ^w */
fullscroll();
normal();
clearscreen();
bold=inv=under=wherex=wherey=donl=0;
displaystatus();
ofsredisplay(0);
#endif
} else inschar(t);
quote=0;
}
#ifdef __GNUC__
void barf(unsigned char *m) {
#else
void barf(m)
unsigned char *m; {
#endif
fprintf(stderr, "%s\n", m);
exit(1);
}
char *myname;
void use() {
fprintf(stderr, "Use: %s [options] program [program's options]\n", myname);
fprintf(stderr, "Options are:\n");
fprintf(stderr, " -raw, -cooked, -irc : set display mode\n");
fprintf(stderr, " -print : print your input lines\n");
fprintf(stderr, " -prompt <prompt> : specify a command-line prompt\n");
fprintf(stderr, " -hold : pause after each full screen (for cooked/irc mode)\n");
fprintf(stderr, " -beep : let beeps through (for cooked/irc mode)\n");
fprintf(stderr, " -flow : leave ^S/^Q alone for flow control\n");
exit(1);
}
#ifdef __GNUC__
int main(int argc, char *argv[]) {
#else
int main(argc, argv)
int argc;
char *argv[]; {
#endif
char *vr;
int pfds0[2], pfds1[2], pfds2[2];
myname=(*argv);
prompt=nullstring;
while (argc>1) {
if (strcmp(argv[1], "-raw")==0) {
dispmode=0;
argv++; argc--;
} else if (strcmp(argv[1], "-cooked")==0) {
dispmode=1;
argv++; argc--;
} else if (strcmp(argv[1], "-irc")==0) {
dispmode=2;
argv++; argc--;
} else if (strcmp(argv[1], "-hold")==0) {
hold_mode=1;
argv++; argc--;
} else if (strcmp(argv[1], "-print")==0) {
argv++; argc--;
if (prompt==nullstring) prompt=defprompt;
printmode=1;
} else if (strcmp(argv[1], "-beep")==0) {
beep=1;
argv++; argc--;
} else if (strcmp(argv[1], "-flow")==0) {
flow=1;
argv++; argc--;
} else if (strcmp(argv[1], "-prompt")==0) {
if (argc>2) prompt=(unsigned char *)argv[2];
if (strlen(prompt)>8) barf("Prompt too long");
argv+=2; argc-=2;
} else break;
}
if (argc<2) use();
if (!isatty(0)) barf("I can only run on a tty, sorry");
if ((termtype=getenv("TERM"))==NULL) barf("No terminal type set");
if (tgetent(termcap, termtype)<1) barf("No termcap info for your terminal");
tc=capabilities;
if ((t_cm=(char *)tgetstr("cm", &tc))==NULL)
barf("Can't find a way to move the cursor around with your terminal");
if ((t_cl=(char *)tgetstr("cl", &tc))==NULL)
barf("Can't find a way to clear the screen with your terminal");
if ((t_ce=(char *)tgetstr("ce", &tc))==NULL)
barf("Can't find a way to clear to end of line with your terminal");
if ((t_cs=(char *)tgetstr("cs", &tc))==NULL) {
if (strncmp(termtype, "xterm", 5)==0 || strncmp(termtype, "vt100", 5)==0)
ansi_cs=1;
else
barf("Can't find a way to set the scrolling region with your terminal");
}
if ((t_me=(char *)tgetstr("me", &tc))!=NULL) {
if ((t_mr=(char *)tgetstr("mr", &tc))==NULL) t_mr=t_me;
if ((t_md=(char *)tgetstr("md", &tc))==NULL) t_md=t_me;
if ((t_us=(char *)tgetstr("us", &tc))==NULL) t_us=t_me;
} else if ((t_me=(char *)tgetstr("se", &tc))!=NULL &&
(t_mr=(char *)tgetstr("so", &tc))!=NULL) {
t_md=t_mr;
t_us=tc;
(*tc++)='\0';
} else {
t_me=t_md=t_mr=t_us=tc;
(*tc++)='\0';
}
/*
if ((ttyfd=open("/dev/tty", O_RDWR))<0 &&
(ttyfd=open("/dev/tty", O_RDONLY))<0) barf("Can't open terminal!");
*/
ttyfd = 0;
#ifdef TIOCGWINSZ
if (ioctl(ttyfd, TIOCGWINSZ, &wsz)<0 || wsz.ws_row<1 || wsz.ws_col<1) {
#endif
lines=((vr=getenv("LINES"))?atoi(vr):0);
cols=((vr=getenv("COLUMNS"))?atoi(vr):0);
if (lines<1 || cols<1) {
if ((lines=tgetnum("li"))<1 || (cols=tgetnum("co"))<1) {
lines=24; cols=80;
}
}
#ifdef TIOCGWINSZ
} else {
lines=wsz.ws_row;
cols=wsz.ws_col;
}
#endif
if (pipe(pfds0)<0 || pipe(pfds1)<0 || pipe(pfds2)<0) {
perror("pipe");
exit(1);
}
mypid=getpid();
switch (pid=fork()) {
case -1:
perror("fork");
exit(1);
case 0:
if (pfds0[0]!=0) dup2(pfds0[0], 0);
if (pfds1[1]!=1) dup2(pfds1[1], 1);
if (pfds2[1]!=2) dup2(pfds2[1], 2);
if (pfds0[0]>2) close(pfds0[0]);
if (pfds0[1]>2) close(pfds0[1]);
if (pfds1[0]>2) close(pfds1[0]);
if (pfds1[1]>2) close(pfds1[1]);
if (pfds2[0]>2) close(pfds2[0]);
if (pfds2[1]>2) close(pfds2[1]);
/* okay we can read from 0 and write to 1 and 2, now.. it seems */
execvp(argv[1], argv+1);
perror("exec");
sleep(1);
exit(1);
default:
close(pfds0[0]);
close(pfds1[1]);
close(pfds2[1]);
readfd=pfds1[0];
writefd=pfds0[1];
errfd=pfds2[0];
}
#ifdef USE_SGTTY
if (ioctl(ttyfd, TIOCGETP, &term)<0 || ioctl(ttyfd, TIOCGETC, &tch)<0 ||
ioctl(ttyfd, TIOCGLTC, &lch)<0) {
perror("sgtty get ioctl");
exit(1);
}
term0=term;
tch0=tch;
lch0=lch;
term.sg_flags|=CBREAK;
term.sg_flags&= ~ECHO & ~CRMOD;
memset(&tch, -1, sizeof(tch));
memset(&lch, -1, sizeof(lch));
tch.t_intrc=(char)28;
tch.t_quitc=(char)3;
if (flow) {
tch.t_startc=(char)17;
tch.t_stopc=(char)19;
}
lch.t_suspc=(char)26;
if (ioctl(ttyfd, TIOCSETP, &term)<0 || ioctl(ttyfd, TIOCSETC, &tch)<0 ||
ioctl(ttyfd, TIOCSLTC, &lch)<0) {
perror("sgtty set ioctl");
exit(1);
}
#else
if (tcgetattr(ttyfd, &term)<0) {
perror("tcgetattr");
exit(1);
}
term0=term;
term.c_lflag &= ~ECHO & ~ICANON;
term.c_cc[VTIME]=(char)0;
term.c_cc[VMIN]=(char)1;
if (!flow) {
term.c_cc[VSTOP]=(char)0;
term.c_cc[VSTART]=(char)0;
}
term.c_cc[VQUIT]=(char)3;
term.c_cc[VINTR]=(char)28; /* reverse ^c and ^\ */
term.c_cc[VSUSP]=(char)26;
#ifdef VREPRINT
term.c_cc[VREPRINT]=(char)0;
#endif
#ifdef VDISCARD
term.c_cc[VDISCARD]=(char)0;
#endif
#ifdef VLNEXT
term.c_cc[VLNEXT]=(char)0;
#endif
#ifdef VDSUSP
term.c_cc[VDSUSP]=(char)0;
#endif
if (tcsetattr(ttyfd, TCSANOW, &term)<0) {
perror("tcsetattr");
exit(1);
}
#endif
allsigs();
ystatus=lines-2;
yinput=lines-1;
if (lines>255) barf("Screen too big");
if (ystatus<=2 || cols<20) barf("Screen too small");
statusline=(unsigned char *)malloc(MAX_COLS);
writebuf=(unsigned char *)malloc(20*BUF_SIZE);
strcpy(tmpstr, " ");
for (i=1; i<argc; i++)
if (strlen(tmpstr)+strlen(argv[i])<cols-1) {
strcat(tmpstr, argv[i]);
strcat(tmpstr, " ");
}
setstatus(tmpstr);
if (dispmode==0) wherey=ystatus-1;
clearscreen();
displaystatus();
histoldest=histcurrent=(struct histinfo *)malloc(sizeof (struct histinfo));
input=histcurrent->string;
histcurrent->prev=histcurrent->next=histcurrent;
histlines=1;
plen=strlen(prompt);
inputlast=inputcursor=plen;
strcpy(input, prompt);
ofsredisplay(0);
*protcmd='\0';
*o_buffer='\0';
cursorwhere=1;
FD_ZERO(&ready);
FD_SET(ttyfd, &ready);
FD_SET(readfd, &ready);
FD_SET(errfd, &ready);
while(1) {
result=ready;
if (select(64, &result, NULL, NULL, NULL)<=0)
if (errno==EINTR) continue;
else cleanupexit(1, "select error");
if (FD_ISSET(readfd, &result))
if ((rc=read(readfd, readbuf, BUF_SIZE))>0)
doprogramline(readbuf, rc);
else
cleanupexit(1, "program terminated");
if (FD_ISSET(errfd, &result))
if ((rc=read(errfd, readbuf, BUF_SIZE))>0)
doprogramline(readbuf, rc);
else
cleanupexit(1, "program terminated");
if (FD_ISSET(ttyfd, &result))
if ((rrc=read(0, readbuf, BUF_SIZE))>0)
for (t=readbuf; rrc>0; rrc--) dokbdchar(*(t++));
else
cleanupexit(1, "read error from keyboard");
}
}
syntax highlighted by Code2HTML, v. 0.9.1