Changed the email addresses to francois@fleuret.org, changed the Makefile header.
[ircml.git] / ssfe.c
1 /* An ircII-like split-screen front end
2    Copyright (C) 1995 Roger Espel Llima
3
4    Started: 17 Feb 95 by orabidoo <roger.espel.llima@ens.fr>
5    Latest modification: 7 June 97
6
7    To compile: gcc ssfe.c -o ssfe -ltermcap
8
9    If it doesn't work, try gcc ssfe.c -o ssfe -lcurses
10    or try cc, acc or c89 instead of gcc, or -lncurses.
11
12    Use: ssfe [options] program arguments
13
14    This program is free software; you can redistribute it and/or modify
15    it under the terms of the GNU General Public License as published by
16    the Free Software Foundation. See the file LICENSE for details.
17 */
18
19 #include <sys/time.h>
20 #include <sys/types.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <signal.h>
26 #include <errno.h>
27
28 #ifdef USE_SGTTY
29 #include <sgtty.h>
30 #else
31 #include <termios.h>
32 #endif
33
34 #include <sys/ioctl.h>
35
36 #ifdef _AIX
37 #include <sys/select.h>
38 #endif
39
40 #define BUF_SIZE 512
41 #define MAX_COLS 512
42
43 unsigned char *statusline;
44 int ystatus, yinput;     /* line number of the status line, input line */
45
46 int ttyfd;
47 #ifdef TIOCGWINSZ
48 struct winsize wsz;
49 #endif
50
51 #ifdef USE_SGTTY
52 struct sgttyb term, term0;
53 struct tchars tch, tch0;
54 struct ltchars lch, lch0;
55 #else
56 struct termios term, term0;
57 #endif
58
59 int pid, mypid;
60 int i;
61 int cols, lines;
62 int readfd, writefd, errfd;
63
64 unsigned char *t, *w;
65 unsigned char tmpstr[BUF_SIZE], extrainput[BUF_SIZE+20], readbuf[2*BUF_SIZE],
66               *input, *writebuf, o_buffer[BUF_SIZE];
67 int bold=0, inv=0, under=0, wherex=0, wherey=0, donl=0;
68 int hold_mode=0, hold_lines=0, ctrlx=0, beep=0, flow=0;
69
70 unsigned char defprompt[]="> ",
71          nullstring[]="",
72          *prompt;
73 int plen=0, specialprompt=0, modified=1, no_echo=0;
74
75 #define MAX_TAB_LINES 20
76 struct tabinfo {
77   unsigned char string[BUF_SIZE];
78   struct tabinfo *prev, *next;
79 };
80 int tablines=0;
81 struct tabinfo *curtabt=NULL, *curtabr=NULL, *oldest=NULL;
82
83 #define MAX_HIST_LINES 50
84 struct histinfo {
85   unsigned char string[BUF_SIZE+20];
86   int len, plen;
87   struct histinfo *prev, *next;
88 };
89 int histlines=0;
90 struct histinfo *histcurrent=NULL, *histoldest=NULL;
91
92 char ctrl_t[128] = "/next\n";
93
94 unsigned char id[]="`#ssfe#", *inid=id, protcmd[BUF_SIZE], *wpc=protcmd;
95 int idstatus=0;  /* 0 looking for/in the word, 1 in the arguments */
96 #define ID_BACK "@ssfe@"
97
98 int rc, rrc, inputcursor, inputlast, inputofs, inarrow=0, quote=0;
99 int cursorwhere;     /* 0 = up, 1 = down, 2 = undef */
100 int dispmode=1; /* 0=raw, 1=wordwrap, 2=process ^b^v^_ */
101 int printmode=0;
102 int cutline=0;
103
104 char *termtype, termcap[1024], *tc, capabilities[2048];
105 char *t_cm, *t_cl, *t_mr, *t_md, *t_me, *t_cs, *t_ce, *t_us;
106 int ansi_cs = 0;
107
108 fd_set ready, result;
109 extern int errno;
110
111 #ifdef __GNUC__
112 extern unsigned char *tgoto(unsigned char *cm, int col, int line);
113 #else
114 extern unsigned char *tgoto();
115 #endif
116
117 #ifdef __GNUC__
118 int myputchar(int c) {
119 #else
120 int myputchar(c) {
121 #endif
122   unsigned char cc=(unsigned char)c;
123   return(write(1, &cc, 1));
124 }
125
126 #ifdef __GNUC__
127 int addchar(int c) {
128 #else
129 int addchar(c) {
130 #endif
131   (*w++)=(unsigned char)c;
132 }
133
134 #ifdef __GNUC__
135 void putcap(unsigned char *s) {
136 #else
137 void putcap(s)
138 unsigned char *s; {
139 #endif
140   tputs(s, 0, myputchar);
141 }
142
143 #ifdef __GNUC__
144 int do_cs(int y1, int y2) {
145 #else
146 int do_cs(y1, y2) {
147 #endif
148   static char temp[16];
149   if (ansi_cs) {
150     sprintf(temp, "\e[%d;%dr", y1, y2);
151     write(1, temp, strlen(temp));
152   } else putcap((char *)tgoto(t_cs, y2-1, y1-1));
153 }
154
155 #ifdef __GNUC__
156 void writecap(unsigned char *s) {
157 #else
158 void writecap(s)
159 unsigned char *s; {
160 #endif
161   tputs(s, 0, addchar);
162 }
163
164 #ifdef __GNUC__
165 void gotoxy(int x, int y) {
166 #else
167 void gotoxy(x, y) {
168 #endif
169 /* left upper = 0, 0 */
170   putcap(tgoto(t_cm, x, y));
171 }
172
173 #define clearscreen() (putcap(t_cl))
174 #define cleareol() (putcap(t_ce))
175 #define fullscroll() (do_cs(0, 0))
176 #define winscroll() (do_cs(1, lines-2))
177 #define setbold() (putcap(t_md))
178 #define setunder() (putcap(t_us))
179 #define setinv() (putcap(t_mr))
180 #define normal() (putcap(t_me))
181
182 #ifdef __GNUC__
183 void ofsredisplay(int x);
184 void inschar(unsigned char t);
185 void dokbdchar(unsigned char t);
186 #else
187 void ofsredisplay();
188 void inschar();
189 void dokbdchar();
190 #endif
191 void displaystatus();
192
193 #ifdef __GNUC__
194 void cleanupexit(int n, unsigned char *error) {
195 #else
196 void cleanupexit(n, error)
197 int n;
198 unsigned char *error; {
199 #endif
200   normal();
201   fullscroll();
202   gotoxy(0, lines-1);
203   cleareol();
204 #ifdef USE_SGTTY
205   ioctl(ttyfd, TIOCSETP, &term0);
206   ioctl(ttyfd, TIOCSETC, &tch0);
207   ioctl(ttyfd, TIOCSLTC, &lch0);
208 #else
209   tcsetattr(ttyfd, TCSADRAIN, &term0);
210 #endif
211   close(ttyfd);
212   if (error!=NULL)
213     fprintf(stderr, "%s\n", error);
214   exit(n);
215 }
216
217 void allsigs();
218
219 void interrupted() {
220   cleanupexit(1, "interrupted");
221 }
222
223 void sigpipe() {
224   cleanupexit(1, "program died");
225 }
226
227 void sigcont() {
228   allsigs();
229 #ifdef USE_SGTTY
230   ioctl(ttyfd, TIOCSETP, &term);
231   ioctl(ttyfd, TIOCSETC, &tch);
232   ioctl(ttyfd, TIOCSLTC, &lch);
233 #else
234   tcsetattr(ttyfd, TCSANOW, &term);
235 #endif
236   wherex=0;
237   wherey=ystatus-1;
238   displaystatus();
239   ofsredisplay(0);
240 }
241
242 void suspend() {
243   normal();
244   fullscroll();
245   gotoxy(0, ystatus);
246   cleareol();
247 #ifdef USE_SGTTY
248   ioctl(ttyfd, TIOCSETP, &term0);
249   ioctl(ttyfd, TIOCSETC, &tch0);
250   ioctl(ttyfd, TIOCSLTC, &lch0);
251 #else
252   tcsetattr(ttyfd, TCSANOW, &term0);
253 #endif
254   kill(pid, SIGCONT);
255   signal(SIGTSTP, SIG_DFL);
256   signal(SIGCONT, sigcont);
257   kill(mypid, SIGTSTP);
258 }
259
260 void sigwinch() {
261 #ifdef TIOCGWINSZ
262   signal(SIGWINCH, sigwinch);
263   if (ioctl(ttyfd, TIOCGWINSZ, &wsz)>=0 && wsz.ws_row>0 && wsz.ws_col>0) {
264     lines=wsz.ws_row;
265     cols=wsz.ws_col;
266     cursorwhere=2;
267     ystatus=lines-2;
268     yinput=lines-1;
269     wherex=0;
270     wherey=ystatus-1;
271     displaystatus();
272     if (inputlast>cols-8) {
273       inputcursor=cols-9;
274       inputofs=inputlast-cols+9;
275     } else {
276       inputofs=0;
277       inputcursor=inputlast;
278     }
279     ofsredisplay(0);
280   }
281 #endif
282 }
283
284 void allsigs() {
285   signal(SIGHUP, interrupted);
286   signal(SIGINT, interrupted);
287   signal(SIGQUIT, SIG_IGN);
288   signal(SIGPIPE, sigpipe);
289   signal(SIGTSTP, suspend);
290   signal(SIGCONT, sigcont);
291 #ifdef TIOCGWINSZ
292   signal(SIGWINCH, sigwinch);
293 #endif
294 }
295
296 #ifdef __GNUC__
297 void setstatus(unsigned char *title) {
298 #else
299 void setstatus(title)
300 unsigned char *title; {
301 #endif
302   unsigned char *t=title;
303   for (;*t;t++) if (*t<' ') (*t)+='@';
304   memset(statusline, ' ', MAX_COLS-1);
305   memcpy(statusline, title, strlen(title)<MAX_COLS ? strlen(title) : MAX_COLS);
306 }
307
308 void displaystatus() {
309   normal();
310   fullscroll();
311   gotoxy(0, ystatus);
312   setinv();
313   write(1, statusline, cols-1);
314   if (hold_mode) {
315     gotoxy(cols-4, ystatus);
316     write(1, "(h)", 3);
317   }
318   cursorwhere=2;
319   normal();
320   cleareol();
321 }
322
323 #ifdef __GNUC__
324 int casecmp(unsigned char *s, unsigned char *t) {
325 #else
326 int casecmp(s, t)
327 unsigned char *s, *t; {
328 #endif
329   while (((*s>='a' && *s<='z')?(*s)-32:*s)==
330          ((*t>='a' && *t<='z')?(*t)-32:*t)) {
331     if (*s=='\0') return 1;
332     s++; t++;
333   }
334   return 0;
335 }
336
337 #ifdef __GNUC__
338 void addtab(unsigned char *line) {
339 #else
340 void addtab(line)
341 unsigned char *line; {
342 #endif
343   struct tabinfo *nt;
344
345   nt=oldest;
346   if (tablines) do {
347     if (casecmp(nt->string, line)) {
348       strcpy(nt->string, line);
349       if (nt==oldest) oldest=nt->prev;
350       else {
351         nt->prev->next=nt->next;
352         nt->next->prev=nt->prev;
353         nt->prev=oldest;
354         nt->next=oldest->next;
355         oldest->next=nt;
356         nt->next->prev=nt;
357       }
358       curtabt=oldest->next;
359       curtabr=oldest;
360       return;
361     }
362     nt=nt->next;
363   } while (nt!=oldest);
364
365   if (!tablines) {
366     nt=(struct tabinfo *)malloc(sizeof (struct tabinfo));
367     nt->prev=nt->next=curtabt=curtabr=oldest=nt;
368     tablines++;
369   } else if (tablines<MAX_TAB_LINES) {
370     nt=(struct tabinfo *)malloc(sizeof (struct tabinfo));
371     nt->prev=oldest;
372     nt->next=oldest->next;
373     oldest->next=nt;
374     nt->next->prev=nt;
375     tablines++;
376   } else {
377     nt=oldest;
378     oldest=nt->prev;
379   }
380   strcpy(nt->string, line);
381   oldest=nt->prev;
382   curtabt=oldest->next;
383   curtabr=oldest;
384 }
385
386 void doprotcommand() {
387   unsigned char *tmp;
388
389   switch (protcmd[0]) {
390     case 'i' : dispmode=2;      /* set irc mode, ack */
391                bold=inv=under=0;
392                write(writefd, "@ssfe@i\n", 8);
393                break;
394     case 'c' : dispmode=1;      /* set cooked mode, ack */
395                write(writefd, "@ssfe@c\n", 8);
396                break;
397     case 's' : setstatus(protcmd+1); /* set status */
398                displaystatus();
399                break;
400     case 'T' : strncpy(ctrl_t, protcmd+1, 127); /* set ^t's text */
401                ctrl_t[126] = '\0';
402                strcat(ctrl_t, "\n");
403                break;
404     case 't' : addtab(protcmd+1); /* add tabkey entry */
405                break;
406     case 'l' : fullscroll(); /* clear screen */
407                normal();
408                clearscreen();
409                bold=inv=under=wherex=wherey=donl=0;
410                displaystatus();
411                ofsredisplay(0);
412                break;
413
414     case 'P' : no_echo = 1;                 /* password prompt */
415     case 'p' : if (strlen(protcmd+1)<=8) {  /* prompt something */
416                  fullscroll();
417                  if (!specialprompt) {
418                    histcurrent->len=inputlast;
419                    histcurrent->plen=plen;
420                  }
421                  input=extrainput;
422                  strcpy(input, protcmd+1);
423                  plen=strlen(input);
424                  inputofs=0;
425                  modified=specialprompt=1;
426                  inputlast=inputcursor=plen;
427                  ofsredisplay(0);
428                }
429                break;
430     case 'n' : if (cursorwhere!=1) { /* type text */
431                  normal();
432                  fullscroll();
433                  gotoxy(inputcursor, yinput);
434                  cursorwhere=1;
435                }
436                for (tmp=protcmd+1; *tmp; tmp++) {
437                  inschar(*tmp);
438                }
439                break;
440     case 'o' : strcpy(o_buffer, protcmd+1);
441                break;
442   }
443 }
444
445 void newline() {
446   unsigned char t;
447   hold_lines++;
448   if (hold_mode && hold_lines>lines-4) {
449     normal();
450     fullscroll();
451     gotoxy(cols-4, ystatus);
452     setinv();
453     write(1, "(H)", 3);
454     while(1) {
455       read(0, &t, 1);
456       if (t==9) break;
457       dokbdchar(t);
458     }
459     normal();
460     fullscroll();
461     gotoxy(cols-4, ystatus);
462     setinv();
463     write(1, "(h)", 3);
464     hold_lines=0;
465     normal();
466     winscroll();
467     gotoxy(cols-1, wherey);
468     if (bold) setbold();
469     if (under) setunder();
470     if (inv) setinv();
471   }
472 }
473
474 #ifdef __GNUC__
475 void formatter(unsigned char *readbuf, int rc) {
476 #else
477 void formatter(readbuf, rc)
478 unsigned char *readbuf;
479 int rc; {
480 #endif
481
482   unsigned char t, *r, *lwr, *lww;
483   int lwrc, lwbold, lwunder, lwinv, lwx;
484
485   if (cursorwhere!=0) {
486     winscroll();
487     gotoxy(wherex, wherey);
488     cursorwhere=0;
489   }
490   if (donl) {
491     newline();
492     write(1, "\r\n", 2);
493     normal();
494     wherex=0;
495     bold=inv=under=lwbold=lwinv=lwunder=0;
496     if (wherey<ystatus-1) wherey++;
497   } else if (dispmode>1) {
498     if (bold) setbold();
499     if (under) setunder();
500     if (inv) setinv();
501     lwbold=bold;
502     lwinv=inv;
503     lwunder=under;
504   }
505   if (rc && readbuf[rc-1]=='\n') {
506     rc--;
507     donl=1; cutline=0;
508   } else {
509     donl=0;
510     if (dispmode==0) cutline=1;
511   }
512   if (dispmode==0) {
513     if (rc) write(1, readbuf, rc);
514     normal();
515     return;
516   }
517   lww=w=writebuf;
518   lwr=r=readbuf;
519   lwrc=rc;
520   lwx=wherex;
521   while(rc-->0) {
522     t=(*r++);
523     if (t=='\r') continue;
524     if (wherex>cols-2 || (t==9 && wherex>(cols-2)&0xfff8)) {
525       if (t==' ' || t==9) ;
526       else if (lww>writebuf+cols/2) {
527         wherex=lwx; r=lwr; w=lww; rc=lwrc;
528         bold=lwbold; inv=lwinv; under=lwunder; wherex=lwx;
529       } else {
530         rc++; r--;
531       }
532       write(1, writebuf, w-writebuf);
533       newline();
534       write(1, "\r\n           ", 13);
535       w=writebuf;
536       lwr=r; lww=w; lwrc=rc;
537       lwbold=bold; lwinv=inv; lwunder=under;
538       lwx=wherex=11;
539       if (wherey<ystatus-1) wherey++;
540       rc--; t=(*r++);
541     }
542     if (t=='\n') {
543       if (w!=writebuf) write(1, writebuf, w-writebuf);
544       newline();
545       write(1, "\r\n", 2);
546       normal();
547       w=writebuf;
548       lwr=r; lww=w; lwrc=rc;
549       lwbold=bold=lwinv=inv=lwunder=under=lwx=wherex=0;
550       if (wherey<ystatus-1) wherey++;
551     } else if (dispmode>1 &&
552                ((t==2 && bold) || (t==22 && inv) || (t==31 && under))) {
553       writecap(t_me);
554       bold=under=inv=0;
555     } else if (dispmode>1 && t==2) {
556       writecap(t_md);
557       bold=1;
558     } else if (dispmode>1 && t==22) {
559       writecap(t_mr);
560       inv=1;
561     } else if (dispmode>1 && t==31) {
562       writecap(t_us);
563       under=1;
564     } else if (dispmode>1 && t==15) {
565       if (bold || inv || under) writecap(t_me);
566       bold=under=inv=0;
567     } else if (t==9) {
568       (*w++)=t;
569       wherex=(wherex & 0xfff8)+8;
570     } else if (t<' ' && (t!=7 || !beep)) {
571       wherex++;
572       if (inv) {
573         writecap(t_me);
574         (*w++)=(t+'@');
575       } else {
576         writecap(t_mr);
577         (*w++)=(t+'@');
578         writecap(t_me);
579       }
580       if (bold) writecap(t_md);
581       if (inv) writecap(t_mr);
582       if (under) writecap(t_us);
583     } else {
584       if (t!=7) wherex++;
585       (*w++)=t;
586     }
587     if (t==' ' || t==9) {
588       lwr=r; lww=w; lwrc=rc;
589       lwbold=bold; lwinv=inv; lwunder=under;
590       lwx=wherex;
591     }
592   }
593   if (w!=writebuf) write(1, writebuf, w-writebuf);
594 }
595
596 #ifdef __GNUC__
597 void doprogramline(unsigned char *readbuf, int rc) {
598 #else
599 void doprogramline(readbuf, rc)
600 unsigned char *readbuf;
601 int rc; {
602 #endif
603
604   unsigned char *w, *r, *r2, t;
605   if (dispmode==0) {
606     formatter(readbuf, rc);
607     return;
608   }
609   w=r=readbuf;
610   while(rc-->0) {
611     t=(*r++);
612     if (idstatus==0)
613       if (*inid=='\0') {
614         idstatus=1;
615         wpc=protcmd;
616         inid=id;
617       } else if (*inid==t && (inid!=id || r==(readbuf+1) || *(r-2)=='\n')) {
618         inid++;
619         (*wpc++)=t;
620       } else {
621         r2=protcmd;
622         while (r2!=wpc) (*w++)=(*r2++);
623         (*w++)=t;
624         wpc=protcmd;
625         inid=id;
626       }
627     if (idstatus==1)
628       if (t=='\n')  {
629         *wpc='\0';
630         doprotcommand();
631         inid=id;
632         wpc=protcmd;
633         idstatus=0;
634       } else (*wpc++)=t;
635   }
636   if (w!=readbuf) formatter(readbuf, w-readbuf);
637 }
638
639 #ifdef __GNUC__
640 void write1(unsigned char t, int pos) {
641 #else
642 void write1(t, pos)
643 unsigned char t;
644 int pos; {
645 #endif
646   if (no_echo && pos>=plen) {
647     write(1, "*", 1);
648   } else if (t>=' ')
649       write(1, &t, 1);
650   else {
651       setinv();
652       t+='@';
653       write(1, &t, 1);
654       normal();
655   }
656 }
657
658 #ifdef __GNUC__
659 void ofsredisplay(int x) {
660 #else
661 void ofsredisplay(x) {
662 #endif
663 /* redisplays starting at x */
664   unsigned char *w;
665   int i;
666   gotoxy(x, yinput);
667   if (inputlast-inputofs>=x) {
668     i=((inputlast-inputofs>cols-1 ? cols-1-x : inputlast-inputofs-x));
669     for (w=input+inputofs+x; i--; w++) write1(*w, w-input);
670   }
671   cleareol();
672   gotoxy(inputcursor, yinput);
673   cursorwhere=1;
674 }
675
676 #ifdef __GNUC__
677 void delempty(struct histinfo *leavealone) {
678 #else
679 void delempty(leavealone)
680 struct histinfo *leavealone; {
681 #endif
682   struct histinfo *h, *h2;
683   int cont=0;
684   h=histoldest;
685   do {
686     cont=0;
687     if ((h->len<=h->plen) && (h!=leavealone)) {
688       histlines--;
689       h->next->prev=h->prev;
690       h->prev->next=h->next;
691       h2=h->prev;
692       free(h);
693       if (h==histoldest) {
694         histoldest=h2;
695         cont=1;
696       }
697       h=h2;
698     } else h=h->prev;
699   } while ((h!=histoldest || cont) && histlines>0);
700   if (!histlines) {
701     histoldest=NULL;
702     return;
703   }
704 }
705
706 struct histinfo *makenew() {
707   struct histinfo *nh;
708   if (!histlines) {
709     nh=(struct histinfo *)malloc(sizeof (struct histinfo));
710     nh->prev=nh->next=histoldest=nh;
711     histlines++;
712   } else if (histlines<MAX_HIST_LINES) {
713     nh=(struct histinfo *)malloc(sizeof (struct histinfo));
714     nh->prev=histoldest;
715     nh->next=histoldest->next;
716     histoldest->next=nh;
717     nh->next->prev=nh;
718     histlines++;
719   } else {
720     nh=histoldest;
721     histoldest=nh->prev;
722   }
723   return nh;
724 }
725
726 #ifdef __GNUC__
727 void sendline(int yank) {
728 #else
729 void sendline(yank) {
730 #endif
731   if (!specialprompt) {
732     histcurrent->len=inputlast;
733     histcurrent->plen=plen;
734   }
735   if (!yank) {
736     input[inputlast]='\n';
737     if (printmode) formatter(input, inputlast+1);
738     if (write(writefd, input+plen, inputlast+1-plen)<inputlast+1-plen)
739       cleanupexit(1, "write error");
740   }
741   input[inputlast]='\0';
742   delempty(NULL);
743   histcurrent=makenew();
744   input=histcurrent->string;
745   strcpy(input, prompt);
746   plen=strlen(prompt);
747   inputofs=specialprompt=0;
748   modified=1;
749   inputcursor=inputlast=plen;
750   ofsredisplay(0);
751   no_echo=0;
752 }
753
754 void modify() {
755   struct histinfo *h;
756   if (!modified) {
757     if (inputlast>plen) {
758       h=histcurrent;
759       delempty(h);
760       histcurrent=makenew();
761       strcpy(histcurrent->string, h->string);
762       input=histcurrent->string;
763     }
764     modified=1;
765   }
766 }
767
768 void fixpos() {
769   if (inputcursor<8 && inputofs>0) {
770     inputofs-=cols-16;
771     inputcursor+=cols-16;
772     if (inputofs<0) {
773       inputcursor+=inputofs;
774       inputofs=0;
775     }
776     ofsredisplay(0);
777   } else if (inputcursor>cols-8) {
778     inputofs+=cols-16;
779     inputcursor-=cols-16;
780     ofsredisplay(0);
781   }
782 }
783
784 void reshow() {
785   if (inputlast>cols-8) {
786     inputcursor=cols-9;
787     inputofs=inputlast-cols+9;
788   } else {
789     inputofs=0;
790     inputcursor=inputlast;
791   }
792   ofsredisplay(0);
793 }
794
795 #ifdef __GNUC__
796 void inschar(unsigned char t) {
797 #else
798 void inschar(t)
799 unsigned char t; {
800 #endif
801
802   unsigned char *tmp;
803
804   if (inputlast<BUF_SIZE-4) {
805     modify();
806     if (inputofs+inputcursor==inputlast) {
807       write1(t, inputlast);
808       input[inputlast++]=t;
809       input[inputlast]='\0';
810       inputcursor++;
811     } else {
812       tmp=input+inputlast;
813       while (tmp>=input+inputofs+inputcursor)
814         *(tmp+1)=(*tmp--);
815       input[inputofs+(inputcursor++)]=t;
816       inputlast++;
817       ofsredisplay(inputcursor-1);
818     }
819     fixpos();
820   }
821 }
822
823 #ifdef __GNUC__
824 void dokbdchar(unsigned char t) {
825 #else
826 void dokbdchar(t)
827 unsigned char t; {
828 #endif
829
830   unsigned char *tmp;
831
832   if (inarrow==1) {
833     if (t=='[' || t=='O') {
834       inarrow++;
835       return;
836     }
837     inarrow=0;
838   } else if (inarrow==2) {
839     inarrow=0;
840     if (t=='D') t=2;
841     else if (t=='C') t=6;
842     else if (t=='A') t=16;
843     else if (t=='B') t=14;
844     else return;
845   }
846   if (ctrlx && !quote) {
847     ctrlx=0;
848     t|=0x20;
849     if (dispmode>0 && ((t=='h' && !hold_mode) || t=='y')) {
850       hold_mode=1;
851       hold_lines=0;
852       if (cursorwhere!=1) fullscroll();
853       cursorwhere=2;
854       normal();
855       gotoxy(cols-4, ystatus);
856       setinv();
857       write(1, "(h)", 3);
858       normal();
859     } else if (dispmode>0 && ((t=='h' && hold_mode) || t=='n')) {
860       hold_mode=0;
861       if (cursorwhere!=1) fullscroll();
862       cursorwhere=2;
863       normal();
864       gotoxy(cols-4, ystatus);
865       setinv();
866       write(1, "   ", 3);
867       normal();
868     } else if (dispmode>0 && t=='i') {
869       dispmode=3-dispmode;
870       bold=inv=under=0;
871     } else if (dispmode>0 && t=='b') {
872       beep=!beep;
873     } else if (t=='c') cleanupexit(1, "exiting");
874     return;
875   }
876   if (cutline) donl=1;
877   if (cursorwhere!=1) {
878     normal();
879     fullscroll();
880     gotoxy(inputcursor, yinput);
881     cursorwhere=1;
882   }
883   if (t==24 && !quote) {
884     ctrlx=1;
885     return;
886   } else ctrlx=0;
887   if (t==27 && !quote) {
888     inarrow=1;
889   } else if ((t==10 || t==13) && !quote) {  /* return, newline */
890     sendline(0);
891     if (tablines) {
892       curtabr=oldest;
893       curtabt=oldest->next;
894     }
895   } else if (t==25 && !quote) {   /* ^y */
896     if (!specialprompt) {
897       sendline(1);
898       if (tablines) {
899         curtabr=oldest;
900         curtabt=oldest->next;
901       }
902     }
903   } else if (t==21 && !quote) {   /* ^u */
904     modify();
905     input[plen]='\0';
906     inputcursor=inputlast=plen;
907     inputofs=0;
908     ofsredisplay(0);
909   } else if ((t==8 || t==0x7f) && !quote) {  /* ^h, ^? */
910     if (inputcursor>plen) {
911       modify();
912       tmp=input+inputcursor+inputofs;
913       while (tmp<input+inputlast)
914         *(tmp-1)=(*tmp++);
915       input[--inputlast]='\0';
916       gotoxy(--inputcursor, yinput);
917       ofsredisplay(inputcursor);
918       fixpos();
919     }
920   } else if (t==4 && !quote) {  /* ^d */
921     if (inputcursor+inputofs<inputlast) {
922       modify();
923       tmp=input+inputcursor+inputofs+1;
924       while (tmp<input+inputlast)
925         *(tmp-1)=(*tmp++);
926       input[--inputlast]='\0';
927       gotoxy(inputcursor, yinput);
928       ofsredisplay(inputcursor);
929     }
930   } else if (t==11 && !quote) {  /* ^k */
931     if (inputcursor+inputofs<inputlast) {
932       modify();
933       input[inputlast=inputofs+inputcursor]='\0';
934       ofsredisplay(inputcursor);
935     }
936   } else if (t==2 && !quote) {  /* ^b */
937     if (inputcursor>0 && (inputcursor>plen || inputofs>0)) {
938       gotoxy(--inputcursor, yinput);
939       fixpos();
940     }
941   } else if (t==6 && !quote) {  /* ^f */
942     if (inputcursor+inputofs<inputlast) {
943       gotoxy(++inputcursor, yinput);
944       fixpos();
945     }
946   } else if (t==1 && !quote) { /* ^a */
947     if (inputcursor+inputofs>plen) {
948       if (inputofs==0)
949         gotoxy((inputcursor=plen), yinput);
950       else {
951         inputofs=0;
952         inputcursor=plen;
953         ofsredisplay(0);
954       }
955     }
956   } else if (t==5 && !quote) { /* ^e */
957     if (inputcursor+inputofs<inputlast) {
958       if (inputlast-inputofs<cols-3) {
959         gotoxy((inputcursor=inputlast-inputofs), yinput);
960       } else if (inputlast>cols-8) {
961         inputcursor=cols-9;
962         inputofs=inputlast-cols+9;
963         ofsredisplay(0);
964       } else {
965         inputofs=0;
966         inputcursor=inputlast;
967         ofsredisplay(0);
968       }
969     }
970   } else if (t==12 && !quote) { /* ^l */
971     displaystatus();
972     ofsredisplay(0);
973   } else if (t==9 && !quote) { /* TAB */
974     if (tablines) {
975       modify();
976       strcpy(input+plen, curtabt->string);
977       curtabr=curtabt->prev;
978       curtabt=curtabt->next;
979       inputlast=strlen(input);
980       reshow();
981     }
982   } else if (t==18 && !quote) { /* ^r */
983     if (tablines) {
984       modify();
985       strcpy(input+plen, curtabr->string);
986       curtabt=curtabr->next;
987       curtabr=curtabr->prev;
988       inputlast=strlen(input);
989       reshow();
990     }
991   } else if (t==16 && !quote) { /* ^p */
992     if (histlines>1 && !specialprompt) {
993       histcurrent->plen=plen;
994       histcurrent->len=inputlast;
995       histcurrent=histcurrent->next;
996       plen=histcurrent->plen;
997       inputlast=histcurrent->len;
998       input=histcurrent->string;
999       modified=0;
1000       reshow();
1001     }
1002   } else if (t==14 && !quote) { /* ^n */
1003     if (histlines>1 && !specialprompt) {
1004       histcurrent->plen=plen;
1005       histcurrent->len=inputlast;
1006       histcurrent=histcurrent->prev;
1007       plen=histcurrent->plen;
1008       inputlast=histcurrent->len;
1009       input=histcurrent->string;
1010       modified=0;
1011       reshow();
1012     }
1013   } else if (t==15 &&!quote) { /* ^o */
1014     if (strlen(o_buffer)) modify();
1015     for (tmp=o_buffer; *tmp; tmp++) inschar(*tmp);
1016   } else if (t==20 && !quote) { /* ^t */
1017     write(writefd, ctrl_t, strlen(ctrl_t));
1018   } else if (t==22 && !quote) { /* ^v */
1019     quote++;
1020     return;
1021 #ifdef CONTROL_W
1022   } else if (t==23 && !quote) { /* ^w */
1023     fullscroll();
1024     normal();
1025     clearscreen();
1026     bold=inv=under=wherex=wherey=donl=0;
1027     displaystatus();
1028     ofsredisplay(0);
1029 #endif
1030   } else inschar(t);
1031   quote=0;
1032 }
1033
1034 #ifdef __GNUC__
1035 void barf(unsigned char *m) {
1036 #else
1037 void barf(m)
1038 unsigned char *m; {
1039 #endif
1040   fprintf(stderr, "%s\n", m);
1041   exit(1);
1042 }
1043
1044 char *myname;
1045
1046 void use() {
1047   fprintf(stderr, "Use: %s [options] program [program's options]\n", myname);
1048   fprintf(stderr, "Options are:\n");
1049   fprintf(stderr, "   -raw, -cooked, -irc  : set display mode\n");
1050   fprintf(stderr, "   -print               : print your input lines\n");
1051   fprintf(stderr, "   -prompt <prompt>     : specify a command-line prompt\n");
1052   fprintf(stderr, "   -hold                : pause after each full screen (for cooked/irc mode)\n");
1053   fprintf(stderr, "   -beep                : let beeps through (for cooked/irc mode)\n");
1054   fprintf(stderr, "   -flow                : leave ^S/^Q alone for flow control\n");
1055   exit(1);
1056 }
1057
1058 #ifdef __GNUC__
1059 int main(int argc, char *argv[]) {
1060 #else
1061 int main(argc, argv)
1062 int argc;
1063 char *argv[]; {
1064 #endif
1065
1066   char *vr;
1067   int pfds0[2], pfds1[2], pfds2[2];
1068
1069   myname=(*argv);
1070   prompt=nullstring;
1071   while (argc>1) {
1072     if (strcmp(argv[1], "-raw")==0) {
1073       dispmode=0;
1074       argv++; argc--;
1075     } else if (strcmp(argv[1], "-cooked")==0) {
1076       dispmode=1;
1077       argv++; argc--;
1078     } else if (strcmp(argv[1], "-irc")==0) {
1079       dispmode=2;
1080       argv++; argc--;
1081     } else if (strcmp(argv[1], "-hold")==0) {
1082       hold_mode=1;
1083       argv++; argc--;
1084     } else if (strcmp(argv[1], "-print")==0) {
1085       argv++; argc--;
1086       if (prompt==nullstring) prompt=defprompt;
1087       printmode=1;
1088     } else if (strcmp(argv[1], "-beep")==0) {
1089       beep=1;
1090       argv++; argc--;
1091     } else if (strcmp(argv[1], "-flow")==0) {
1092       flow=1;
1093       argv++; argc--;
1094     } else if (strcmp(argv[1], "-prompt")==0) {
1095       if (argc>2) prompt=(unsigned char *)argv[2];
1096       if (strlen(prompt)>8) barf("Prompt too long");
1097       argv+=2; argc-=2;
1098     } else break;
1099   }
1100   if (argc<2) use();
1101   if (!isatty(0)) barf("I can only run on a tty, sorry");
1102   if ((termtype=getenv("TERM"))==NULL) barf("No terminal type set");
1103   if (tgetent(termcap, termtype)<1) barf("No termcap info for your terminal");
1104   tc=capabilities;
1105   if ((t_cm=(char *)tgetstr("cm", &tc))==NULL)
1106     barf("Can't find a way to move the cursor around with your terminal");
1107   if ((t_cl=(char *)tgetstr("cl", &tc))==NULL)
1108     barf("Can't find a way to clear the screen with your terminal");
1109   if ((t_ce=(char *)tgetstr("ce", &tc))==NULL)
1110     barf("Can't find a way to clear to end of line with your terminal");
1111   if ((t_cs=(char *)tgetstr("cs", &tc))==NULL) {
1112     if (strncmp(termtype, "xterm", 5)==0 || strncmp(termtype, "vt100", 5)==0)
1113       ansi_cs=1;
1114     else
1115       barf("Can't find a way to set the scrolling region with your terminal");
1116   }
1117   if ((t_me=(char *)tgetstr("me", &tc))!=NULL) {
1118     if ((t_mr=(char *)tgetstr("mr", &tc))==NULL) t_mr=t_me;
1119     if ((t_md=(char *)tgetstr("md", &tc))==NULL) t_md=t_me;
1120     if ((t_us=(char *)tgetstr("us", &tc))==NULL) t_us=t_me;
1121   } else if ((t_me=(char *)tgetstr("se", &tc))!=NULL &&
1122              (t_mr=(char *)tgetstr("so", &tc))!=NULL) {
1123     t_md=t_mr;
1124     t_us=tc;
1125     (*tc++)='\0';
1126   } else {
1127     t_me=t_md=t_mr=t_us=tc;
1128     (*tc++)='\0';
1129   }
1130
1131 /*
1132   if ((ttyfd=open("/dev/tty", O_RDWR))<0 &&
1133       (ttyfd=open("/dev/tty", O_RDONLY))<0) barf("Can't open terminal!");
1134     */
1135     ttyfd = 0;
1136
1137 #ifdef TIOCGWINSZ
1138   if (ioctl(ttyfd, TIOCGWINSZ, &wsz)<0 || wsz.ws_row<1 || wsz.ws_col<1) {
1139 #endif
1140     lines=((vr=getenv("LINES"))?atoi(vr):0);
1141     cols=((vr=getenv("COLUMNS"))?atoi(vr):0);
1142     if (lines<1 || cols<1) {
1143       if ((lines=tgetnum("li"))<1 || (cols=tgetnum("co"))<1) {
1144         lines=24; cols=80;
1145       }
1146     }
1147 #ifdef TIOCGWINSZ
1148   } else {
1149     lines=wsz.ws_row;
1150     cols=wsz.ws_col;
1151   }
1152 #endif
1153
1154   if (pipe(pfds0)<0 || pipe(pfds1)<0 || pipe(pfds2)<0) {
1155     perror("pipe");
1156     exit(1);
1157   }
1158   mypid=getpid();
1159   switch (pid=fork()) {
1160     case -1:
1161       perror("fork");
1162       exit(1);
1163     case 0:
1164       if (pfds0[0]!=0) dup2(pfds0[0], 0);
1165       if (pfds1[1]!=1) dup2(pfds1[1], 1);
1166       if (pfds2[1]!=2) dup2(pfds2[1], 2);
1167       if (pfds0[0]>2) close(pfds0[0]);
1168       if (pfds0[1]>2) close(pfds0[1]);
1169       if (pfds1[0]>2) close(pfds1[0]);
1170       if (pfds1[1]>2) close(pfds1[1]);
1171       if (pfds2[0]>2) close(pfds2[0]);
1172       if (pfds2[1]>2) close(pfds2[1]);
1173       /* okay we can read from 0 and write to 1 and 2, now.. it seems */
1174       execvp(argv[1], argv+1);
1175       perror("exec");
1176       sleep(1);
1177       exit(1);
1178     default:
1179       close(pfds0[0]);
1180       close(pfds1[1]);
1181       close(pfds2[1]);
1182       readfd=pfds1[0];
1183       writefd=pfds0[1];
1184       errfd=pfds2[0];
1185   }
1186
1187 #ifdef USE_SGTTY
1188
1189   if (ioctl(ttyfd, TIOCGETP, &term)<0 || ioctl(ttyfd, TIOCGETC, &tch)<0 ||
1190       ioctl(ttyfd, TIOCGLTC, &lch)<0) {
1191     perror("sgtty get ioctl");
1192     exit(1);
1193   }
1194   term0=term;
1195   tch0=tch;
1196   lch0=lch;
1197   term.sg_flags|=CBREAK;
1198   term.sg_flags&= ~ECHO & ~CRMOD;
1199
1200   memset(&tch, -1, sizeof(tch));
1201   memset(&lch, -1, sizeof(lch));
1202   tch.t_intrc=(char)28;
1203   tch.t_quitc=(char)3;
1204   if (flow) {
1205     tch.t_startc=(char)17;
1206     tch.t_stopc=(char)19;
1207   }
1208   lch.t_suspc=(char)26;
1209
1210   if (ioctl(ttyfd, TIOCSETP, &term)<0 || ioctl(ttyfd, TIOCSETC, &tch)<0 ||
1211       ioctl(ttyfd, TIOCSLTC, &lch)<0) {
1212     perror("sgtty set ioctl");
1213     exit(1);
1214   }
1215
1216 #else
1217   if (tcgetattr(ttyfd, &term)<0) {
1218     perror("tcgetattr");
1219     exit(1);
1220   }
1221   term0=term;
1222
1223   term.c_lflag &= ~ECHO & ~ICANON;
1224   term.c_cc[VTIME]=(char)0;
1225   term.c_cc[VMIN]=(char)1;
1226   if (!flow) {
1227     term.c_cc[VSTOP]=(char)0;
1228     term.c_cc[VSTART]=(char)0;
1229   }
1230   term.c_cc[VQUIT]=(char)3;
1231   term.c_cc[VINTR]=(char)28; /* reverse ^c and ^\ */
1232   term.c_cc[VSUSP]=(char)26;
1233 #ifdef VREPRINT
1234   term.c_cc[VREPRINT]=(char)0;
1235 #endif
1236 #ifdef VDISCARD
1237   term.c_cc[VDISCARD]=(char)0;
1238 #endif
1239 #ifdef VLNEXT
1240   term.c_cc[VLNEXT]=(char)0;
1241 #endif
1242 #ifdef VDSUSP
1243   term.c_cc[VDSUSP]=(char)0;
1244 #endif
1245
1246   if (tcsetattr(ttyfd, TCSANOW, &term)<0) {
1247     perror("tcsetattr");
1248     exit(1);
1249   }
1250 #endif
1251
1252   allsigs();
1253
1254   ystatus=lines-2;
1255   yinput=lines-1;
1256
1257   if (lines>255) barf("Screen too big");
1258   if (ystatus<=2 || cols<20) barf("Screen too small");
1259
1260   statusline=(unsigned char *)malloc(MAX_COLS);
1261   writebuf=(unsigned char *)malloc(20*BUF_SIZE);
1262   strcpy(tmpstr, " ");
1263   for (i=1; i<argc; i++)
1264     if (strlen(tmpstr)+strlen(argv[i])<cols-1) {
1265       strcat(tmpstr, argv[i]);
1266       strcat(tmpstr, " ");
1267     }
1268   setstatus(tmpstr);
1269
1270   if (dispmode==0) wherey=ystatus-1;
1271   clearscreen();
1272   displaystatus();
1273
1274   histoldest=histcurrent=(struct histinfo *)malloc(sizeof (struct histinfo));
1275   input=histcurrent->string;
1276   histcurrent->prev=histcurrent->next=histcurrent;
1277   histlines=1;
1278   plen=strlen(prompt);
1279   inputlast=inputcursor=plen;
1280   strcpy(input, prompt);
1281   ofsredisplay(0);
1282   *protcmd='\0';
1283   *o_buffer='\0';
1284   cursorwhere=1;
1285
1286   FD_ZERO(&ready);
1287   FD_SET(ttyfd, &ready);
1288   FD_SET(readfd, &ready);
1289   FD_SET(errfd, &ready);
1290
1291   while(1) {
1292     result=ready;
1293     if (select(64, &result, NULL, NULL, NULL)<=0)
1294       if (errno==EINTR) continue;
1295       else cleanupexit(1, "select error");
1296
1297     if (FD_ISSET(readfd, &result))
1298       if ((rc=read(readfd, readbuf, BUF_SIZE))>0)
1299         doprogramline(readbuf, rc);
1300       else
1301         cleanupexit(1, "program terminated");
1302     if (FD_ISSET(errfd, &result))
1303       if ((rc=read(errfd, readbuf, BUF_SIZE))>0)
1304         doprogramline(readbuf, rc);
1305       else
1306         cleanupexit(1, "program terminated");
1307     if (FD_ISSET(ttyfd, &result))
1308       if ((rrc=read(0, readbuf, BUF_SIZE))>0)
1309         for (t=readbuf; rrc>0; rrc--) dokbdchar(*(t++));
1310       else
1311         cleanupexit(1, "read error from keyboard");
1312   }
1313 }
1314