minix/commands/elvis/curses.c
2012-03-25 21:58:27 +02:00

926 lines
19 KiB
C

/* curses.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains the functions & variables needed for a tiny subset of
* curses. The principle advantage of this version of curses is its
* extreme speed. Disadvantages are potentially larger code, few supported
* functions, limited compatibility with full curses, and only stdscr.
*/
#include "config.h"
#include "vi.h"
#if ANY_UNIX
/* The termios/termio/sgtty #ifdefs were a mess, so I removed all but termios.
* (KJB)
*/
# include <termios.h>
# if MINIX
# include <sys/ioctl.h>
# endif
#endif
#if TOS
# include <osbind.h>
#endif
#if OSK
# include <sgstat.h>
#endif
#if VMS
extern int VMS_read_raw; /* Set in initscr() */
#endif
extern char *getenv();
static void starttcap();
/* variables, publicly available & used in the macros */
char *termtype; /* name of terminal entry */
short ospeed; /* speed of the tty, eg B2400 */
#if OSK
char PC_; /* Pad char */
char *BC; /* backspace character string */
#else
char PC; /* Pad char */
#endif
WINDOW *stdscr; /* pointer into kbuf[] */
WINDOW kbuf[KBSIZ]; /* a very large output buffer */
int LINES; /* :li#: number of rows */
int COLS; /* :co#: number of columns */
int AM; /* :am: boolean: auto margins? */
int PT; /* :pt: boolean: physical tabs? */
char *VB; /* :vb=: visible bell */
char *UP; /* :up=: move cursor up */
char *SO = ""; /* :so=: standout start */
char *SE = ""; /* :se=: standout end */
char *US = ""; /* :us=: underline start */
char *UE = ""; /* :ue=: underline end */
char *MD = ""; /* :md=: bold start */
char *ME = ""; /* :me=: bold end */
char *AS = ""; /* :as=: alternate (italic) start */
char *AE = ""; /* :ae=: alternate (italic) end */
#ifndef NO_VISIBLE
char *MV; /* :mv=: "visible" selection start */
#endif
char *CM; /* :cm=: cursor movement */
char *CE; /* :ce=: clear to end of line */
char *CD; /* :cd=: clear to end of screen */
char *AL; /* :al=: add a line */
char *DL; /* :dl=: delete a line */
#if OSK
char *SR_; /* :sr=: scroll reverse */
#else
char *SR; /* :sr=: scroll reverse */
#endif
char *KS = ""; /* :ks=: init string for cursor */
char *KE = ""; /* :ke=: restore string for cursor */
char *KU; /* :ku=: key sequence sent by up arrow */
char *KD; /* :kd=: key sequence sent by down arrow */
char *KL; /* :kl=: key sequence sent by left arrow */
char *KR; /* :kr=: key sequence sent by right arrow */
char *HM; /* :HM=: key sequence sent by the <Home> key */
char *EN; /* :EN=: key sequence sent by the <End> key */
char *PU; /* :PU=: key sequence sent by the <PgUp> key */
char *PD; /* :PD=: key sequence sent by the <PgDn> key */
char *KI; /* :kI=: key sequence sent by the <Insert> key */
#ifndef NO_FKEY
char *FKEY[NFKEYS]; /* :k0=: ... :k9=: sequences sent by function keys */
#endif
char *IM = ""; /* :im=: insert mode start */
char *IC = ""; /* :ic=: insert the following character */
char *EI = ""; /* :ei=: insert mode end */
char *DC; /* :dc=: delete a character */
char *TI = ""; /* :ti=: terminal init */ /* GB */
char *TE = ""; /* :te=: terminal exit */ /* GB */
#ifndef NO_CURSORSHAPE
#if 1
char *CQ = (char *)0;/* :cQ=: normal cursor */
char *CX = (char *)1;/* :cX=: cursor used for EX command/entry */
char *CV = (char *)2;/* :cV=: cursor used for VI command mode */
char *CI = (char *)3;/* :cI=: cursor used for VI input mode */
char *CR = (char *)4;/* :cR=: cursor used for VI replace mode */
#else
char *CQ = ""; /* :cQ=: normal cursor */
char *CX = ""; /* :cX=: cursor used for EX command/entry */
char *CV = ""; /* :cV=: cursor used for VI command mode */
char *CI = ""; /* :cI=: cursor used for VI input mode */
char *CR = ""; /* :cR=: cursor used for VI replace mode */
#endif
#endif
char *aend = ""; /* end an attribute -- either UE or ME */
char ERASEKEY; /* backspace key taken from ioctl structure */
#ifndef NO_COLOR
char normalcolor[16];
char SOcolor[16];
char SEcolor[16];
char UScolor[16];
char UEcolor[16];
char MDcolor[16];
char MEcolor[16];
char AScolor[16];
char AEcolor[16];
# ifndef NO_POPUP
char POPUPcolor[16];
# endif
# ifndef NO_VISIBLE
char VISIBLEcolor[16];
# endif
#endif
#if ANY_UNIX
static struct termios oldtermio; /* original tty mode */
static struct termios newtermio; /* cbreak/noecho tty mode */
#endif
#if OSK
static struct sgbuf oldsgttyb; /* orginal tty mode */
static struct sgbuf newsgttyb; /* noecho tty mode */
#endif
static char *capbuf; /* capability string buffer */
/* Initialize the Curses package. */
void initscr()
{
/* make sure TERM variable is set */
termtype = getenv("TERM");
#if VMS
/* VMS getenv() handles TERM as a environment setting. Foreign
* terminal support can be implemented by setting the ELVIS_TERM
* logical or symbol to match a tinytcap entry.
*/
if (!strcmp(termtype,"unknown"))
termtype = getenv("ELVIS_TERM");
#endif
#if MSDOS
/* For MS-DOS, if TERM is unset we can default to "pcbios", or
* maybe "rainbow".
*/
if (!termtype)
{
#ifdef RAINBOW
if (*(unsigned char far*)(0xffff000eL) == 6 /* Rainbow 100a */
|| *(unsigned char far*)(0xffff000eL) == 148)/* Rainbow 100b */
{
termtype = "rainbow";
}
else
#endif
termtype = "pcbios";
}
if (!strcmp(termtype, "pcbios"))
#else
if (!termtype)
#endif
{
#if ANY_UNIX
write(2, "Environment variable TERM must be set\n", (unsigned)38);
exit(1);
#endif
#if OSK
writeln(2, "Environment variable TERM must be set\n", (unsigned)38);
exit(1);
#endif
#if AMIGA
termtype = TERMTYPE;
starttcap(termtype);
#endif
#if MSDOS
starttcap("pcbios");
#endif
#if TOS
termtype = "vt52";
starttcap(termtype);
#endif
#if VMS
write(2, "UNKNOWN terminal: define ELVIS_TERM\n", (unsigned)36);
exit(1);
#endif
}
else
{
#if MSDOS
*o_pcbios = 0;
#endif
/* start termcap stuff */
starttcap(termtype);
}
/* create stdscr and curscr */
stdscr = kbuf;
/* change the terminal mode to cbreak/noecho */
#if ANY_UNIX
tcgetattr(2, &oldtermio);
#endif
#if OSK
_gs_opt(0, &oldsgttyb);
#endif
#if VMS
VMS_read_raw = 1; /* cbreak/noecho */
vms_open_tty();
#endif
resume_curses(TRUE);
}
/* Shut down the Curses package. */
void endwin()
{
/* change the terminal mode back the way it was */
suspend_curses();
#if AMIGA
amiclosewin();
#endif
}
static int curses_active = FALSE;
/* Send any required termination strings. Turn off "raw" mode. */
void suspend_curses()
{
#ifndef NO_CURSORSHAPE
if (has_CQ)
{
do_CQ();
}
#endif
if (has_TE) /* GB */
{
do_TE();
}
if (has_KE)
{
do_KE();
}
#ifndef NO_COLOR
quitcolor();
#endif
refresh();
/* change the terminal mode back the way it was */
#if ANY_UNIX
tcsetattr(2, TCSADRAIN, &oldtermio);
#endif
#if OSK
_ss_opt(0, &oldsgttyb);
#endif
#if AMIGA
ttyshutdown();
#endif
#if MSDOS
raw_set_stdio(FALSE);
#endif
#if VMS
VMS_read_raw = 0;
#endif
curses_active = FALSE;
}
/* put the terminal in RAW mode. If "quietly" is FALSE, then ask the user
* to hit a key, and wait for keystroke before returning.
*/
void resume_curses(quietly)
int quietly;
{
if (!curses_active)
{
/* change the terminal mode to cbreak/noecho */
#if ANY_UNIX
ospeed = cfgetospeed(&oldtermio);
ERASEKEY = oldtermio.c_cc[VERASE];
newtermio = oldtermio;
newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
newtermio.c_oflag &= ~OPOST;
newtermio.c_lflag &= ISIG;
newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
newtermio.c_cc[VMIN] = 1;
newtermio.c_cc[VTIME] = 0;
newtermio.c_cc[VSUSP] = 0;
tcsetattr(2, TCSADRAIN, &newtermio);
#endif
#if OSK
newsgttyb = oldsgttyb;
newsgttyb.sg_echo = 0;
newsgttyb.sg_eofch = 0;
newsgttyb.sg_kbach = 0;
newsgttyb.sg_kbich = ctrl('C');
_ss_opt(0, &newsgttyb);
ospeed = oldsgttyb.sg_baud;
ERASEKEY = oldsgttyb.sg_bspch;
#endif
#if AMIGA
/* turn on window resize and RAW */
ttysetup();
#endif
#if MSDOS
raw_set_stdio(TRUE);
#endif
#if VMS
VMS_read_raw = 1;
{ int c;
read(0,&c,0); /* Flush the tty buffer. */
}
ERASEKEY = '\177'; /* Accept <DEL> as <^H> for VMS */
#endif
if (has_TI) /* GB */
{
do_TI();
}
if (has_KS)
{
do_KS();
}
curses_active = TRUE;
}
/* If we're supposed to quit quietly, then we're done */
if (quietly)
{
return;
}
signal(SIGINT, SIG_IGN);
move(LINES - 1, 0);
do_SO();
#if VMS
qaddstr("\n[Press <RETURN> to continue]");
#else
qaddstr("[Press <RETURN> to continue]");
#endif
do_SE();
refresh();
ttyread(kbuf, 20, 0); /* in RAW mode, so <20 is very likely */
if (kbuf[0] == ':')
{
mode = MODE_COLON;
addch('\n');
refresh();
}
else
{
mode = MODE_VI;
redraw(MARK_UNSET, FALSE);
}
exwrote = FALSE;
signal(SIGINT, (void(*)()) trapint);
}
/* This function fetches an optional string from termcap */
static void mayhave(T, s)
char **T; /* where to store the returned pointer */
char *s; /* name of the capability */
{
char *val;
val = tgetstr(s, &capbuf);
if (val)
{
*T = val;
}
}
/* This function fetches a required string from termcap */
static void musthave(T, s)
char **T; /* where to store the returned pointer */
char *s; /* name of the capability */
{
mayhave(T, s);
if (!*T)
{
write(2, "This termcap entry lacks the :", (unsigned)30);
write(2, s, (unsigned)2);
write(2, "=: capability\n", (unsigned)14);
#if OSK
write(2, "\l", 1);
#endif
exit(1);
}
}
/* This function fetches a pair of strings from termcap. If one of them is
* missing, then the other one is ignored.
*/
static void pair(T, U, sT, sU)
char **T; /* where to store the first pointer */
char **U; /* where to store the second pointer */
char *sT; /* name of the first capability */
char *sU; /* name of the second capability */
{
mayhave(T, sT);
mayhave(U, sU);
if (!**T || !**U)
{
*T = *U = "";
}
}
void getsize(int signo);
/* Read everything from termcap */
static void starttcap(term)
char *term;
{
static char cbmem[800];
/* allocate memory for capbuf */
capbuf = cbmem;
/* get the termcap entry */
switch (tgetent(kbuf, term))
{
case -1:
write(2, "Can't read /etc/termcap\n", (unsigned)24);
#if OSK
write(2, "\l", 1);
#endif
exit(2);
case 0:
write(2, "Unrecognized TERM type\n", (unsigned)23);
#if OSK
write(2, "\l", 1);
#endif
exit(3);
}
/* get strings */
musthave(&UP, "up");
mayhave(&VB, "vb");
musthave(&CM, "cm");
pair(&SO, &SE, "so", "se");
mayhave(&TI, "ti");
mayhave(&TE, "te");
if (tgetnum("ug") <= 0)
{
pair(&US, &UE, "us", "ue");
pair(&MD, &ME, "md", "me");
/* get italics, or have it default to underline */
pair(&AS, &AE, "as", "ae");
if (!*AS)
{
AS = US;
AE = UE;
}
}
#ifndef NO_VISIBLE
MV = SO; /* by default */
mayhave(&MV, "mv");
#endif
mayhave(&AL, "al");
mayhave(&DL, "dl");
musthave(&CE, "ce");
mayhave(&CD, "cd");
#if OSK
mayhave(&SR_, "sr");
#else
mayhave(&SR, "sr");
#endif
pair(&IM, &EI, "im", "ei");
mayhave(&IC, "ic");
mayhave(&DC, "dc");
/* other termcap stuff */
AM = (tgetflag("am") && !tgetflag("xn"));
PT = tgetflag("pt");
#if AMIGA
amiopenwin(termtype); /* Must run this before ttysetup(); */
ttysetup(); /* Must run this before getsize(0); */
#endif
getsize(0);
/* Key sequences */
pair(&KS, &KE, "ks", "ke");
mayhave(&KU, "ku"); /* up */
mayhave(&KD, "kd"); /* down */
mayhave(&KL, "kl"); /* left */
mayhave(&KR, "kr"); /* right */
mayhave(&PU, "kP"); /* PgUp */
mayhave(&PD, "kN"); /* PgDn */
mayhave(&HM, "kh"); /* Home */
mayhave(&EN, "kH"); /* End */
mayhave(&KI, "kI"); /* Insert */
#ifndef CRUNCH
if (!PU) mayhave(&PU, "K2"); /* "3x3 pad" names for PgUp, etc. */
if (!PD) mayhave(&PD, "K5");
if (!HM) mayhave(&HM, "K1");
if (!EN) mayhave(&EN, "K4");
mayhave(&PU, "PU"); /* old XENIX names for PgUp, etc. */
mayhave(&PD, "PD"); /* (overrides others, if used.) */
mayhave(&HM, "HM");
mayhave(&EN, "EN");
#endif
#ifndef NO_FKEY
mayhave(&FKEY[0], "k0"); /* function key codes */
mayhave(&FKEY[1], "k1");
mayhave(&FKEY[2], "k2");
mayhave(&FKEY[3], "k3");
mayhave(&FKEY[4], "k4");
mayhave(&FKEY[5], "k5");
mayhave(&FKEY[6], "k6");
mayhave(&FKEY[7], "k7");
mayhave(&FKEY[8], "k8");
mayhave(&FKEY[9], "k9");
# ifndef NO_SHIFT_FKEY
mayhave(&FKEY[10], "s0"); /* shift function key codes */
mayhave(&FKEY[11], "s1");
mayhave(&FKEY[12], "s2");
mayhave(&FKEY[13], "s3");
mayhave(&FKEY[14], "s4");
mayhave(&FKEY[15], "s5");
mayhave(&FKEY[16], "s6");
mayhave(&FKEY[17], "s7");
mayhave(&FKEY[18], "s8");
mayhave(&FKEY[19], "s9");
# ifndef NO_CTRL_FKEY
mayhave(&FKEY[20], "c0"); /* control function key codes */
mayhave(&FKEY[21], "c1");
mayhave(&FKEY[22], "c2");
mayhave(&FKEY[23], "c3");
mayhave(&FKEY[24], "c4");
mayhave(&FKEY[25], "c5");
mayhave(&FKEY[26], "c6");
mayhave(&FKEY[27], "c7");
mayhave(&FKEY[28], "c8");
mayhave(&FKEY[29], "c9");
# ifndef NO_ALT_FKEY
mayhave(&FKEY[30], "a0"); /* alt function key codes */
mayhave(&FKEY[31], "a1");
mayhave(&FKEY[32], "a2");
mayhave(&FKEY[33], "a3");
mayhave(&FKEY[34], "a4");
mayhave(&FKEY[35], "a5");
mayhave(&FKEY[36], "a6");
mayhave(&FKEY[37], "a7");
mayhave(&FKEY[38], "a8");
mayhave(&FKEY[39], "a9");
# endif
# endif
# endif
#endif
#ifndef NO_CURSORSHAPE
/* cursor shapes */
CQ = tgetstr("cQ", &capbuf);
if (has_CQ)
{
CX = tgetstr("cX", &capbuf);
if (!CX) CX = CQ;
CV = tgetstr("cV", &capbuf);
if (!CV) CV = CQ;
CI = tgetstr("cI", &capbuf);
if (!CI) CI = CQ;
CR = tgetstr("cR", &capbuf);
if (!CR) CR = CQ;
}
# ifndef CRUNCH
else
{
CQ = CV = "";
pair(&CQ, &CV, "ve", "vs");
CX = CI = CR = CQ;
}
# endif /* !CRUNCH */
#endif /* !NO_CURSORSHAPE */
#ifndef NO_COLOR
strcpy(SOcolor, SO);
strcpy(SEcolor, SE);
strcpy(AScolor, AS);
strcpy(AEcolor, AE);
strcpy(MDcolor, MD);
strcpy(MEcolor, ME);
strcpy(UScolor, US);
strcpy(UEcolor, UE);
# ifndef NO_POPUP
strcpy(POPUPcolor, SO);
# endif
# ifndef NO_VISIBLE
strcpy(VISIBLEcolor, MV);
# endif
#endif
}
/* This function gets the window size. It uses the TIOCGWINSZ ioctl call if
* your system has it, or tgetnum("li") and tgetnum("co") if it doesn't.
* This function is called once during initialization, and thereafter it is
* called whenever the SIGWINCH signal is sent to this process.
*/
void getsize(signo)
int signo;
{
int lines;
int cols;
#ifdef TIOCGWINSZ
struct winsize size;
#endif
#ifdef SIGWINCH
/* reset the signal vector */
signal(SIGWINCH, getsize);
#endif
/* get the window size, one way or another. */
lines = cols = 0;
#ifdef TIOCGWINSZ
if (ioctl(2, TIOCGWINSZ, &size) >= 0)
{
lines = size.ws_row;
cols = size.ws_col;
}
#endif
#if AMIGA
/* Amiga gets window size by asking the console.device */
if (!strcmp(TERMTYPE, termtype))
{
auto long len;
auto char buf[30];
Write(Output(), "\2330 q", 4); /* Ask the console.device */
len = Read(Input(), buf, 29);
buf[len] = '\000';
sscanf(&buf[5], "%d;%d", &lines, &cols);
}
#endif
if ((lines == 0 || cols == 0) && signo == 0)
{
LINES = tgetnum("li");
COLS = tgetnum("co");
}
#if MSDOS
# ifdef RAINBOW
if (!strcmp(termtype, "rainbow"))
{
/* Determine whether Rainbow is in 80-column or 132-column mode */
cols = *(unsigned char far *)0xee000f57L;
}
else
# endif
{
lines = v_rows();
cols = v_cols();
}
#endif
if (lines >= 2)
{
LINES = lines;
}
if (cols >= 30)
{
COLS = cols;
}
/* Make sure we got values that we can live with */
if (LINES < 2 || COLS < 30)
{
write(2, "Screen too small\n", (unsigned)17);
#if OSK
write(2, "\l", 1);
#endif
endwin();
exit(2);
}
#if AMIGA
if (*o_lines != LINES || *o_columns != COLS)
{
*o_lines = LINES;
*o_columns = COLS;
}
#endif
}
/* This is a function version of addch() -- it is used by tputs() */
int faddch(ch)
int ch;
{
addch(ch);
return 0;
}
/* This function quickly adds a string to the output queue. It does *NOT*
* convert \n into <CR><LF>.
*/
void qaddstr(str)
char *str;
{
REG char *s_, *d_;
#if MSDOS
if (o_pcbios[0])
{
while (*str)
qaddch(*str++);
return;
}
#endif
for (s_=(str), d_=stdscr; *d_++ = *s_++; )
{
}
stdscr = d_ - 1;
}
/* Output the ESC sequence needed to go into any video mode, if supported */
void attrset(a)
int a;
{
do_aend();
if (a == A_BOLD)
{
do_MD();
aend = ME;
}
else if (a == A_UNDERLINE)
{
do_US();
aend = UE;
}
else if (a == A_ALTCHARSET)
{
do_AS();
aend = AE;
}
else
{
aend = "";
}
}
/* Insert a single character into the display */
void insch(ch)
int ch;
{
if (has_IM)
do_IM();
do_IC();
qaddch(ch);
if (has_EI)
do_EI();
}
void wrefresh()
{
if (stdscr != kbuf)
{
VOIDBIOS(;,ttywrite(kbuf, (unsigned)(stdscr - kbuf)));
stdscr = kbuf;
}
}
void wqrefresh()
{
if (stdscr - kbuf > 2000)
{
VOIDBIOS(stdscr = kbuf,
{
ttywrite(kbuf, (unsigned)(stdscr - kbuf));
stdscr = kbuf;
});
}
}
#ifndef NO_COLOR
/* This function is called during termination. It resets color modes */
int ansiquit()
{
/* if ANSI color terminal, then reset the colors */
if (!strcmp(UP, "\033[A"))
{
tputs("\033[37;40m\033[m", 1, faddch);
clrtoeol();
return 1;
}
return 0;
}
/* This sets the color strings that work for ANSI terminals. If the TERMCAP
* doesn't look like an ANSI terminal, then it returns FALSE. If the colors
* aren't understood, it also returns FALSE. If all goes well, it returns TRUE
*/
int ansicolor(cmode, attrbyte)
int cmode; /* mode to set, e.g. A_NORMAL */
int attrbyte; /* IBM PC attribute byte */
{
char temp[16]; /* hold the new mode string */
/* if not ANSI-ish, then fail */
if (strcmp(UP, "\033[A") && strcmp(UP, "\033OA"))
{
msg("Don't know how to set colors for this terminal");
return 0;
}
/* construct the color string */
sprintf(temp, "\033[m\033[3%c;4%c%s%sm",
"04261537"[attrbyte & 0x07],
"04261537"[(attrbyte >> 4) & 0x07],
(attrbyte & 0x08) ? ";1" : "",
(attrbyte & 0x80) ? ";5" : "");
/* stick it in the right place */
switch (cmode)
{
case A_NORMAL:
if (!strcmp(MEcolor, normalcolor))
strcpy(MEcolor, temp);
if (!strcmp(UEcolor, normalcolor))
strcpy(UEcolor, temp);
if (!strcmp(AEcolor, normalcolor))
strcpy(AEcolor, temp);
if (!strcmp(SEcolor, normalcolor))
strcpy(SEcolor, temp);
strcpy(normalcolor, temp);
tputs(normalcolor, 1, faddch);
break;
case A_BOLD:
strcpy(MDcolor, temp);
strcpy(MEcolor, normalcolor);
break;
case A_UNDERLINE:
strcpy(UScolor, temp);
strcpy(UEcolor, normalcolor);
break;
case A_ALTCHARSET:
strcpy(AScolor, temp);
strcpy(AEcolor, normalcolor);
break;
case A_STANDOUT:
strcpy(SOcolor, temp);
strcpy(SEcolor, normalcolor);
break;
#ifndef NO_POPUP
case A_POPUP:
strcpy(POPUPcolor, temp);
break;
#endif
#ifndef NO_VISIBLE
case A_VISIBLE:
strcpy(VISIBLEcolor, temp);
break;
#endif
}
return 1;
}
/* This function outputs the ESC sequence needed to switch the screen back
* to "normal" mode. On color terminals which haven't had their color set
* yet, this is one of the termcap strings; for color terminals that really
* have had colors defined, we just the "normal color" escape sequence.
*/
endcolor()
{
if (aend == ME)
tputs(MEcolor, 1, faddch);
else if (aend == UE)
tputs(UEcolor, 1, faddch);
else if (aend == AE)
tputs(AEcolor, 1, faddch);
else if (aend == SE)
tputs(SEcolor, 1, faddch);
aend = "";
return 0;
}
#endif /* !NO_COLOR */