From: William F. Jolitz Date: Mon, 18 May 1992 03:27:09 +0000 (-0800) Subject: 386BSD 0.1 development X-Git-Tag: 386BSD-0.1~344 X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/commitdiff_plain/bced42fbcf3ac3011a894f02ab5fe1b690dedc35?ds=inline;hp=e43d26799d086061b8b1ccab4c5a5dc4cd429bf9 386BSD 0.1 development Work on file usr/othersrc/public/screen-3.2/screen3.2/ansi.c Work on file usr/othersrc/public/screen-3.2/screen3.2/ansi.h Work on file usr/othersrc/public/screen-3.2/screen3.2/fileio.c Work on file usr/othersrc/public/screen-3.2/screen3.2/patchlevel.h Work on file usr/othersrc/public/screen-3.2/screen3.2/putenv.c Work on file usr/othersrc/public/screen-3.2/screen3.2/screen.1 Co-Authored-By: Lynne Greer Jolitz Synthesized-from: 386BSD-0.1 --- diff --git a/usr/othersrc/public/screen-3.2/screen3.2/ansi.c b/usr/othersrc/public/screen-3.2/screen3.2/ansi.c new file mode 100644 index 0000000000..51284f168f --- /dev/null +++ b/usr/othersrc/public/screen-3.2/screen3.2/ansi.c @@ -0,0 +1,3500 @@ +/* Copyright (c) 1991 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * All rights reserved. Not derived from licensed software. + * + * Permission is granted to freely use, copy, modify, and redistribute + * this software, provided that no attempt is made to gain profit from it, + * the authors are not construed to be liable for any results of using the + * software, alterations are clearly marked as such, and this notice is + * not modified. + * + * Noteworthy contributors to screen's design and implementation: + * Wayne Davison (davison@borland.com) + * Patrick Wolfe (pat@kai.com, kailand!pat) + * Bart Schaefer (schaefer@cse.ogi.edu) + * Nathan Glasser (nathan@brokaw.lcs.mit.edu) + * Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu) + * Howard Chu (hyc@hanauma.jpl.nasa.gov) + * Tim MacKenzie (tym@dibbler.cs.monash.edu.au) + * Markku Jarvinen (mta@{cc,cs,ee}.tut.fi) + * Marc Boucher (marc@CAM.ORG) + * + **************************************************************** + */ + +#ifndef lint + static char rcs_id[] = "$Id: ansi.c,v 1.2 92/02/03 02:27:30 jnweiger Exp $ FAU"; +#endif + +#include +#include +#ifdef BSDI +#include +#endif /* BSDI */ +#include +#include "config.h" +#include "screen.h" +#include "ansi.h" +#include "extern.h" +#ifndef sun /* we want to know about TIOCGWINSZ. jw. */ +# include +#endif + +extern char *getenv(), *tgetstr(), *tgoto(); +#ifndef __STDC__ +extern char *malloc(); +#endif + +extern struct win *fore; +extern int ForeNum; +extern force_vt, assume_LP; +extern int BellDisplayed; +extern int MsgMinWait; +extern int all_norefresh; + +int TermcapROWS, TermcapCOLS; /* defaults that we learned from termcap */ +int default_width, default_height; /* width/height a new window will get */ + +int maxwidth; + +int Z0width, Z1width; /* widths for Z0/Z1 switching */ + +char display_tty[MAXPATH]; +int screenwidth, screenheight; /* width/height of the screen */ +int screentop, screenbot; /* scrollregion start/end */ +int screenx, screeny; /* cursor position */ +char GlobalAttr; /* current attributes */ +char GlobalCharset; /* current font */ +int insert; /* insert mode */ +int keypad; /* application keypad */ +int flow = 1; /* flow control */ + +int status; /* status is displayed */ +static int status_lastx, status_lasty; + +static int rows, cols; /* window size of the curr window */ + +int default_flow = -1, wrap = 1, default_monitor = 0; +int visual_bell = 0, termcapHS, use_hardstatus = 1; +char *Termcap, *extra_incap, *extra_outcap; +static int Termcaplen; +char *blank, *null, *LastMsg; +char Term[MAXSTR+5]; /* +5: "TERM=" */ +char screenterm[20] = "screen"; +char *Z0, *Z1; +int ISO2022, HS; +time_t TimeDisplayed, time(); + +/* + * the termcap routines need this to work + */ +short ospeed; +char *BC; +char *UP; + +static void AddCap __P((char *)); +static void MakeString __P((char *, char *, int, char *)); +static int Special __P((int)); +static void DoESC __P((int, int )); +static void DoCSI __P((int, int )); +static void CPutStr __P((char *, int)); +static void SetChar __P(()); +static void StartString __P((enum string_t)); +static void AddChar __P((int )); +static void PrintChar __P((int )); +static void PrintFlush __P((void)); +static void KeypadMode __P((int)); +static void DesignateCharset __P((int, int )); +static void MapCharset __P((int)); +static void SaveCursor __P((void)); +static void RestoreCursor __P((void)); +static void CountChars __P((int)); +static int CalcCost __P((char *)); +static int Rewrite __P((int, int, int, int)); +static void BackSpace __P((void)); +static void Return __P((void)); +static void LineFeed __P((int)); +static void ReverseLineFeed __P((void)); +static void InsertAChar __P((int)); +static void InsertChar __P((int)); +static void DeleteChar __P((int)); +static void DeleteLine __P((int)); +static void InsertLine __P((int)); +static void ScrollUpMap __P((int)); +static void ScrollDownMap __P((int)); +static void Scroll __P((char *, int, int, char *)); +static void ForwardTab __P((void)); +static void BackwardTab __P((void)); +static void ClearScreen __P((void)); +static void ClearFromBOS __P((void)); +static void ClearToEOS __P((void)); +static void ClearLine __P((void)); +static void ClearToEOL __P((void)); +static void ClearFromBOL __P((void)); +static void ClearInLine __P((int, int, int, int )); +static void CursorRight __P(()); +static void CursorUp __P(()); +static void CursorDown __P(()); +static void CursorLeft __P(()); +static void ASetMode __P((int)); +static void SelectRendition __P((void)); +static void FillWithEs __P((void)); +static void RedisplayLine __P((char *, char *, char *, int, int, int )); +static void FindAKA __P((void)); +static void SetCurr __P((struct win *)); +static void inpRedisplayLine __P((int, int, int, int)); +static void process_inp_input __P((char **, int *)); +static void AbortInp __P((void)); +static void AKAfin __P((char *, int)); +static void Colonfin __P((char *, int)); +static void RAW_PUTCHAR __P((int)); +static char *e_tgetstr __P((char *, char **)); +static int e_tgetflag __P((char *)); +static int e_tgetnum __P((char *)); + + +static char *tbuf, *tentry, *termname; +static char *tp; +static char *TI, *TE, *BL, *VB, *CR, *NL, *CL, *IS; +char *WS; /* used in ResizeScreen() */ +char *CE; /* used in help.c */ +static char *CM, *US, *UE, *SO, *SE, *CD, *DO, *SR, *SF, *AL; +static char *CS, *DL, *DC, *IC, *IM, *EI, *ND, *KS, *KE; +static char *MB, *MD, *MH, *MR, *ME, *PO, *PF, *HO; +static char *TS, *FS, *DS, *VI, *VE, *VS; +static char *CDC, *CDL, *CAL, *CUP, *CDO, *CLE, *CRI, *CIC; +static char *attrtab[NATTR]; +static AM, MS, COP; +int LP; +/* + * Do not confuse S0 (es-zero), E0 (e-zero) with SO (es-oh), PO (pe-oh), + * Z0 (z-zero), DO (de-oh)... :-) + */ +static char *C0, *S0, *E0; +static char c0_tab[256]; +/* + */ +static screencap = 0; +char *OldImage, *OldAttr, *OldFont; +static struct win *curr; +static display = 1; +static StrCost; +static UPcost, DOcost, LEcost, NDcost, CRcost, IMcost, EIcost, NLcost; +static tcLineLen; +static StatLen; +static lp_missing = 0; + +int in_ovl; +int ovl_blockfore; +void (*ovl_process)(); +void (*ovl_RedisplayLine)(); +int (*ovl_Rewrite)(); + +static char *KeyCaps[] = +{ + "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7", "k8", "k9", + "kb", "kd", "kh", "kl", "ko", "kr", "ku", + "K1", "K2", "K3", "K4", "K5", + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", "l8", "l9" +}; +#define NKEYCAPS ((int)(sizeof(KeyCaps)/sizeof(*KeyCaps))) +static char *KeyCapsArr[NKEYCAPS]; + +static char TermcapConst[] = "\\\n\ +\t:DO=\\E[%dB:LE=\\E[%dD:RI=\\E[%dC:UP=\\E[%dA:bs:bt=\\E[Z:\\\n\ +\t:cd=\\E[J:ce=\\E[K:cl=\\E[H\\E[J:cm=\\E[%i%d;%dH:ct=\\E[3g:\\\n\ +\t:do=^J:nd=\\E[C:pt:rc=\\E8:rs=\\Ec:sc=\\E7:st=\\EH:up=\\EM:\\\n\ +\t:le=^H:bl=^G:cr=^M:it#8:ho=\\E[H:nw=\\EE:ta=^I:is=\\E)0:xv:"; + +void +InitTermcap() +{ + register char *s; + int i; + + screencap = 0; + if ((s = getenv("SCREENCAP")) != 0) + { + if ((Termcap = malloc(strlen(s) + 10)) != 0) + { + sprintf(Termcap, "TERMCAP=%s", s); + screencap = 1; + } + } + else + Termcap = malloc((unsigned) 1024); + Termcaplen = 0; + tbuf = malloc((unsigned) 1024); + tentry = tp = malloc((unsigned) 1024); + if (!(Termcap && tbuf && tentry)) + Msg_nomem; + bzero(tbuf, 1024); + if ((termname = getenv("TERM")) == 0) + Msg(0, "No TERM in environment."); + debug1("InitTermcap: looking for tgetent('%s');\n", termname); + if (tgetent(tbuf, termname) != 1) + Msg(0, "Cannot find termcap entry for %s.", termname); + debug1("got it:\n%s\n",tbuf); +#ifdef DEBUG + if (extra_incap) + debug1("Extra incap: %s\n", extra_incap); + if (extra_outcap) + debug1("Extra outcap: %s\n", extra_outcap); +#endif + + TermcapCOLS = TermcapROWS = 0; + if (s = getenv("COLUMNS")) + TermcapCOLS = atoi(s); + if (TermcapCOLS <= 0) + TermcapCOLS = e_tgetnum("co"); + if (TermcapCOLS <= 0) + TermcapCOLS = 80; + if (s = getenv("LINES")) + TermcapROWS = atoi(s); + if (TermcapROWS <= 0) + TermcapROWS = e_tgetnum("li"); + if (TermcapROWS <= 0) + TermcapROWS = 24; + + if (e_tgetflag("hc")) + Msg(0, "You can't run screen on a hardcopy terminal."); + if (e_tgetflag("os")) + Msg(0, "You can't run screen on a terminal that overstrikes."); + if (e_tgetflag("ns")) + Msg(0, "Terminal must support scrolling."); + if (!(CL = e_tgetstr("cl", &tp))) + Msg(0, "Clear screen capability required."); + if (!(CM = e_tgetstr("cm", &tp))) + Msg(0, "Addressable cursor capability required."); + if (default_flow < 0) + default_flow = e_tgetflag("NF") ? FLOW_NOW * 0 : + e_tgetflag("xo") ? FLOW_NOW * 1 : + FLOW_AUTOFLAG; + AM = e_tgetflag("am"); + LP = assume_LP || (!extra_incap && !strncmp(termname, "vt", 2)) + || !AM || e_tgetflag("LP") || e_tgetflag("xv"); + COP = e_tgetflag("OP"); + HO = e_tgetstr("ho", &tp); + TI = e_tgetstr("ti", &tp); + TE = e_tgetstr("te", &tp); + if (!(BL = e_tgetstr("bl", &tp))) + BL = "\007"; + VB = e_tgetstr("vb", &tp); + if (!(BC = e_tgetstr("bc", &tp))) + { + if (e_tgetflag("bs")) + BC = "\b"; + else + BC = e_tgetstr("le", &tp); + } + if (!(CR = e_tgetstr("cr", &tp))) + CR = "\r"; + if (!(NL = e_tgetstr("nl", &tp))) + NL = "\n"; + IS = e_tgetstr("is", &tp); + MS = 1; + if (e_tgetnum("sg") <= 0 && e_tgetnum("ug") <= 0) + { + MS = e_tgetflag("ms"); + attrtab[ATTR_DI] = MH = e_tgetstr("mh", &tp); /* Dim */ + attrtab[ATTR_US] = US = e_tgetstr("us", &tp); /* Underline */ + attrtab[ATTR_BD] = MD = e_tgetstr("md", &tp); /* Bold */ + attrtab[ATTR_RV] = MR = e_tgetstr("mr", &tp); /* Reverse */ + attrtab[ATTR_SO] = SO = e_tgetstr("so", &tp); /* Standout */ + attrtab[ATTR_BL] = MB = e_tgetstr("mb", &tp); /* Blinking */ + ME = e_tgetstr("me", &tp); + SE = e_tgetstr("se", &tp); + UE = e_tgetstr("ue", &tp); + /* + * Does ME also reverse the effect of SO and/or US? This is not + * clearly specified by the termcap manual. Anyway, we should at + * least look whether ME and SE/UE are equal: + */ + if (UE && ((SE && strcmp(SE, UE) == 0) || (ME && strcmp(ME, UE) == 0))) + UE = 0; + if (SE && (ME && strcmp(ME, SE) == 0)) + SE = 0; + + /* Set up missing entries */ + s = 0; + for (i = NATTR-1; i >= 0; i--) + if (attrtab[i]) + s = attrtab[i]; + for (i = 0; i < NATTR; i++) + { + if (attrtab[i] == 0) + attrtab[i] = s; + else + s = attrtab[i]; + } + } + else + { + US = UE = SO = SE = MB = MD = MH = MR = ME = 0; + for (i = 0; i < NATTR; i++) + attrtab[i] = 0; + } + CE = e_tgetstr("ce", &tp); + CD = e_tgetstr("cd", &tp); + if (!(DO = e_tgetstr("do", &tp))) + DO = NL; + UP = e_tgetstr("up", &tp); + ND = e_tgetstr("nd", &tp); + SR = e_tgetstr("sr", &tp); + if (!(SF = e_tgetstr("sf", &tp))) + SF = NL; + AL = e_tgetstr("al", &tp); + DL = e_tgetstr("dl", &tp); + CS = e_tgetstr("cs", &tp); + DC = e_tgetstr("dc", &tp); + IC = e_tgetstr("ic", &tp); + CIC = e_tgetstr("IC", &tp); + CDC = e_tgetstr("DC", &tp); + CDL = e_tgetstr("DL", &tp); + CAL = e_tgetstr("AL", &tp); + CUP = e_tgetstr("UP", &tp); + CDO = e_tgetstr("DO", &tp); + CLE = e_tgetstr("LE", &tp); + CRI = e_tgetstr("RI", &tp); + IM = e_tgetstr("im", &tp); + EI = e_tgetstr("ei", &tp); + if (e_tgetflag("in")) + IC = IM = 0; + if (IC && IC[0] == '\0') + IC = 0; + if (CIC && CIC[0] == '\0') + CIC = 0; + if (IM && IM[0] == '\0') + IM = 0; + if (EI && EI[0] == '\0') + EI = 0; + if (EI == 0) + IM = 0; + if (IC && IM && strcmp(IC, IM) == 0) + IC = 0; + KS = e_tgetstr("ks", &tp); + KE = e_tgetstr("ke", &tp); + if (KE == 0) + KS = 0; + ISO2022 = e_tgetflag("G0"); + if (ISO2022) + { + if ((S0 = e_tgetstr("S0", &tp)) == NULL) +#ifdef TERMINFO + S0 = "\033(%p1%c"; +#else + S0 = "\033(%."; +#endif + if ((E0 = e_tgetstr("E0", &tp)) == NULL) + E0 = "\033(B"; + C0 = e_tgetstr("C0", &tp); + } + else if ((S0 = e_tgetstr("as", &tp)) != NULL + && (E0 = e_tgetstr("ae", &tp)) != NULL) + { + ISO2022 = 1; + C0 = e_tgetstr("ac", &tp); + } + else + { + S0 = E0 = ""; + C0 = "g.h.i'j-k-l-m-n+o~p\"q-r-s_t+u+v+w+x|y"; + } + for (i = 0; i < 256; i++) + c0_tab[i] = i; + if (C0) + for (i = strlen(C0)&~1; i >= 0; i-=2) + c0_tab[C0[i]] = C0[i+1]; + debug1("ISO2022 = %d\n", ISO2022); + /* WS changes the window size */ + WS = e_tgetstr("WS", &tp); + VI = e_tgetstr("vi", &tp); + VE = e_tgetstr("ve", &tp); + VS = e_tgetstr("vs", &tp); + PO = e_tgetstr("po", &tp); + if (!(PF = e_tgetstr("pf", &tp))) + PO = 0; + debug2("terminal size is %d, %d (says TERMCAP)\n", TermcapCOLS, TermcapROWS); + /* Termcap fields Z0 & Z1 contain width-changing sequences. */ + if ((Z0 = e_tgetstr("Z0", &tp)) != NULL + && (Z1 = e_tgetstr("Z1", &tp)) == NULL) + Z0 = NULL; + + Z0width = 132; + Z1width = 80; + + CheckScreenSize(0); + if ((HS = e_tgetflag("hs")) != 0) + { + debug("oy! we have a hardware status line, says termcap\n"); + TS = e_tgetstr("ts", &tp); + FS = e_tgetstr("fs", &tp); + DS = e_tgetstr("ds", &tp); + if ((HS = e_tgetnum("ws")) <= 0) + HS = screenwidth; + if (!TS || !FS || !DS) + HS = 0; + } + termcapHS = HS; + if (!use_hardstatus) + HS = 0; + + UPcost = CalcCost(UP); + DOcost = CalcCost(DO); + NLcost = CalcCost(NL); + LEcost = CalcCost(BC); + NDcost = CalcCost(ND); + CRcost = CalcCost(CR); + IMcost = CalcCost(IM); + EIcost = CalcCost(EI); + for (i = 0; i < NKEYCAPS; i++) + KeyCapsArr[i] = e_tgetstr(KeyCaps[i], &tp); + MakeTermcap(0); +} + +/* + * if the adaptflag is on, we keep the size of this display, else + * we may try to restore our old window sizes. + */ +void +InitTerm(adapt) +int adapt; +{ + display = 1; + screentop = screenbot = -1; + PutStr(IS); + PutStr(TI); + if (IM && strcmp(IM, EI)) + PutStr(EI); + insert = 0; + if (KS && strcmp(KS, KE)) + PutStr(KE); + keypad = 0; + PutStr(E0); + GlobalCharset = ASCII; + ResizeScreen((struct win *)0); + ChangeScrollRegion(0, screenheight-1); + PutStr(CL); + screenx = screeny = 0; + fflush(stdout); + debug1("we %swant to adapt all our windows to the display\n", + (adapt) ? "" : "don't "); + /* In case the size was changed by a init sequence */ + CheckScreenSize((adapt) ? 2 : 0); +} + +void +FinitTerm() +{ + display = 1; + InsertMode(0); + KeypadMode(0); + ResizeScreen((struct win *)0); + ChangeScrollRegion(0, screenheight - 1); + SaveSetAttr(0, ASCII); + screenx = screeny = -1; + GotoPos(0, screenheight - 1); + PutStr(TE); + fflush(stdout); + if (Termcap) + { + Free(Termcap); + debug("FinitTerm: old termcap freed\n"); + } + if (tbuf) + { + Free(tbuf); + debug("FinitTerm: old tbuf freed\n"); + } + if (tentry) + { + Free(tentry); + debug("FinitTerm: old tentry freed\n"); + } +} + +static void AddCap(s) +char *s; +{ + register int n; + + if (tcLineLen + (n = strlen(s)) > 55 && Termcaplen < 1024-4) + { + strcpy(Termcap + Termcaplen, "\\\n\t:"); + Termcaplen += 4; + tcLineLen = 0; + } + if (Termcaplen + n < 1024) + { + strcpy(Termcap + Termcaplen, s); + Termcaplen += n; + tcLineLen += n; + } + else + Msg(0, "TERMCAP overflow - sorry."); +} + +char *MakeTermcap(aflag) +int aflag; +{ + char buf[1024]; + register char *p, *cp, ch; + int i; + + if (screencap) + { + sprintf(Term, "TERM=screen"); + return Termcap; + } + if (screenterm == 0 || *screenterm == '\0') + { + debug("MakeTermcap sets screenterm=screen\n"); + strcpy(screenterm, "screen"); + } + for (;;) + { + sprintf(Term, "TERM="); + p = Term + 5; + if (!aflag && strlen(screenterm) + strlen(termname) < MAXSTR-1) + { + sprintf(p, "%s.%s", screenterm, termname); + if (tgetent(buf, p) == 1) + break; + } + if (screenwidth >= 132) + { + sprintf(p, "%s-w", screenterm); + if (tgetent(buf, p) == 1) + break; + } + sprintf(p, "%s", screenterm); + if (tgetent(buf, p) == 1) + break; + sprintf(p, "vt100"); + break; + } + tcLineLen = 100; /* Force NL */ + sprintf(Termcap, + "TERMCAP=SC|%s|VT 100/ANSI X3.64 virtual terminal|", p); + Termcaplen = strlen(Termcap); + if (extra_outcap && *extra_outcap) + { + for (cp = extra_outcap; p = index(cp, ':'); cp = p) + { + ch = *++p; + *p = '\0'; + AddCap(cp); + *p = ch; + } + tcLineLen = 100; /* Force NL */ + } + if (Termcaplen + strlen(TermcapConst) < 1024) + { + strcpy(Termcap + Termcaplen, TermcapConst); + Termcaplen += strlen(TermcapConst); + } + sprintf(buf, "li#%d:co#%d:", screenheight, screenwidth); + AddCap(buf); + if ((force_vt && !COP) || LP || !AM) + AddCap("LP:"); + else + AddCap("am:"); + if (VB) + AddCap("vb=\\E[?5h\\E[?5l:"); + if (US) + { + AddCap("us=\\E[4m:"); + AddCap("ue=\\E[24m:"); + } + if (SO) + { + AddCap("so=\\E[3m:"); + AddCap("se=\\E[23m:"); + } + if (MB) + AddCap("mb=\\E[5m:"); + if (MD) + AddCap("md=\\E[1m:"); + if (MH) + AddCap("mh=\\E[2m:"); + if (MR) + AddCap("mr=\\E[7m:"); + if (MB || MD || MH || MR) + AddCap("me=\\E[m:ms:"); + if ((CS && SR) || AL || CAL || aflag) + { + AddCap("sr=\\EM:"); + AddCap("al=\\E[L:"); + AddCap("AL=\\E[%dL:"); + } + else if (SR) + AddCap("sr=\\EM:"); + if (CS || DL || CDL || aflag) + { + AddCap("dl=\\E[M:"); + AddCap("DL=\\E[%dM:"); + } + if (CS) + AddCap("cs=\\E[%i%d;%dr:"); + if (DC || CDC || aflag) + { + AddCap("dc=\\E[P:"); + AddCap("DC=\\E[%dP:"); + } + if (CIC || IC || IM || aflag) + { + AddCap("im=\\E[4h:"); + AddCap("ei=\\E[4l:"); + AddCap("mi:"); + AddCap("ic=\\E[@:"); + AddCap("IC=\\E[%d@:"); + } + if (KS) + AddCap("ks=\\E=:"); + if (KE) + AddCap("ke=\\E>:"); + if (ISO2022) + AddCap("G0:"); + if (PO) + { + AddCap("po=\\E[5i:"); + AddCap("pf=\\E[4i:"); + } + if (Z0) + { + AddCap("Z0=\\E[?3h:"); + AddCap("Z1=\\E[?3l:"); + } + if (WS) + AddCap("WS=\\E[8;%d;%dt:"); + for (i = 0; i < NKEYCAPS; i++) + { + if (KeyCapsArr[i] == 0) + continue; + MakeString(KeyCaps[i], buf, sizeof(buf), KeyCapsArr[i]); + AddCap(buf); + } + return Termcap; +} + +static void MakeString(cap, buf, buflen, s) +char *cap, *buf; +int buflen; +char *s; +{ + register char *p, *pmax; + register unsigned int c; + + p = buf; + pmax = p + buflen - (3+4+2); + *p++ = *cap++; + *p++ = *cap; + *p++ = '='; + while ((c = *s++) && (p < pmax)) + { + switch (c) + { + case '\033': + *p++ = '\\'; + *p++ = 'E'; + break; + case ':': + sprintf(p, "\\072"); + p += 4; + break; + case '^': + case '\\': + *p++ = '\\'; + *p++ = c; + break; + default: + if (c >= 200) + { + sprintf(p, "\\%03o", c & 0377); + p += 4; + } + else if (c < ' ') + { + *p++ = '^'; + *p++ = c + '@'; + } + else + *p++ = c; + } + } + *p++ = ':'; + *p = '\0'; +} + +void +Activate(norefresh) +int norefresh; +{ + debug1("Activate(%d)\n", norefresh); + if (display) + RemoveStatus(); + display = fore->active = 1; + ResizeScreen(fore); + SetCurr(fore); + debug3("Fore (%d) has size %dx%d", ForeNum, curr->width, curr->height); + debug1("(%d)\n", curr->histheight); + ChangeScrollRegion(curr->top, curr->bot); + KeypadMode(curr->keypad); + SetFlow(curr->flow & FLOW_NOW); + if (curr->monitor != MON_OFF) + curr->monitor = MON_ON; + curr->bell = BELL_OFF; + Redisplay(norefresh || all_norefresh); +} + +void +ResetScreen(p) +register struct win *p; +{ + register int i; + + p->wrap = wrap; + p->origin = 0; + p->insert = 0; + p->vbwait = 0; + p->keypad = 0; + p->top = 0; + p->bot = p->height - 1; + p->saved = 0; + p->LocalAttr = 0; + p->x = p->y = 0; + p->state = LIT; + p->StringType = NONE; + p->ss = 0; + p->LocalCharset = G0; + bzero(p->tabs, p->width); + for (i = 8; i < p->width; i += 8) + p->tabs[i] = 1; + for (i = G0; i <= G3; i++) + p->charsets[i] = ASCII; +} + +void +WriteString(wp, buf, len) +struct win *wp; +char *buf; +int len; +{ + register int c, intermediate = 0; + + if (!len) + return; + if (wp->logfp != NULL) + if ((int)fwrite(buf, len, 1, wp->logfp) < 1) + { + extern int errno; + + Msg(errno, "Error writing logfile"); + fclose(wp->logfp); + wp->logfp = NULL; + } + /* + * SetCurr() here may prevent output, as it may set display = 0 + */ + SetCurr(wp); + if (display) + { + if (!HS) + RemoveStatus(); + } + else if (curr->monitor == MON_ON) + curr->monitor = MON_FOUND; + + do + { + c = (unsigned char)*buf++; + if (c == '\0' || c == '\177') + continue; + NextChar: + switch (curr->state) + { + case PRIN: + switch (c) + { + case '\033': + curr->state = PRINESC; + break; + default: + PrintChar(c); + } + break; + case PRINESC: + switch (c) + { + case '[': + curr->state = PRINCSI; + break; + default: + PrintChar('\033'); + PrintChar(c); + curr->state = PRIN; + } + break; + case PRINCSI: + switch (c) + { + case '4': + curr->state = PRIN4; + break; + default: + PrintChar('\033'); + PrintChar('['); + PrintChar(c); + curr->state = PRIN; + } + break; + case PRIN4: + switch (c) + { + case 'i': + curr->state = LIT; + PrintFlush(); + break; + default: + PrintChar('\033'); + PrintChar('['); + PrintChar('4'); + PrintChar(c); + curr->state = PRIN; + } + break; + case STRESC: + switch (c) + { + case '\\': + curr->state = LIT; + *(curr->stringp) = '\0'; + switch (curr->StringType) + { + case PM: + if (!display) + break; + MakeStatus(curr->string); + if (!HS && status && len > 1) + { + curr->outlen = len - 1; + bcopy(buf, curr->outbuf, curr->outlen); + return; + } + break; + case DCS: + if (display) + printf("%s", curr->string); + break; + case AKA: + if (curr->akapos == 0 && !*curr->string) + break; + strncpy(curr->cmd + curr->akapos, curr->string, 20); + if (!*curr->string) + curr->autoaka = curr->y + 1; + break; + default: + break; + } + break; + default: + curr->state = ASTR; + AddChar('\033'); + AddChar(c); + } + break; + case ASTR: + switch (c) + { + case '\0': + break; + case '\033': + curr->state = STRESC; + break; + default: + AddChar(c); + } + break; + case ESC: + switch (c) + { + case '[': + curr->NumArgs = 0; + intermediate = 0; + bzero((char *) curr->args, MAXARGS * sizeof(int)); + curr->state = CSI; + break; + case ']': + StartString(OSC); + break; + case '_': + StartString(APC); + break; + case 'P': + StartString(DCS); + break; + case '^': + StartString(PM); + break; + case '"': + case 'k': + StartString(AKA); + break; + default: + if (Special(c)) + break; + if (c >= ' ' && c <= '/') + intermediate = intermediate ? -1 : c; + else if (c >= '0' && c <= '~') + { + DoESC(c, intermediate); + curr->state = LIT; + } + else + { + curr->state = LIT; + goto NextChar; + } + } + break; + case CSI: + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (curr->NumArgs < MAXARGS) + { + curr->args[curr->NumArgs] = + 10 * curr->args[curr->NumArgs] + c - '0'; + } + break; + case ';': + case ':': + curr->NumArgs++; + break; + default: + if (Special(c)) + break; + if (c >= '@' && c <= '~') + { + curr->NumArgs++; + DoCSI(c, intermediate); + if (curr->state != PRIN) + curr->state = LIT; + } + else if ((c >= ' ' && c <= '/') || (c >= '<' && c <= '?')) + intermediate = intermediate ? -1 : c; + else + { + curr->state = LIT; + goto NextChar; + } + } + break; + case LIT: + default: + if (!Special(c)) + { + if (c == '\033') + { + intermediate = 0; + curr->state = ESC; + if (display && lp_missing && (CIC || IC || IM)) + { + RedisplayLine(blank, null, null, screenbot, + cols - 2, cols - 1); + GotoPos(curr->x, curr->y); + } + if (curr->autoaka < 0) + curr->autoaka = 0; + } + else if (c < ' ') + break; + else + { + NewRendition(curr->LocalAttr); + NewCharset(curr->charsets[(curr->ss) ? curr->ss : + curr->LocalCharset]); + if (curr->x < cols - 1) + { + if (curr->insert) + InsertAChar(c); + else + { + if (display) + PUTCHAR(c); + SetChar(c); + } + curr->x++; + } + else if (curr->x == cols - 1) + { + if (curr->wrap && (LP || !force_vt || COP)) + { + if (display) + RAW_PUTCHAR(c); + SetChar(c); + if (AM && !LP) + { + curr->x = 0; /* terminal auto-wrapped */ + LineFeed(0); + } + else + curr->x++; + } + else + { + if (display) + { + if (LP || curr->y != screenbot) + { + RAW_PUTCHAR(c); + GotoPos(curr->x, curr->y); + } + else + CheckLP(c); + } + SetChar(c); + if (curr->wrap) + curr->x++; + } + } + else + { + LineFeed(2); /* cr+lf, handle LP */ + if (curr->insert) + InsertAChar(c); + else + { + if (display) + PUTCHAR(c); + SetChar(c); + } + curr->x = 1; + } + if (curr->ss) + { + NewCharset(curr->charsets[curr->LocalCharset]); + curr->ss = 0; + } + } + } + } + } while (--len); + curr->outlen = 0; + if (curr->state == PRIN) + PrintFlush(); +} + +static int Special(c) +register int c; +{ + switch (c) + { + case '\b': + BackSpace(); + return 1; + case '\r': + Return(); + return 1; + case '\n': + if (curr->autoaka) + FindAKA(); + LineFeed(1); + return 1; + case '\007': + if (!visual_bell) + PutStr(BL); + else + { + if (!VB) + curr->bell = BELL_VISUAL; + else + PutStr(VB); + } + if (!display) + curr->bell = BELL_ON; + return 1; + case '\t': + ForwardTab(); + return 1; + case '\017': /* SI */ + MapCharset(G0); + return 1; + case '\016': /* SO */ + MapCharset(G1); + return 1; + } + return 0; +} + +static void DoESC(c, intermediate) +int c, intermediate; +{ + switch (intermediate) + { + case 0: + switch (c) + { + case 'E': + LineFeed(2); + break; + case 'D': + LineFeed(1); + break; + case 'M': + ReverseLineFeed(); + break; + case 'H': + curr->tabs[curr->x] = 1; + break; + case 'Z': /* jph: Identify as VT100 */ + Report(curr, "\033[?%d;%dc", 1, 2); + break; + case '7': + SaveCursor(); + break; + case '8': + RestoreCursor(); + break; + case 'c': + ClearScreen(); + ResetScreen(curr); + NewRendition(0); + NewCharset(ASCII); + InsertMode(0); + KeypadMode(0); + ChangeScrollRegion(0, rows-1); + break; + case '=': + KeypadMode(curr->keypad = 1); +#if !defined(TIOCPKT) || defined(sgi) + NewAutoFlow(curr, 0); +#endif /* !TIOCPKT || sgi */ + break; + case '>': + KeypadMode(curr->keypad = 0); +#if !defined(TIOCPKT) || defined(sgi) + NewAutoFlow(curr, 1); +#endif /* !TIOCPKT || sgi */ + break; + case 'n': /* LS2 */ + MapCharset(G2); + break; + case 'o': /* LS3 */ + MapCharset(G3); + break; + case 'N': /* SS2 */ + if (curr->charsets[curr->LocalCharset] != curr->charsets[G2]) + curr->ss = G2; + else + curr->ss = 0; + break; + case 'O': /* SS3 */ + if (curr->charsets[curr->LocalCharset] != curr->charsets[G3]) + curr->ss = G3; + else + curr->ss = 0; + break; + } + break; + case '#': + switch (c) + { + case '8': + FillWithEs(); + break; + } + break; + case '(': + DesignateCharset(c, G0); + break; + case ')': + DesignateCharset(c, G1); + break; + case '*': + DesignateCharset(c, G2); + break; + case '+': + DesignateCharset(c, G3); + break; + } +} + +static void DoCSI(c, intermediate) +int c, intermediate; +{ + register int i, a1 = curr->args[0], a2 = curr->args[1]; + + if (curr->NumArgs > MAXARGS) + curr->NumArgs = MAXARGS; + switch (intermediate) + { + case 0: + switch (c) + { + case 'H': + case 'f': + if (a1 < 1) + a1 = 1; + if (curr->origin) + a1 += curr->top; + if (a1 > rows) + a1 = rows; + if (a2 < 1) + a2 = 1; + if (a2 > cols) + a2 = cols; + GotoPos(--a2, --a1); + curr->x = a2; + curr->y = a1; + if (curr->autoaka) + curr->autoaka = a1 + 1; + break; + case 'J': + if (a1 < 0 || a1 > 2) + a1 = 0; + switch (a1) + { + case 0: + ClearToEOS(); + break; + case 1: + ClearFromBOS(); + break; + case 2: + ClearScreen(); + GotoPos(curr->x, curr->y); + break; + } + break; + case 'K': + if (a1 < 0 || a1 > 2) + a1 %= 3; + switch (a1) + { + case 0: + ClearToEOL(); + break; + case 1: + ClearFromBOL(); + break; + case 2: + ClearLine(); + break; + } + break; + case 'A': + CursorUp(a1 ? a1 : 1); + break; + case 'B': + CursorDown(a1 ? a1 : 1); + break; + case 'C': + CursorRight(a1 ? a1 : 1); + break; + case 'D': + CursorLeft(a1 ? a1 : 1); + break; + case 'm': + SelectRendition(); + break; + case 'g': + if (a1 == 0) + curr->tabs[curr->x] = 0; + else if (a1 == 3) + bzero(curr->tabs, cols); + break; + case 'r': + if (!a1) + a1 = 1; + if (!a2) + a2 = rows; + if (a1 < 1 || a2 > rows || a1 >= a2) + break; + curr->top = a1 - 1; + curr->bot = a2 - 1; + ChangeScrollRegion(curr->top, curr->bot); + if (curr->origin) + { + GotoPos(0, curr->top); + curr->y = curr->top; + curr->x = 0; + } + else + { + GotoPos(0, 0); + curr->y = curr->x = 0; + } + break; + case 's': + SaveCursor(); + break; + case 't': + if (a1 != 8) + break; + a1 = curr->args[2]; + if (a1 < 1) + a1 = curr->width; + if (a2 < 1) + a2 = curr->height; + if (WS == NULL) + { + a2 = curr->height; + if (Z0 == NULL || (a1 != Z0width && a1 != Z1width)) + a1 = curr->width; + } + if (a1 == curr->width && a2 == curr->height) + break; + ChangeWindowSize(curr, a1, a2); + SetCurr(curr); + if (display) + Activate(0); + break; + case 'u': + RestoreCursor(); + break; + case 'I': + if (!a1) + a1 = 1; + while (a1--) + ForwardTab(); + break; + case 'Z': + if (!a1) + a1 = 1; + while (a1--) + BackwardTab(); + break; + case 'L': + InsertLine(a1 ? a1 : 1); + break; + case 'M': + DeleteLine(a1 ? a1 : 1); + break; + case 'P': + DeleteChar(a1 ? a1 : 1); + break; + case '@': + InsertChar(a1 ? a1 : 1); + break; + case 'h': + ASetMode(1); + break; + case 'l': + ASetMode(0); + break; + case 'i': + if (PO && a1 == 5) + { + curr->stringp = curr->string; + curr->state = PRIN; + } + break; + case 'n': + if (a1 == 6) /* Report cursor position */ + Report(curr, "\033[%d;%dR", curr->y + 1, curr->x + 1); + break; + case 'c': /* Identify as VT100 */ + Report(curr, "\033[?%d;%dc", 1, 2); + break; + } + break; + case '?': + debug2("\\E[?%d%c\n",a1,c); + if (c != 'h' && c != 'l') + break; + i = (c == 'h'); + switch (a1) + { + case 3: + i = (i ? Z0width : Z1width); + if ((Z0 || WS) && curr->width != i) + { + ChangeWindowSize(curr, i, curr->height); + SetCurr(curr); + if (display) + Activate(0); + } + break; + case 5: + if (i) + curr->vbwait = 1; + else + { + if (curr->vbwait) + PutStr(VB); + curr->vbwait = 0; + } + break; + case 6: + if ((curr->origin = i) != 0) + { + GotoPos(0, curr->top); + curr->y = curr->top; + curr->x = 0; + } + else + { + GotoPos(0, 0); + curr->y = curr->x = 0; + } + break; + case 7: + curr->wrap = i; + break; + case 35: + debug1("Cursor %svisible\n", i?"in":""); + curr->cursor_invisible = i; + break; + } + break; + } +} + +void +INSERTCHAR(c) +int c; +{ + if (!insert && (IC || CIC)) + { + if (IC) + PutStr(IC); + else + CPutStr(CIC, 1); + RAW_PUTCHAR(c); + return; + } + InsertMode(1); + if (insert) + RAW_PUTCHAR(c); + else + RefreshLine(screeny, screenx, screenwidth-1); +} + +void +PUTCHAR(c) +int c; +{ + if (insert) + InsertMode(0); + RAW_PUTCHAR(c); +} + +/* + * RAW_PUTCHAR() is for all text that will be displayed. + * NOTE, that charset Nr. 0 has a conversion table, but c1, c2, ... don't. + */ + +static void +RAW_PUTCHAR(c) +int c; +{ + if (GlobalCharset == '0') + putchar(c0_tab[c]); + else + putchar(c); + if (screenx < screenwidth - 1) + screenx++; + else + { + screenx++; + if ((AM && !LP) || screenx > screenwidth) + { + screenx -= screenwidth; + if (screeny < screenheight-1 && screeny != screenbot) + screeny++; + } + } +} + +void +PutChar(c) +int c; +{ + /* this PutChar for ESC-sequences only */ + putchar(c); +} + +void +PutStr(s) +char *s; +{ + if (display && s) + tputs(s, 1, PutChar); +} + +static void CPutStr(s, c) +char *s; +int c; +{ + if (display && s) + tputs(tgoto(s, 0, c), 1, PutChar); +} + +static void SetChar(c) +register int c; +{ + register struct win *p = curr; + + p->image[p->y][p->x] = c; + p->attr[p->y][p->x] = p->LocalAttr; + p->font[p->y][p->x] = p->charsets[p->ss ? p->ss : p->LocalCharset]; +} + +static void StartString(type) +enum string_t type; +{ + curr->StringType = type; + curr->stringp = curr->string; + curr->state = ASTR; +} + +static void AddChar(c) +int c; +{ + if (curr->stringp >= curr->string + MAXSTR - 1) + curr->state = LIT; + else + *(curr->stringp)++ = c; +} + +static void PrintChar(c) +int c; +{ + if (curr->stringp >= curr->string + MAXSTR - 1) + PrintFlush(); + *(curr->stringp)++ = c; +} + +static void PrintFlush() +{ + if (curr->stringp > curr->string) + { + tputs(PO, 1, PutChar); + (void) fflush(stdout); + (void) write(1, curr->string, curr->stringp - curr->string); + tputs(PF, 1, PutChar); + (void) fflush(stdout); + curr->stringp = curr->string; + } +} + +/* Insert mode is a toggle on some terminals, so we need this hack: + */ +void +InsertMode(on) +int on; +{ + if (display && on != insert && IM) + { + insert = on; + if (insert) + PutStr(IM); + else + PutStr(EI); + } +} + +/* ...and maybe keypad application mode is a toggle, too: + */ +static void KeypadMode(on) +int on; +{ + if (display && keypad != on && KS) + { + keypad = on; + if (keypad) + PutStr(KS); + else + PutStr(KE); + } +} + +void +NewAutoFlow(win, on) +struct win *win; +int on; +{ + debug1("NewAutoFlow: %d\n", on); + SetCurr(win); + if (win->flow & FLOW_AUTOFLAG) + win->flow = FLOW_AUTOFLAG | (FLOW_AUTO|FLOW_NOW) * on; + else + win->flow = (win->flow & ~FLOW_AUTO) | FLOW_AUTO * on; + if (display) + SetFlow(win->flow & FLOW_NOW); +} + +static void DesignateCharset(c, n) +int c, n; +{ + curr->ss = 0; + if (c == 'B') + c = ASCII; + if (curr->charsets[n] != c) + { + curr->charsets[n] = c; + if (curr->LocalCharset == n) + NewCharset(c); + } +} + +static void MapCharset(n) +int n; +{ + curr->ss = 0; + if (curr->LocalCharset != n) + { + curr->LocalCharset = n; + NewCharset(curr->charsets[n]); + } +} + +void +NewCharset(new) +int new; +{ + if (!display || GlobalCharset == new) + return; + GlobalCharset = new; + if (new == ASCII) + PutStr(E0); + else + CPutStr(S0, new); +} + +static void SaveCursor() +{ + curr->saved = 1; + curr->Saved_x = curr->x; + curr->Saved_y = curr->y; + curr->SavedLocalAttr = curr->LocalAttr; + curr->SavedLocalCharset = curr->LocalCharset; + bcopy((char *) curr->charsets, (char *) curr->SavedCharsets, + 4 * sizeof(int)); +} + +static void RestoreCursor() +{ + if (curr->saved) + { + GotoPos(curr->Saved_x, curr->Saved_y); + curr->x = curr->Saved_x; + curr->y = curr->Saved_y; + curr->LocalAttr = curr->SavedLocalAttr; + NewRendition(curr->LocalAttr); + bcopy((char *) curr->SavedCharsets, (char *) curr->charsets, + 4 * sizeof(int)); + curr->LocalCharset = curr->SavedLocalCharset; + NewCharset(curr->charsets[curr->LocalCharset]); + } +} + +/*ARGSUSED*/ +static void CountChars(c) +int c; +{ + StrCost++; +} + +static int CalcCost(s) +register char *s; +{ + if (s) + { + StrCost = 0; + tputs(s, 1, CountChars); + return StrCost; + } + else + return EXPENSIVE; +} + +void +GotoPos(x2, y2) +int x2, y2; +{ + register int dy, dx, x1, y1; + register int costx, costy; + register int m; + register char *s; + int CMcost; + enum move_t xm = M_NONE, ym = M_NONE; + + if (!display) + return; + + x1 = screenx; + y1 = screeny; + + if (x1 == screenwidth) + if (LP && AM) + x1 = -1; /* don't know how the terminal treats this */ + else + x1--; + if (x2 == screenwidth) + x2--; + dx = x2 - x1; + dy = y2 - y1; + if (dy == 0 && dx == 0) + { + return; + } + if (!MS && GlobalAttr) /* Safe to move in SO mode ? */ + NewRendition(0); + if (y1 < 0 /* don't know the y position */ + || (y2 > screenbot && y1 <= screenbot) /* have to cross border */ + || (y2 < screentop && y1 >= screentop)) /* of scrollregion ? */ + { + DoCM: + if (HO && !x2 && !y2) + PutStr(HO); + else + PutStr(tgoto(CM, x2, y2)); + screenx = x2; + screeny = y2; + return; + } + /* Calculate CMcost */ + if (HO && !x2 && !y2) + s = HO; + else + s = tgoto(CM, x2, y2); + CMcost = CalcCost(s); + + /* Calculate the cost to move the cursor to the right x position */ + costx = EXPENSIVE; + if (x1 >= 0) /* relativ x positioning only if we know where we are */ + { + if (dx > 0) + { + if (CRI && (dx > 1 || !ND)) + { + costx = CalcCost(tgoto(CRI, 0, dx)); + xm = M_CRI; + } + if ((m = NDcost * dx) < costx) + { + costx = m; + xm = M_RI; + } + /* Speedup: dx <= Rewrite() */ + if (dx < costx && (m = Rewrite(y1, x1, x2, 0)) < costx) + { + costx = m; + xm = M_RW; + } + } + else if (dx < 0) + { + if (CLE && (dx < -1 || !BC)) + { + costx = CalcCost(tgoto(CLE, 0, -dx)); + xm = M_CLE; + } + if ((m = -dx * LEcost) < costx) + { + costx = m; + xm = M_LE; + } + } + else + costx = 0; + } + /* Speedup: Rewrite() >= x2 */ + if (x2 + CRcost < costx && (m = Rewrite(y1, 0, x2, 0) + CRcost) < costx) + { + costx = m; + xm = M_CR; + } + + /* Check if it is already cheaper to do CM */ + if (costx >= CMcost) + goto DoCM; + + /* Calculate the cost to move the cursor to the right y position */ + costy = EXPENSIVE; + if (dy > 0) + { + if (CDO && dy > 1) /* DO & NL are always != 0 */ + { + costy = CalcCost(tgoto(CDO, 0, dy)); + ym = M_CDO; + } + if ((m = dy * ((x2 == 0) ? NLcost : DOcost)) < costy) + { + costy = m; + ym = M_DO; + } + } + else if (dy < 0) + { + if (CUP && (dy < -1 || !UP)) + { + costy = CalcCost(tgoto(CUP, 0, -dy)); + ym = M_CUP; + } + if ((m = -dy * UPcost) < costy) + { + costy = m; + ym = M_UP; + } + } + else + costy = 0; + + /* Finally check if it is cheaper to do CM */ + if (costx + costy >= CMcost) + goto DoCM; + + switch (xm) + { + case M_LE: + while (dx++ < 0) + PutStr(BC); + break; + case M_CLE: + CPutStr(CLE, -dx); + break; + case M_RI: + while (dx-- > 0) + PutStr(ND); + break; + case M_CRI: + CPutStr(CRI, dx); + break; + case M_CR: + PutStr(CR); + screenx = 0; + x1 = 0; + /* FALLTHROUGH */ + case M_RW: + if (x1 < x2) + (void) Rewrite(y1, x1, x2, 1); + break; + default: + break; + } + switch (ym) + { + case M_UP: + while (dy++ < 0) + PutStr(UP); + break; + case M_CUP: + CPutStr(CUP, -dy); + break; + case M_DO: + s = (x2 == 0) ? NL : DO; + while (dy-- > 0) + PutStr(s); + break; + case M_CDO: + CPutStr(CDO, dy); + break; + default: + break; + } + screenx = x2; + screeny = y2; +} + +static int +Rewrite(y, x1, x2, doit) +int y, x1, x2, doit; +{ + register int cost, dx; + register char *p, *f, *i; + + if (x1 == x2) + return(0); + if (in_ovl) + { + if (ovl_Rewrite == 0) + return EXPENSIVE; + else + return ((*ovl_Rewrite)(y, x1, x2, doit)); + } + dx = x2 - x1; + if (doit) + { + i = curr->image[y] + x1; + while (dx-- > 0) + PUTCHAR(*i++); + return(0); + } + p = curr->attr[y] + x1; + f = curr->font[y] + x1; + + cost = dx = x2 - x1; + if (insert) + cost += EIcost + IMcost; + while(dx-- > 0) + { + if (*p++ != GlobalAttr || *f++ != GlobalCharset) + return EXPENSIVE; + } + return cost; +} + +static void BackSpace() +{ + if (curr->x > 0) + { + curr->x--; + } + else if (curr->wrap && curr->y > 0) + { + curr->x = cols - 1; + curr->y--; + } + if (display) + GotoPos(curr->x, curr->y); +} + +static void Return() +{ + if (curr->x > 0) + { + curr->x = 0; + if (display) + GotoPos(curr->x, curr->y); + } +} + +static void LineFeed(out_mode) +int out_mode; +{ + /* out_mode: 0=no-output lf, 1=lf, 2=cr+lf */ + if (out_mode == 2) + curr->x = 0; + if (curr->y != curr->bot) /* Don't scroll */ + { + if (curr->y < rows-1) + curr->y++; + if (out_mode && display) + GotoPos(curr->x, curr->y); + return; + } + ScrollUpMap(1); + if (curr->autoaka > 1) + curr->autoaka--; + if (out_mode && display) + { + ScrollRegion(curr->top, curr->bot, 1); + GotoPos(curr->x, curr->y); + } +} + +static void ReverseLineFeed() +{ + if (curr->y == curr->top) + { + ScrollDownMap(1); + if (!display) + return; + ScrollRegion(curr->top, curr->bot, -1); + GotoPos(curr->x, curr->y); + } + else if (curr->y > 0) + CursorUp(1); +} + +static void InsertAChar(c) +int c; +{ + register int y = curr->y, x = curr->x; + + if (x == cols) + x--; + bcopy(curr->image[y], OldImage, cols); + bcopy(curr->attr[y], OldAttr, cols); + bcopy(curr->font[y], OldFont, cols); + bcopy(curr->image[y] + x, curr->image[y] + x + 1, cols - x - 1); + bcopy(curr->attr[y] + x, curr->attr[y] + x + 1, cols - x - 1); + bcopy(curr->font[y] + x, curr->font[y] + x + 1, cols - x - 1); + SetChar(c); + if (!display) + return; + if (CIC || IC || IM) + { + InsertMode(curr->insert); + INSERTCHAR(c); + if (y == screenbot) + lp_missing = 0; + } + else + { + RedisplayLine(OldImage, OldAttr, OldFont, y, x, cols - 1); + GotoPos(++x, y); + } +} + +static void InsertChar(n) +int n; +{ + register int i, y = curr->y, x = curr->x; + + if (n <= 0) + return; + /* + * The termcap manual states that only one of IM and IC is + * to be defined unless the terminal needs both sequences. + * We don't like this because we think that there may be cases + * where it is preferable to send IC instead of IM/EI. + * The hack is to ignore the IC sequence if we are already + * in insert mode, so that programs which follow the termcap + * guidelines still work. (I don't believe that there are + * terminals which need IC in the insert mode. Why switch to + * insert mode if you must send IC before every character ?) + */ + if (curr->insert) + return; + if (x == cols) + --x; + bcopy(curr->image[y], OldImage, cols); + bcopy(curr->attr[y], OldAttr, cols); + bcopy(curr->font[y], OldFont, cols); + if (n > cols - x) + n = cols - x; + bcopy(curr->image[y] + x, curr->image[y] + x + n, cols - x - n); + bcopy(curr->attr[y] + x, curr->attr[y] + x + n, cols - x - n); + bcopy(curr->font[y] + x, curr->font[y] + x + n, cols - x - n); + ClearInLine(0, y, x, x + n - 1); + if (!display) + return; + if (IC || CIC || IM) + { + if (y == screenbot) + lp_missing = 0; + if (!insert) + { + if (n == 1 && IC) + { + PutStr(IC); + return; + } + if (CIC) + { + CPutStr(CIC, n); + return; + } + } + InsertMode(1); + for (i = n; i--; ) + INSERTCHAR(' '); + GotoPos(x, y); + } + else + { + RedisplayLine(OldImage, OldAttr, OldFont, y, x, cols - 1); + GotoPos(x, y); + } +} + +static void DeleteChar(n) +int n; +{ + register int i, y = curr->y, x = curr->x; + + if (x == cols) + --x; + bcopy(curr->image[y], OldImage, cols); + bcopy(curr->attr[y], OldAttr, cols); + bcopy(curr->font[y], OldFont, cols); + if (n > cols - x) + n = cols - x; + bcopy(curr->image[y] + x + n, curr->image[y] + x, cols - x - n); + bcopy(curr->attr[y] + x + n, curr->attr[y] + x, cols - x - n); + bcopy(curr->font[y] + x + n, curr->font[y] + x, cols - x - n); + ClearInLine(0, y, cols - n, cols - 1); + if (!display) + return; + if (CDC && !(n == 1 && DC)) + { + CPutStr(CDC, n); + if (lp_missing && y == screenbot) + { + FixLP(cols - 1 - n, y); + GotoPos(x, y); + } + } + else if (DC) + { + for (i = n; i; i--) + PutStr(DC); + if (lp_missing && y == screenbot) + { + FixLP(cols - 1 - n, y); + GotoPos(x, y); + } + } + else + { + RedisplayLine(OldImage, OldAttr, OldFont, y, x, cols - 1); + GotoPos(x, y); + } +} + +static void DeleteLine(n) +int n; +{ + register int old = curr->top; + + if (curr->y < curr->top || curr->y > curr->bot) + return; + if (n > curr->bot - curr->y + 1) + n = curr->bot - curr->y + 1; + curr->top = curr->y; + ScrollUpMap(n); + curr->top = old; + if (!display) + return; + ScrollRegion(curr->y, curr->bot, n); + GotoPos(curr->x, curr->y); +} + +static void InsertLine(n) +int n; +{ + register int old = curr->top; + + if (curr->y < curr->top || curr->y > curr->bot) + return; + if (n > curr->bot - curr->y + 1) + n = curr->bot - curr->y + 1; + curr->top = curr->y; + ScrollDownMap(n); + curr->top = old; + if (!display) + return; + ScrollRegion(curr->y, curr->bot, -n); + GotoPos(curr->x, curr->y); +} + +void +ScrollRegion(ys, ye, n) +int ys, ye, n; +{ + int i; + int up; + int oldtop, oldbot; + int alok, dlok, aldlfaster; + int missy = 0; + + if (n == 0) + return; + if (ys == 0 && ye == screenheight-1 && + (n >= screenheight || -n >= screenheight)) + { + PutStr(CL); + screeny = screenx = 0; + lp_missing = 0; + return; + } + + if (lp_missing) + { + if (screenbot>ye || screenbotye || missy= ye-ys+1) + n = ye-ys+1; + + oldtop = screentop; + oldbot = screenbot; + if (screenbot != ye) + ChangeScrollRegion(ys, ye); + alok = (AL || CAL || (ye == screenbot && up)); + dlok = (DL || CDL || (ye == screenbot && !up)); + if (screentop != ys && !(alok && dlok)) + ChangeScrollRegion(ys, ye); + + if (lp_missing && + (oldbot != screenbot || + (oldbot == screenbot && up && screentop == ys && screenbot == ye))) + { + /* Can't use FixLP */ + GotoPos(screenwidth-1, oldbot); + SaveSetAttr(curr->attr[missy][screenwidth-1], curr->font[missy][screenwidth-1]); + PUTCHAR(curr->image[missy][screenwidth-1]); + RestoreAttr(); + lp_missing = 0; + if (oldbot == screenbot) /* have scrolled */ + { + if (--n == 0) + { + ChangeScrollRegion(oldtop, oldbot); + return; + } + } + } + + aldlfaster = (n > 1 && ye == screenbot && ((up && CDL) || (!up && CAL))); + + if ((up || SR) && screentop == ys && screenbot == ye && !aldlfaster) + { + if (up) + { + GotoPos(0, ye); + while (n-- > 0) + PutStr(NL); /* was SF, I think NL is faster */ + } + else + { + GotoPos(0, ys); + while (n-- > 0) + PutStr(SR); + } + } + else if (alok && dlok) + { + if (up || ye != screenbot) + { + GotoPos(0, up ? ys : ye+1-n); + if (CDL && !(n == 1 && DL)) + CPutStr(CDL, n); + else + for(i=n; i--; ) + PutStr(DL); + } + if (!up || ye != screenbot) + { + GotoPos(0, up ? ye+1-n : ys); + if (CAL && !(n == 1 && AL)) + CPutStr(CAL, n); + else + for(i=n; i--; ) + PutStr(AL); + } + } + else + { + Redisplay(0); + return; + } + if (lp_missing && missy != screenbot) + FixLP(screenwidth-1, missy); + ChangeScrollRegion(oldtop, oldbot); + if (lp_missing && missy != screenbot) + FixLP(screenwidth-1, missy); +} + +static void ScrollUpMap(n) +int n; +{ + char tmp[256 * sizeof(char *)]; + register int ii, i, cnt1, cnt2; + register char **ppi, **ppa, **ppf; + + i = curr->top + n; + cnt1 = n * sizeof(char *); + cnt2 = (curr->bot - i + 1) * sizeof(char *); + ppi = curr->image + i; + ppa = curr->attr + i; + ppf = curr->font + i; + for(ii = curr->top; ii < i; ii++) + AddLineToHist(curr, &curr->image[ii], &curr->attr[ii], &curr->font[ii]); + for (i = n; i; --i) + { + bclear(*--ppi, cols); + bzero(*--ppa, cols); + bzero(*--ppf, cols); + } + Scroll((char *) ppi, cnt1, cnt2, tmp); + Scroll((char *) ppa, cnt1, cnt2, tmp); + Scroll((char *) ppf, cnt1, cnt2, tmp); +} + +static void ScrollDownMap(n) +int n; +{ + char tmp[256 * sizeof(char *)]; + register int i, cnt1, cnt2; + register char **ppi, **ppa, **ppf; + + i = curr->top; + cnt1 = (curr->bot - i - n + 1) * sizeof(char *); + cnt2 = n * sizeof(char *); + Scroll((char *) (ppi = curr->image + i), cnt1, cnt2, tmp); + Scroll((char *) (ppa = curr->attr + i), cnt1, cnt2, tmp); + Scroll((char *) (ppf = curr->font + i), cnt1, cnt2, tmp); + for (i = n; i; --i) + { + bclear(*ppi++, cols); + bzero(*ppa++, cols); + bzero(*ppf++, cols); + } +} + +static void Scroll(cp, cnt1, cnt2, tmp) +char *cp, *tmp; +int cnt1, cnt2; +{ + if (!cnt1 || !cnt2) + return; + if (cnt1 <= cnt2) + { + bcopy(cp, tmp, cnt1); + bcopy(cp + cnt1, cp, cnt2); + bcopy(tmp, cp + cnt2, cnt1); + } + else + { + bcopy(cp + cnt1, tmp, cnt2); + bcopy(cp, cp + cnt2, cnt1); + bcopy(tmp, cp, cnt2); + } +} + +static void ForwardTab() +{ + register int x = curr->x; + + if (x == cols) + { + LineFeed(2); + x = 0; + } + if (curr->tabs[x] && x < cols - 1) + x++; + while (x < cols - 1 && !curr->tabs[x]) + x++; + GotoPos(x, curr->y); + curr->x = x; +} + +static void BackwardTab() +{ + register int x = curr->x; + + if (curr->tabs[x] && x > 0) + x--; + while (x > 0 && !curr->tabs[x]) + x--; + GotoPos(x, curr->y); + curr->x = x; +} + +static void ClearScreen() +{ + register int i; + register char **ppi = curr->image, **ppa = curr->attr, **ppf = curr->font; + + for (i = 0; i < rows; ++i) + { + AddLineToHist(curr, ppi, ppa, ppf); + bclear(*ppi++, cols); + bzero(*ppa++, cols); + bzero(*ppf++, cols); + } + if (display) + { + PutStr(CL); + screenx = screeny = 0; + lp_missing = 0; + } +} + +static void ClearFromBOS() +{ + register int n, y = curr->y, x = curr->x; + + for (n = 0; n < y; ++n) + ClearInLine(1, n, 0, cols - 1); + ClearInLine(1, y, 0, x); + GotoPos(x, y); + RestoreAttr(); +} + +static void ClearToEOS() +{ + register int n, y = curr->y, x = curr->x; + + if (!y && !x) + { + ClearScreen(); + return; + } + if (display && CD) + { + PutStr(CD); + lp_missing = 0; + } + ClearInLine(!CD, y, x, cols - 1); + for (n = y + 1; n < rows; n++) + ClearInLine(!CD, n, 0, cols - 1); + GotoPos(x, y); + RestoreAttr(); +} + +static void ClearLine() +{ + register int y = curr->y, x = curr->x; + + ClearInLine(1, y, 0, cols - 1); + GotoPos(x, y); + RestoreAttr(); +} + +static void ClearToEOL() +{ + register int y = curr->y, x = curr->x; + + ClearInLine(1, y, x, cols - 1); + GotoPos(x, y); + RestoreAttr(); +} + +static void ClearFromBOL() +{ + register int y = curr->y, x = curr->x; + + ClearInLine(1, y, 0, x); + GotoPos(x, y); + RestoreAttr(); +} + +static void ClearInLine(displ, y, x1, x2) +int displ, y, x1, x2; +{ + register int n; + + if (x1 == cols) + x1--; + if (x2 == cols) + x2--; + if ((n = x2 - x1 + 1) != 0) + { + if (displ && display) + { + if (x2 == cols - 1 && CE) + { + GotoPos(x1, y); + PutStr(CE); + if (y == screenbot) + lp_missing = 0; + } + else + DisplayLine(curr->image[y], curr->attr[y], curr->font[y], + blank, null, null, y, x1, x2); + } + if (curr) + { + bclear(curr->image[y] + x1, n); + bzero(curr->attr[y] + x1, n); + bzero(curr->font[y] + x1, n); + } + } +} + +static void CursorRight(n) +register int n; +{ + register int x = curr->x; + + if (x == cols) + { + LineFeed(2); + x = 0; + } + if ((curr->x += n) >= cols) + curr->x = cols - 1; + GotoPos(curr->x, curr->y); +} + +static void CursorUp(n) +register int n; +{ + if (curr->y < curr->top) /* if above scrolling rgn, */ + { + if ((curr->y -= n) < 0) /* ignore its limits */ + curr->y = 0; + } + else + if ((curr->y -= n) < curr->top) + curr->y = curr->top; + GotoPos(curr->x, curr->y); +} + +static void CursorDown(n) +register int n; +{ + if (curr->y > curr->bot) /* if below scrolling rgn, */ + { + if ((curr->y += n) > rows - 1) /* ignore its limits */ + curr->y = rows - 1; + } + else + if ((curr->y += n) > curr->bot) + curr->y = curr->bot; + GotoPos(curr->x, curr->y); +} + +static void CursorLeft(n) +register int n; +{ + if ((curr->x -= n) < 0) + curr->x = 0; + GotoPos(curr->x, curr->y); +} + +static void ASetMode(on) +int on; +{ + register int i; + + for (i = 0; i < curr->NumArgs; ++i) + { + switch (curr->args[i]) + { + case 4: + curr->insert = on; + InsertMode(on); + break; + } + } +} + +static void SelectRendition() +{ + register int i = 0, a = curr->LocalAttr; + + do + { + switch (curr->args[i]) + { + case 0: + a = 0; + break; + case 1: + a |= A_BD; + break; + case 2: + a |= A_DI; + break; + case 3: + a |= A_SO; + break; + case 4: + a |= A_US; + break; + case 5: + a |= A_BL; + break; + case 7: + a |= A_RV; + break; + case 22: + a &= ~(A_BD | A_SO | A_DI); + break; + case 23: + a &= ~A_SO; + break; + case 24: + a &= ~A_US; + break; + case 25: + a &= ~A_BL; + break; + case 27: + a &= ~A_RV; + break; + } + } while (++i < curr->NumArgs); + NewRendition(curr->LocalAttr = a); +} + +void +NewRendition(new) +register int new; +{ + register int i, old = GlobalAttr; + + if (!display || old == new) + return; + GlobalAttr = new; + for (i = 1; i <= A_MAX; i <<= 1) + { + if ((old & i) && !(new & i)) + { + PutStr(UE); + PutStr(SE); + PutStr(ME); + if (new & A_DI) + PutStr(attrtab[ATTR_DI]); + if (new & A_US) + PutStr(attrtab[ATTR_US]); + if (new & A_BD) + PutStr(attrtab[ATTR_BD]); + if (new & A_RV) + PutStr(attrtab[ATTR_RV]); + if (new & A_SO) + PutStr(attrtab[ATTR_SO]); + if (new & A_BL) + PutStr(attrtab[ATTR_BL]); + return; + } + } + if ((new & A_DI) && !(old & A_DI)) + PutStr(attrtab[ATTR_DI]); + if ((new & A_US) && !(old & A_US)) + PutStr(attrtab[ATTR_US]); + if ((new & A_BD) && !(old & A_BD)) + PutStr(attrtab[ATTR_BD]); + if ((new & A_RV) && !(old & A_RV)) + PutStr(attrtab[ATTR_RV]); + if ((new & A_SO) && !(old & A_SO)) + PutStr(attrtab[ATTR_SO]); + if ((new & A_BL) && !(old & A_BL)) + PutStr(attrtab[ATTR_BL]); +} + +void +SaveSetAttr(newattr, newcharset) +int newattr, newcharset; +{ + NewRendition(newattr); + NewCharset(newcharset); +} + +void +RestoreAttr() +{ + NewRendition(curr->LocalAttr); + NewCharset(curr->charsets[curr->LocalCharset]); +} + +static void FillWithEs() +{ + register int i; + register char *p, *ep; + + curr->y = curr->x = 0; + for (i = 0; i < rows; ++i) + { + bzero(curr->attr[i], cols); + bzero(curr->font[i], cols); + p = curr->image[i]; + ep = p + cols; + while (p < ep) + *p++ = 'E'; + } + if (display) + Redisplay(0); +} + +/* + * if cur_only, we only redisplay current line, as a full refresh is + * too expensive. + */ +void Redisplay(cur_only) +int cur_only; +{ + register int i, stop; + + PutStr(CL); + screenx = screeny = 0; + lp_missing = 0; + stop = rows; i = 0; + if (cur_only) + { + i = stop = curr->y; + stop++; + } + for (; i < stop; ++i) + { + if (in_ovl) + (*ovl_RedisplayLine)(i, 0, cols - 1, 1); + else + DisplayLine(blank, null, null, curr->image[i], curr->attr[i], + curr->font[i], i, 0, cols - 1); + } + if (!in_ovl) + { + GotoPos(curr->x, curr->y); + NewRendition(curr->LocalAttr); + NewCharset(curr->charsets[curr->LocalCharset]); + } +} + +void +DisplayLine(os, oa, of, s, as, fs, y, from, to) +int from, to, y; +register char *os, *oa, *of, *s, *as, *fs; +{ + register int x; + int last2flag = 0, delete_lp = 0; + + if (!LP && y == screenbot && to == cols - 1) + if (lp_missing + || s[to] != os[to] || as[to] != oa[to] || of[to] != fs[to]) + { + if ((IC || IM) && (from < to || !in_ovl)) + { + if ((to -= 2) < from - 1) + from--; + last2flag = 1; + lp_missing = 0; + } + else + { + to--; + delete_lp = (CE || DC || CDC); + lp_missing = (s[to] != ' ' || as[to] || fs[to]); + } + } + else + to--; + for (x = from; x <= to; ++x) + { + if (s[x] == os[x] && as[x] == oa[x] && of[x] == fs[x]) + continue; + GotoPos(x, y); + NewRendition(as[x]); + NewCharset(fs[x]); + PUTCHAR(s[x]); + } + if (last2flag) + { + GotoPos(x, y); + NewRendition(as[x + 1]); + NewCharset(fs[x + 1]); + PUTCHAR(s[x + 1]); + GotoPos(x, y); + NewRendition(as[x]); + NewCharset(fs[x]); + INSERTCHAR(s[x]); + } + else if (delete_lp) + { + if (DC) + PutStr(DC); + else if (CDC) + CPutStr(CDC, 1); + else if (CE) + PutStr(CE); + } +} + +void +RefreshLine(y, from, to) +int y, from, to; +{ + char *oi = null; + + if (CE && to == screenwidth-1) + { + GotoPos(from, y); + PutStr(CE); + oi = blank; + } + if (in_ovl) + (*ovl_RedisplayLine)(y, from, to, (oi == blank)); + else + DisplayLine(oi, null, null, curr->image[y], curr->attr[y], + curr->font[y], y, from, to); +} + +static void RedisplayLine(os, oa, of, y, from, to) +int from, to, y; +char *os, *oa, *of; +{ + DisplayLine(os, oa, of, curr->image[y], curr->attr[y], + curr->font[y], y, from, to); + NewRendition(curr->LocalAttr); + NewCharset(curr->charsets[curr->LocalCharset]); +} + +void +FixLP(x2, y2) +register int x2, y2; +{ + register struct win *p = curr; + + GotoPos(x2, y2); + SaveSetAttr(p->attr[y2][x2], p->font[y2][x2]); + PUTCHAR(p->image[y2][x2]); + RestoreAttr(); + lp_missing = 0; +} + +void +CheckLP(n_ch) +char n_ch; +{ + register int y = screenbot, x = cols - 1; + register char n_at, n_fo, o_ch, o_at, o_fo; + + o_ch = curr->image[y][x]; + o_at = curr->attr[y][x]; + o_fo = curr->font[y][x]; + + n_at = curr->LocalAttr; + n_fo = curr->charsets[curr->LocalCharset]; + + lp_missing = 0; + if (n_ch == o_ch && n_at == o_at && n_fo == o_fo) + { + return; + } + if (n_ch != ' ' || n_at || n_fo) + lp_missing = 1; + if (o_ch != ' ' || o_at || o_fo) + { + if (DC) + PutStr(DC); + else if (CDC) + CPutStr(CDC, 1); + else if (CE) + PutStr(CE); + else + lp_missing = 1; + } +} + +static void FindAKA() +{ + register char *cp, *line, ch; + register struct win *wp = curr; + register int len = strlen(wp->cmd); + int y; + + y = (wp->autoaka > 0 && wp->autoaka <= wp->height) ? wp->autoaka - 1 : wp->y; + cols = wp->width; + try_line: + cp = line = wp->image[y]; + if (wp->autoaka > 0 && (ch = *wp->cmd) != '\0') + { + for (;;) + { + if ((cp = index(cp, ch)) != NULL + && !strncmp(cp, wp->cmd, len)) + break; + if (!cp || ++cp - line >= cols - len) + { + if (++y == wp->autoaka && y < rows) + goto try_line; + return; + } + } + cp += len; + } + for (len = cols - (cp - line); len && *cp == ' '; len--, cp++) + ; + if (len) + { + if (wp->autoaka > 0 && (*cp == '!' || *cp == '%' || *cp == '^')) + wp->autoaka = -1; + else + wp->autoaka = 0; + line = wp->cmd + wp->akapos; + while (len && *cp != ' ') + { + if ((*line++ = *cp++) == '/') + line = wp->cmd + wp->akapos; + len--; + } + *line = '\0'; + } + else + wp->autoaka = 0; +} + + +/* We dont use HS status line with Input. + * If we would use it, then we should check e_tgetflag("es") if + * we are allowed to use esc sequences there. + * For now, we hope that Goto(,,STATLINE,0) brings us in the bottom + * line. jw. + */ + +static char inpbuf[101]; +static int inplen; +static int inpmaxlen; +static char *inpstring; +static int inpstringlen; +static void (*inpfinfunc)(); + +void +Input(istr, len, finfunc) +char *istr; +int len; +void (*finfunc)(); +{ + int maxlen; + + inpstring = istr; + inpstringlen = strlen(istr); + if (len > 100) + len = 100; + maxlen = screenwidth - inpstringlen; + if (!LP && STATLINE == screenbot) + maxlen--; + if (len > maxlen) + len = maxlen; + if (len < 2) + { + Msg(0, "Width too small"); + return; + } + inpmaxlen = len; + inpfinfunc = finfunc; + InitOverlayPage(process_inp_input, inpRedisplayLine, (int (*)())0, 1); + inplen = 0; + GotoPos(0, STATLINE); + if (CE) + PutStr(CE); + else + { + DisplayLine(curr->image[screeny], curr->attr[screeny], + curr->font[screeny], + blank, null, null, screeny, 0, cols - 1); + } + inpRedisplayLine(STATLINE, 0, inpstringlen - 1, 0); + GotoPos(inpstringlen, STATLINE); +} + +static void +process_inp_input(ppbuf, plen) +char **ppbuf; +int *plen; +{ + int len, x; + char *pbuf; + char ch; + + if (ppbuf == 0) + { + AbortInp(); + return; + } + x = inpstringlen+inplen; + len = *plen; + pbuf = *ppbuf; + while (len) + { + ch = *pbuf++; + len--; + if (ch >= ' ' && ch <= '~' && inplen < inpmaxlen) + { + inpbuf[inplen++] = ch; + GotoPos(x, STATLINE); + SaveSetAttr(A_SO, ASCII); + PUTCHAR(ch); + x++; + } + else if ((ch == '\b' || ch == 0177) && inplen > 0) + { + inplen--; + x--; + GotoPos(x, STATLINE); + SaveSetAttr(0, ASCII); + PUTCHAR(' '); + GotoPos(x, STATLINE); + } + else if (ch == '\004' || ch == '\003' || ch == '\000' || ch == '\n' || ch == '\r') + { + if (ch != '\n' && ch != '\r') + inplen = 0; + inpbuf[inplen] = 0; + AbortInp(); /* redisplays... */ + (*inpfinfunc)(inpbuf, inplen); + break; + } + } + *ppbuf = pbuf; + *plen = len; +} + +static void +AbortInp() +{ + in_ovl = 0; /* So we can use RefreshLine() */ + RefreshLine(STATLINE, 0, screenwidth-1); + ExitOverlayPage(); +} + +static void +inpRedisplayLine(y, xs, xe, isblank) +int y, xs, xe, isblank; +{ + int q, r, s, l, v; + + if (y != STATLINE) + return; + inpbuf[inplen] = 0; + GotoPos(xs,y); + q = xs; + v = xe - xs + 1; + s = 0; + r = inpstringlen; + if (v > 0 && q < r) + { + SaveSetAttr(A_SO, ASCII); + l = v; + if (l > r-q) + l = r-q; + printf("%-*.*s", l, l, inpstring + q - s); + q += l; + v -= l; + } + s = r; + r += inplen; + if (v > 0 && q < r) + { + SaveSetAttr(A_SO, ASCII); + l = v; + if (l > r-q) + l = r-q; + printf("%-*.*s", l, l, inpbuf + q - s); + q += l; + v -= l; + } + s = r; + r = screenwidth; + if (!isblank && v > 0 && q < r) + { + SaveSetAttr(0, ASCII); + l = v; + if (l > r-q) + l = r-q; + printf("%-*.*s", l, l, ""); + q += l; + } + SetLastPos(q, y); +} + +static void +AKAfin(buf, len) +char *buf; +int len; +{ + if (len) + { + strcpy(curr->cmd + curr->akapos, buf); + } +} + +void +InputAKA() +{ + void Input(), AKAfin(); + + Input("Set window's a.k.a. to: ", 20, AKAfin); +} + +static void +Colonfin(buf, len) +char *buf; +int len; +{ + if (len) + RcLine(buf); +} + +void +InputColon() +{ + void Input(), Colonfin(); + + Input(":", 100, Colonfin); +} + +void +MakeBlankLine(p, n) +register char *p; +register int n; +{ + while (n--) + *p++ = ' '; +} + +void +MakeStatus(msg) +char *msg; +{ + register char *s, *t; + register int max, ti; + + SetCurr(fore); + display = 1; + if (!(max = HS)) + { + max = !LP ? cols - 1 : cols; + } + if (status) + { + if (!BellDisplayed) + { + ti = time((time_t *) 0) - TimeDisplayed; + if (ti < MsgMinWait) + sleep(MsgMinWait - ti); + } + RemoveStatus(); + } + for (s = t = msg; *s && t - msg < max; ++s) + if (*s == BELL) + PutStr(BL); + else if (*s >= ' ' && *s <= '~') + *t++ = *s; + *t = '\0'; + if (t > msg) + { + strncpy(LastMsg, msg, maxwidth); + status = 1; + status_lastx = screenx; + status_lasty = screeny; + StatLen = t - msg; + if (!HS) + { + GotoPos(0, STATLINE); + SaveSetAttr(A_SO, ASCII); + InsertMode(0); + printf("%s", msg); + screenx = -1; + } + else + { + debug("HS:"); + SaveSetAttr(0, ASCII); + InsertMode(0); + CPutStr(TS, 0); + printf("%s", msg); + PutStr(FS); + } + (void) fflush(stdout); + (void) time(&TimeDisplayed); + } +} + +void +RemoveStatus() +{ + if (!status) + return; + status = 0; + BellDisplayed = 0; + SetCurr(fore); + display = 1; + if (!HS) + { + GotoPos(0, STATLINE); + if (in_ovl) + (*ovl_RedisplayLine)(STATLINE, 0, StatLen - 1, 0); + else + RedisplayLine(null, null, null, STATLINE, 0, StatLen - 1); + GotoPos(status_lastx, status_lasty); + } + else + { + SaveSetAttr(0, ASCII); + PutStr(DS); + } +} + +void +ClearDisplay() +{ + PutStr(CL); + screeny = screenx = 0; + fflush(stdout); +} + +static void SetCurr(wp) +struct win *wp; +{ + curr = wp; + cols = curr->width; + rows = curr->height; + display = curr->active; +} + +void +InitOverlayPage(pro, red, rewrite, blockfore) +void (*pro)(); +void (*red)(); +int (*rewrite)(); +int blockfore; +{ + RemoveStatus(); + SetOvlCurr(); + ChangeScrollRegion(0, screenheight - 1); + SetFlow(1); + ovl_process = pro; + ovl_RedisplayLine = red; + ovl_Rewrite = rewrite; + ovl_blockfore = blockfore; + curr->active = 0; + in_ovl = 1; +} + +void +ExitOverlayPage() +{ + ChangeScrollRegion(curr->top, curr->bot); + GotoPos(curr->x, curr->y); + RestoreAttr(); + SetFlow(curr->flow & FLOW_NOW); + curr->active = 1; + in_ovl = 0; +} + +void +SetOvlCurr() +{ + SetCurr(fore); + SaveSetAttr(0, ASCII); + InsertMode(0); + display = 1; +} + +void +SetLastPos(x,y) +int x,y; +{ + screenx = x; + screeny = y; +} + +void +WSresize(width, height) +int width, height; +{ + debug2("(display=%d:WSresize says:'%s'\n", display, tgoto(WS, width, height)); + PutStr(tgoto(WS, width, height)); +} + +void +ChangeScrollRegion(top, bot) +int top, bot; +{ + if (display == 0) + return; + if (CS == 0) + { + screentop = 0; + screenbot = screenheight - 1; + return; + } + if (top == screentop && bot == screenbot) + return; + debug2("ChangeScrollRegion: (%d - %d)\n", top, bot); + PutStr(tgoto(CS, bot, top)); + screentop = top; + screenbot = bot; + screeny = screenx = -1; /* Just in case... */ +} + + +void AddLineToHist(wp, pi, pa, pf) +struct win *wp; +char **pi, **pa, **pf; +{ + register char *q; + + if (wp->histheight == 0) + return; + q = *pi; *pi = wp->ihist[wp->histidx]; wp->ihist[wp->histidx] = q; + q = *pa; *pa = wp->ahist[wp->histidx]; wp->ahist[wp->histidx] = q; + q = *pf; *pf = wp->fhist[wp->histidx]; wp->fhist[wp->histidx] = q; + if (++wp->histidx >= wp->histheight) + wp->histidx = 0; +} + + +/* + * + * Termcap routines that use our extra_incap + * + */ + +/* findcap: + * cap = capability we are looking for + * tepp = pointer to bufferpointer + * n = size of buffer (0 = infinity) + */ + +char * +findcap(cap, tepp, n) +char *cap; +char **tepp; +int n; +{ + char *tep; + char c, *p, *cp; + int mode; /* mode: 0=LIT 1=^ 2=\x 3,4,5=\nnn */ + int num = 0, capl; + + if (!extra_incap) + return (0); + tep = *tepp; + capl = strlen(cap); + cp = 0; + mode = 0; + for (p = extra_incap; *p; ) + { + if (strncmp(p, cap, capl) == 0) + { + p+=capl; + c = *p; + if (c && c != ':' && c != '@') + p++; + if (c == 0 || c == '@' || c == '=' || c == ':' || c == '#') + cp = tep; + } + while (c = *p) + { + p++; + if (mode == 0) + { + if (c == ':') + break; + if (c == '^') + mode = 1; + if (c == '\\') + mode = 2; + } + else if (mode == 1) + { + c = c & 0x1f; + mode = 0; + } + else if (mode == 2) + { + switch(c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + mode = 3; + num = 0; + break; + case 'E': + c = 27; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + } + if (mode == 2) + mode = 0; + } + if (mode > 2) + { + num = num * 8 + (c - '0'); + if (mode++ == 5 || (*p < '0' || *p > '9')) + { + c = num; + mode = 0; + } + } + if (mode) + continue; + + if (cp && n != 1) + { + *cp++ = c; + n--; + } + } + if (cp) + { + *cp++ = 0; + *tepp = cp; + debug2("'%s' found in extra_incap -> %s\n", cap, tep); + return(tep); + } + } + return(0); +} + +static char * +e_tgetstr(cap, tepp) +char *cap; +char **tepp; +{ + char *tep; + if (tep = findcap(cap, tepp, 0)) + return((*tep == '@') ? 0 : tep); + return (tgetstr(cap, tepp)); +} + +static int +e_tgetflag(cap) +char *cap; +{ + char buf[2], *bufp; + char *tep; + bufp = buf; + if (tep = findcap(cap, &bufp, 2)) + return((*tep == '@') ? 0 : 1); + return (tgetflag(cap)); +} + +static int +e_tgetnum(cap) +char *cap; +{ + char buf[20], *bufp; + char *tep, c; + int res, base = 10; + + bufp = buf; + if (tep = findcap(cap, &bufp, 20)) + { + c = *tep; + if (c == '@') + return(-1); + if (c == '0') + base = 8; + res = 0; + while ((c = *tep++) >= '0' && c <= '9') + res = res * base + (c - '0'); + return(res); + } + return (tgetnum(cap)); +} diff --git a/usr/othersrc/public/screen-3.2/screen3.2/ansi.h b/usr/othersrc/public/screen-3.2/screen3.2/ansi.h new file mode 100644 index 0000000000..4309275432 --- /dev/null +++ b/usr/othersrc/public/screen-3.2/screen3.2/ansi.h @@ -0,0 +1,81 @@ +/* Copyright (c) 1991 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * 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; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, write to the + * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Noteworthy contributors to screen's design and implementation: + * Wayne Davison (davison@borland.com) + * Patrick Wolfe (pat@kai.com, kailand!pat) + * Bart Schaefer (schaefer@cse.ogi.edu) + * Nathan Glasser (nathan@brokaw.lcs.mit.edu) + * Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu) + * Howard Chu (hyc@hanauma.jpl.nasa.gov) + * Tim MacKenzie (tym@dibbler.cs.monash.edu.au) + * Markku Jarvinen (mta@{cc,cs,ee}.tut.fi) + * Marc Boucher (marc@CAM.ORG) + * + **************************************************************** + * $Id: ansi.h,v 1.2 92/02/03 02:27:39 jnweiger Exp $ FAU + */ + +#define NATTR 6 + +#define ATTR_DI 0 /* Dim mode */ +#define ATTR_US 1 /* Underscore mode */ +#define ATTR_BD 2 /* Bold mode */ +#define ATTR_RV 3 /* Reverse mode */ +#define ATTR_SO 4 /* Standout mode */ +#define ATTR_BL 5 /* Blinking */ + +#define A_DI (1< +#ifndef sgi +# include +#endif /* sgi */ +#include +#include + +#ifdef BSDI +# include +#endif /* BSDI */ + +#include "config.h" +#include "screen.h" +#include "extern.h" + +#ifdef _SEQUENT_ +# define UTHOST /* _SEQUENT_ has got ut_find_host() */ +#endif + +#ifndef GETUTENT +# ifdef GETTTYENT +# include +# else +struct ttyent +{ + char *ty_name; +}; +static char *tt, *ttnext; +static char ttys[] = "/etc/ttys"; +# endif +#endif + +#ifdef LOADAV +# ifndef NeXT +# include + +static char KmemName[] = "/dev/kmem"; +# if defined(_SEQUENT_) || defined(MIPS) || defined(SVR4) || defined(ISC) || defined (sgi) +static char UnixName[] = "/unix"; +# else +# ifdef sequent +static char UnixName[] = "/dynix"; +# else +# ifdef hpux +static char UnixName[] = "/hp-ux"; +# else +# ifdef xelos +static char UnixName[] = "/xelos"; +# else +static char UnixName[] = "/vmunix"; +# endif /* xelos */ +# endif /* hpux */ +# endif /* sequent */ +# endif /* _SEQUENT_ ... */ + +# ifdef alliant +static char AvenrunSym[] = "_Loadavg"; +# else +# if defined(hpux) || defined(_SEQUENT_) || defined(SVR4) || defined(ISC) || defined(sgi) +static char AvenrunSym[] = "avenrun"; +# else +static char AvenrunSym[] = "_avenrun"; +# endif +# endif /* alliant */ +static struct nlist nl[2]; +int avenrun; +static kmemf; +# ifdef LOADAV_3LONGS +long loadav[3]; +# else +# ifdef LOADAV_4LONGS +long loadav[4]; +# else +double loadav[3]; +# endif +# endif +# else /* NeXT */ +# include +kern_return_t error; +host_t host; +unsigned int info_count; +struct processor_set_basic_info info; +processor_set_t default_set; +float loadav; +int avenrun; +# endif /* NeXT */ +#endif /* LOADAV */ + +#if defined(UTMPOK) && defined(GETUTENT) && !defined(SVR4) +# if defined(hpux) /* cruel hpux release 8.0 */ +# define pututline _pututline +# endif /* hpux */ +extern struct utmp *getutline(), *pututline(); +# if defined(_SEQUENT_) +extern struct utmp *ut_add_user(), *ut_delete_user(); +extern char *ut_find_host(); +# endif +#endif +#ifdef NETHACK +extern nethackflag; +#endif +int hardcopy_append = 0; +int all_norefresh = 0; + +extern char *RcFileName, *home, *extra_incap, *extra_outcap; +extern char *BellString, *ActivityString, *ShellProg, *ShellArgs[]; +extern char *BufferFile, *PowDetachString, *VisualBellString; +extern int VBellWait, MsgWait, MsgMinWait; +extern struct key ktab[]; +extern char Esc, MetaEsc; +extern char *shellaka, SockPath[], *SockNamePtr, *LoginName; +extern int loginflag, allflag, TtyMode, auto_detach; +extern int iflag, rflag, dflag; +extern int default_flow, wrap; +extern HS, termcapHS, use_hardstatus, visual_bell, default_monitor; +extern int default_histheight; +extern int default_startup; +extern int slowpaste; +extern DeadlyMsg, HasWindow; +extern ForeNum, screenwidth, screenheight; +extern char display_tty[]; +extern struct win *fore; +extern char screenterm[]; +extern int join_with_cr; +extern struct mode OldMode, NewMode; +extern int HasWindow; +extern char mark_key_tab[]; +extern int real_uid, eff_uid; +extern int real_gid, eff_gid; + +#ifdef PASSWORD +int CheckPassword; +char Password[20]; +#endif + +#ifdef COPY_PASTE +extern char *copybuffer; +extern copylen; +#endif + +static char *CatExtra __P((char *, char *)); +static char **SaveArgs __P((int, char **)); +static int Parse __P((char *, char *[])); +static char *ParseChar __P((char *, char *)); +static void ParseNum __P((int, char *[], int*)); +static void ParseOnOff __P((int, char *[], int*)); +static void ParseSaveStr __P((int, char *[], char **, char *)); +static int IsNum __P((char *, int)); +static int IsNumColon __P((char *, int, char *, int)); +static slot_t TtyNameSlot __P((char *)); + +#if !defined(GETTTYENT) && !defined(GETUTENT) +static void setttyent __P((void)); +static struct ttyent *getttyent __P((void)); +#endif + +/* + * XXX: system + */ +extern time_t time __P((time_t *)); +#if !defined(BSDI) && !defined(SVR4) +extern char *getpass __P((char *)); +#endif /* !BSDI && !SVR4 */ +#if defined(LOADAV) && !defined(NeXT) && !defined(NLIST_DECLARED) +extern int nlist __P((char *, struct nlist *)); +#endif + +char *KeyNames[] = +{ + "screen", + "select0", "select1", "select2", "select3", "select4", + "select5", "select6", "select7", "select8", "select9", + "aka", "clear", "colon", "copy", "detach", "flow", + "hardcopy", "help", "histnext", "history", "info", "kill", "lastmsg", + "license", + "lockscreen", "log", "login", "monitor", "next", "other", "paste", + "pow_detach", "prev", "quit", "readbuf", "redisplay", "removebuf", + "reset", "set", "shell", "suspend", "termcap", "time", "vbell", + "version", "width", "windows", "wrap", "writebuf", "xoff", "xon", + 0, +}; + + +/* Must be in alpha order !!! */ + +char *RCNames[] = +{ + "activity", "all", "autodetach", "bell", "bind", "bufferfile", "chdir", + "crlf", "echo", "escape", "flow", "hardcopy_append", "hardstatus", "login", + "markkeys", "mode", "monitor", "msgminwait", "msgwait", "nethack", "password", + "pow_detach_msg", "redraw", "refresh", "screen", "scrollback", "shell", + "shellaka", "sleep", "slowpaste", "startup_message", "term", "termcap", + "terminfo", "vbell", "vbell_msg", "vbellwait", "visualbell", + "visualbell_msg", "wrap", +}; + +enum RCcases +{ + RC_ACTIVITY, + RC_ALL, + RC_AUTODETACH, + RC_BELL, + RC_BIND, + RC_BUFFERFILE, + RC_CHDIR, + RC_CRLF, + RC_ECHO, + RC_ESCAPE, + RC_FLOW, + RC_HARDCOPY_APP, + RC_HARDSTATUS, + RC_LOGIN, + RC_MARKKEYS, + RC_MODE, + RC_MONITOR, + RC_MSGMINWAIT, + RC_MSGWAIT, + RC_NETHACK, + RC_PASSWORD, + RC_POW_DETACH_MSG, + RC_REDRAW, + RC_REFRESH, + RC_SCREEN, + RC_SCROLLBACK, + RC_SHELL, + RC_SHELLAKA, + RC_SLEEP, + RC_SLOWPASTE, + RC_STARTUP_MESSAGE, + RC_TERM, + RC_TERMCAP, + RC_TERMINFO, + RC_VBELL, + RC_VBELL_MSG, + RC_VBELLWAIT, + RC_VISUALBELL, + RC_VISUALBELL_MSG, + RC_WRAP, + RC_RCEND +}; + +#ifdef UTMPOK +static utmp, utmpf; +static char UtmpName[] = UTMPFILE; +# ifdef MIPS + static utmpfappend; +# endif +#endif + +static FILE *fp = NULL; +static char *rc_name; + +char *SaveStr(str) +register char *str; +{ + register char *cp; + + if ((cp = malloc(strlen(str) + 1)) == NULL) + Msg_nomem; + else + strcpy(cp, str); + return cp; +} + +static char *CatExtra(str1, str2) +register char *str1, *str2; +{ + register char *cp; + register int len1, len2, add_colon; + + len1 = strlen(str1); + if (len1 == 0) + return(str2); + add_colon = (str1[len1 - 1] != ':'); + if (str2) + { + len2 = strlen(str2); + if ((cp = realloc(str2, (unsigned) len1 + len2 + add_colon + 1)) == NULL) + Msg_nomem; + bcopy(cp, cp + len1 + add_colon, len2 + 1); + } + else + { + if (len1 == 0) + return 0; + if ((cp = malloc((unsigned) len1 + add_colon + 1)) == NULL) + Msg_nomem; + cp[len1 + add_colon] = '\0'; + } + bcopy(str1, cp, len1); + if (add_colon) + cp[len1] = ':'; + + return cp; +} + +static char *findrcfile(rcfile) +char *rcfile; +{ + static char buf[256]; + char *rc, *p; + + if (rcfile) + { + rc = SaveStr(rcfile); + debug1("findrcfile: you specified '%s'\n", rcfile); + } + else + { + debug("findrcfile: you specified nothing...\n"); + if ((p = getenv("ISCREENRC")) != NULL && *p != '\0') + { + debug1(" ... but $ISCREENRC has: '%s'\n", p); + rc = SaveStr(p); + } + else if ((p = getenv("SCREENRC")) != NULL && *p != '\0') + { + debug1(" ... but $SCREENRC has: '%s'\n", p); + rc = SaveStr(p); + } + else + { + debug(" ...nothing in $SCREENRC, defaulting $HOME/.screenrc\n"); + if (strlen(home) > 244) + Msg(0, "Rc: home too large"); + sprintf(buf, "%s/.iscreenrc", home); + if (access(buf, R_OK)) + sprintf(buf, "%s/.screenrc", home); + rc = SaveStr(buf); + } + } + return rc; +} + +/* + * this will be called twice: + * 1) rcfilename = "/etc/screenrc" + * 2) rcfilename = RcFileName + */ +void +StartRc(rcfilename) +char *rcfilename; +{ + register int argc, len; + register char *p, *cp; + char buf[256]; + char *args[MAXARGS], *t; + + rc_name = findrcfile(rcfilename); + + if ((fp = secfopen(rc_name, "r")) == NULL) + { + if (RcFileName && strcmp(RcFileName, rc_name) == 0) + { + /* + * User explicitly gave us that name, + * this is the only case, where we get angry, if we can't read + * the file. + */ + debug3("StartRc: '%s','%s', '%s'\n", RcFileName, rc_name, rcfilename); + Msg(0, "Unable to open \"%s\".", rc_name); + /* NOTREACHED */ + } + debug1("StartRc: '%s' no good. ignored\n", rc_name); + Free(rc_name); + rc_name = ""; + return; + } + if ((t = getenv("TERM")) == NULL) + Msg(0, "No TERM in environment."); + debug1("startrc got termcp:%s\n", t); + while (fgets(buf, sizeof buf, fp) != NULL) + { + if ((p = rindex(buf, '\n')) != NULL) + *p = '\0'; + if ((argc = Parse(buf, args)) == 0) + continue; + if (strcmp(args[0], "echo") == 0) + { + if (argc < 2 || (argc == 3 && strcmp(args[1], "-n")) || argc > 3) + { + DeadlyMsg = 0; + Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name); + } + else + { + printf((argc == 3) ? "%s" : "%s\r\n", args[argc - 1]); + } + } + else if (strcmp(args[0], "sleep") == 0) + { + if (argc != 2) + { + DeadlyMsg = 0; + Msg(0, "%s: sleep: one numeric argument expected.", rc_name); + } + else + sleep(atoi(args[1])); + } +#ifdef TERMINFO + else if (strcmp(args[0], "terminfo") == 0) +#else + else if (strcmp(args[0], "termcap") == 0) +#endif + { + if (argc < 3 || argc > 4) + Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]); + for (p = args[1]; p && *p; p = cp) + { + if ((cp = index(p, '|')) != 0) + *cp++ = '\0'; + len = strlen(p); + if (p[len - 1] == '*') + { + if (!(len - 1) || !strncmp(p, t, len - 1)) + break; + } + else if (!strcmp(p, t)) + break; + } + if (!(p && *p)) + continue; + extra_incap = CatExtra(args[2], extra_incap); + if (argc == 4) + extra_outcap = CatExtra(args[3], extra_outcap); + } + } + fclose(fp); + Free(rc_name); + rc_name = ""; +} + +static char * +ParseChar(p, cp) +char *p, *cp; +{ + if (*p == '^') + { + if (*++p == '?') + *cp = '\177'; + else if (*p >= '@') + *cp = Ctrl(*p); + else + return 0; + ++p; + } + else if (*p == '\\' && *++p <= '7' && *p >= '0') + { + *cp = 0; + do + *cp = *cp * 8 + *p - '0'; + while (*++p <= '7' && *p >= '0'); + } + else + *cp = *p++; + return p; +} + +/* + * CompileKeys must be called before Markroutine is first used. + * to initialise the keys with defaults, call CompileKeys(NULL, mark_key_tab); + * + * s is an ascii string in a termcap-like syntax. It looks like + * "j=u:k=d:l=r:h=l: =.:" and so on... + * this example rebinds the cursormovement to the keys u (up), d (down), + * l (left), r (right). placing a mark will now be done with ".". + */ +int CompileKeys(s, array) +char *s, *array; +{ + int i; + unsigned char key, value; + + if (!s || !*s) + { + for (i = 0; i < 256; i++) + array[i] = i; + return 0; + } + while (*s) + { + s = ParseChar(s, (char *) &key); + if (*s != '=') + return -1; + do + { + s = ParseChar(++s, (char *) &value); + array[value] = key; + } + while (*s == '='); + if (!*s) + break; + if (*s++ != ':') + return -1; + } + return 0; +} + +static char **SaveArgs(argc, argv) +register int argc; +register char **argv; +{ + register char **ap, **pp; + + if ((pp = ap = (char **) malloc((unsigned) (argc + 1) * sizeof(char **))) == 0) + Msg_nomem; +#ifdef notdef + debug("saveargs:\n"); +#endif + while (argc--) + { + debug1(" '%s'", *argv); + *pp++ = SaveStr(*argv++); + } + debug("\n"); + *pp = 0; + return ap; +} + +void +FinishRc(rcfilename) +char *rcfilename; +{ + /* in FinishRc screen is not yet open, thus Msg() is deadly here. + */ + char buf[256]; + + rc_name = findrcfile(rcfilename); + + if ((fp = secfopen(rc_name, "r")) == NULL) + { + if (RcFileName && strcmp(RcFileName, rc_name) == 0) + { + /* + * User explicitly gave us that name, + * this is the only case, where we get angry, if we can't read + * the file. + */ + debug3("FinishRc:'%s','%s','%s'\n", RcFileName, rc_name, rcfilename); + Msg(0, "Unable to open \"%s\".", rc_name); + /* NOTREACHED */ + } + debug1("FinishRc: '%s' no good. ignored\n", rc_name); + Free(rc_name); + rc_name = ""; + return; + } + + debug("finishrc is going...\n"); + while (fgets(buf, sizeof buf, fp) != NULL) + { + RcLine(buf); + } + (void) fclose(fp); + Free(rc_name); + rc_name = ""; +} + +/* + * this is a KEY_SET pressed + */ +void +DoSet(argv) +char **argv; +{ + char *p; + static char buf[256]; + + p = buf; + debug("DoSet\n"); + if (!argv || !*argv || !**argv) + { + debug("empty DoSet\n"); + sprintf(buf, "set "); + RcLine(buf); + return; + } + sprintf(p, "set"); p+=3; + while(*argv && (strlen(buf) + strlen(*argv) < 255)) + { + sprintf(p, " %s", *argv++); + p += strlen(p); + } + RcLine(buf); +} + +/* + * "$HOST blafoo" -> "localhost blafoo" + * "${HOST}blafoo" -> "localhostblafoo" + * "\$HOST blafoo" -> "$HOST blafoo" + * "\\$HOST blafoo" -> "\localhost blafoo" + * "'$HOST ${HOST}'" -> "'$HOST ${HOST}'" + * "'\$HOST'" -> "'\$HOST'" + * "\'$HOST' $HOST" -> "'localhost' $HOST" + */ +static char *expand_env_vars(ss) +char *ss; +{ + static char ebuf[2048]; + register int esize = 2047, quofl = 0; + register char *e = ebuf; + register char *s = ss; + register char *v; + + while (*s && *s != '\n' && esize > 0) + { + if (*s == '\'') + quofl ^= 1; + if (*s == '$' && !quofl) + { + char *p, c; + + p = ++s; + if (*s == '{') + { + p = ++s; + while (*p != '}') + if (*p++ == '\0') + return ss; + } + else + { + while (*p != ' ' && *p != '\0' && *p != '\n') + p++; + } + c = *p; + debug1("exp: c='%c'\n", c); + *p = '\0'; + if (v = getenv(s)) + { + debug2("exp: $'%s'='%s'\n", s, v); + while (*v && esize-- > 0) + *e++ = *v++; + } + else + debug1("exp: '%s' not env\n", s); + if ((*p = c) == '}') + p++; + s = p; + } + else + { + if (s[0] == '\\' && !quofl) + if (s[1] == '$' || (s[1] == '\\' && s[2] == '$') || + s[1] == '\'' || (s[1] == '\\' && s[2] == '\'')) + s++; + *e++ = *s++; + esize--; + } + } + if (esize <= 0) + Msg(0, "expand_env_vars: buffer overflow\n"); + *e = '\0'; + return ebuf; +} + +void +RcLine(ubuf) +char *ubuf; +{ + char *args[MAXARGS]; + register char *buf, *p, **pp, **ap; + register int argc, setflag; + int q, qq; + char key; + int low, high, mid, x; + + buf = expand_env_vars(ubuf); + + ap = args; + + if ((p = rindex(buf, '\n')) != NULL) + *p = '\0'; + if (strncmp("set ", buf, 4) == 0) + { + buf += 4; + setflag = 1; + debug1("RcLine: '%s' is a set command\n", buf); + } + else if (strncmp("se ", buf, 3) == 0) + { + buf += 3; + setflag = 1; + debug1("RcLine: '%s' is a se command\n", buf); + } + else + { + setflag = 0; + debug1("RcLine: '%s'\n", buf); + } + if ((argc = Parse(buf, ap)) == 0) + { + if (setflag) + { + DeadlyMsg = 0; + Msg(0, "%s: set what?\n", rc_name); + } + return; + } + + low = 0; + high = (int)RC_RCEND - 1; + while (low <= high) + { + mid = (low + high) / 2; + x = strcmp(ap[0], RCNames[mid]); + if (x < 0) + high = mid - 1; + else if (x > 0) + low = mid + 1; + else + break; + } + if (low > high) + mid = (int)RC_RCEND; + switch ((enum RCcases) mid) + { + case RC_ESCAPE: + if (argc != 2 || !ParseEscape(ap[1])) + { + DeadlyMsg = 0; + Msg(0, "%s: two characters required after escape.", rc_name); + return; + } + if (Esc != MetaEsc) + ktab[Esc].type = KEY_OTHER; + else + ktab[Esc].type = KEY_IGNORE; + return; + case RC_CHDIR: + if (setflag) + break; + p = argc < 2 ? home : ap[1]; + if (chdir(p) == -1) + { + DeadlyMsg = 0; + Msg(errno, "%s", p); + } + return; + case RC_SHELL: + ParseSaveStr(argc, ap, &ShellProg, "shell"); + ShellArgs[0] = ShellProg; + return; + case RC_SHELLAKA: + ParseSaveStr(argc, ap, &shellaka, "shellaka"); + return; + case RC_SCREEN: + if (setflag) + break; + DoScreen(rc_name, ap + 1); + return; + case RC_SLEEP: + case RC_TERMCAP: + case RC_TERMINFO: + return; /* Already handled */ + case RC_TERM: + { + char *tmp = NULL; + + ParseSaveStr(argc, ap, &tmp, "term"); + if (!tmp) + return; + if (strlen(tmp) >= 20) + { + DeadlyMsg = 0; + Msg(0,"%s: term: argument too long ( < 20)", rc_name); + Free(tmp); + return; + } + strcpy(screenterm, args[1]); + Free(tmp); + debug1("screenterm set to %s\n", screenterm); + MakeTermcap(0); + return; + } + case RC_ECHO: + if (HasWindow && *rc_name == '\0') + { + /* + * user typed ^A:echo... well, echo isn't FinishRc's job, + * but as he wanted to test us, we show good will + */ + DeadlyMsg = 0; + if (argc == 2 || (argc == 3 && !strcmp(ap[1], "-n"))) + Msg(0, "%s", ap[argc - 1]); + else + Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name); + } + return; + case RC_BELL: + ParseSaveStr(argc, ap, &BellString, "bell"); + return; + case RC_BUFFERFILE: + ParseSaveStr(argc, ap, &BufferFile, "bufferfile"); + return; + case RC_ACTIVITY: + ParseSaveStr(argc, ap, &ActivityString, "activity"); + return; + case RC_POW_DETACH_MSG: + ParseSaveStr(argc, ap, &PowDetachString, "pow_detach"); + return; + case RC_LOGIN: +#ifdef UTMPOK + q = loginflag; + ParseOnOff(argc, ap, &loginflag); + if (fore && setflag) + { + SlotToggle(loginflag?(1):(-1)); + loginflag = q; + } +#endif + return; + case RC_FLOW: + if (argc == 3 && ap[2][0] == 'i') + { + iflag = 1; + argc--; + } + if (argc == 2 && ap[1][0] == 'a') + default_flow = FLOW_AUTOFLAG; + else + ParseOnOff(argc, ap, &default_flow); + return; + case RC_WRAP: + ParseOnOff(argc, ap, &wrap); + return; + case RC_HARDSTATUS: + ParseOnOff(argc, ap, &use_hardstatus); + if (use_hardstatus) + HS = termcapHS; + else + HS = 0; + return; + case RC_MONITOR: + { + int f; + + ParseOnOff(argc, ap, &f); + if (fore && setflag) + fore->monitor = (f == 0) ? MON_OFF : MON_ON; + else + default_monitor = (f == 0) ? MON_OFF : MON_ON; + } + return; + case RC_REDRAW: + case RC_REFRESH: + { + int r; + + ParseOnOff(argc, ap, &r); + if (fore && setflag) + fore->norefresh = (r) ? 0 : 1; + else + { + all_norefresh = (r) ? 0 : 1; + if (all_norefresh) + Msg(0, "No refresh on window change!\n"); + else + Msg(0, "Window specific refresh\n"); + } + } + return; + case RC_VBELL: + case RC_VISUALBELL: + ParseOnOff(argc, ap, &visual_bell); + return; + case RC_VBELLWAIT: + ParseNum(argc, ap, &VBellWait); + if (fore && rc_name[0] == '\0') + Msg(0, "vbellwait set to %d seconds", VBellWait); + return; + case RC_MSGWAIT: + ParseNum(argc, ap, &MsgWait); + if (fore && rc_name[0] == '\0') + Msg(0, "msgwait set to %d seconds", MsgWait); + return; + case RC_MSGMINWAIT: + ParseNum(argc, ap, &MsgMinWait); + if (fore && rc_name[0] == '\0') + Msg(0, "msgminwait set to %d seconds", MsgMinWait); + return; + case RC_SCROLLBACK: + if (fore && setflag) + { + int i; + + ParseNum(argc, ap, &i); + ChangeScrollback(fore, i, fore->width); + if (fore && rc_name[0] == '\0') + Msg(0, "scrollback set to %d", fore->histheight); + } + else + ParseNum(argc, ap, &default_histheight); + return; + case RC_SLOWPASTE: + ParseNum(argc, ap, &slowpaste); + if (fore && rc_name[0] == '\0') + Msg(0, "slowpaste set to %d milliseconds", slowpaste); + return; + case RC_MARKKEYS: + { + char *tmp = NULL; + + ParseSaveStr(argc, ap, &tmp, "markkeys"); + if (CompileKeys(ap[1], mark_key_tab)) + { + DeadlyMsg = 0; + Msg(0, "%s: markkeys: syntax error.", rc_name); + Free(tmp); + return; + } + debug1("markkeys %s\n", ap[1]); + Free(tmp); + return; + } +#ifdef NETHACK + case RC_NETHACK: + ParseOnOff(argc, ap, &nethackflag); + return; +#endif + case RC_HARDCOPY_APP: + ParseOnOff(argc, ap, &hardcopy_append); + return; + case RC_VBELL_MSG: + case RC_VISUALBELL_MSG: + ParseSaveStr(argc, ap, &VisualBellString, "vbell_msg"); + debug1(" new vbellstr '%s'\n", VisualBellString); + return; + case RC_MODE: + if (argc != 2) + { + DeadlyMsg = 0; + Msg(0, "%s: mode: one argument required.", rc_name); + return; + } + if (!IsNum(ap[1], 7)) + { + DeadlyMsg = 0; + Msg(0, "%s: mode: octal number expected.", rc_name); + return; + } + (void) sscanf(ap[1], "%o", &TtyMode); + return; + case RC_CRLF: + ParseOnOff(argc, ap, &join_with_cr); + return; + case RC_AUTODETACH: + ParseOnOff(argc, ap, &auto_detach); + return; + case RC_STARTUP_MESSAGE: + ParseOnOff(argc, ap, &default_startup); + return; +#ifdef PASSWORD + case RC_PASSWORD: + CheckPassword = 1; + if (argc >= 2) + { + strncpy(Password, ap[1], sizeof Password); + if (!strcmp(Password, "none")) + CheckPassword = 0; + } + else + { + char *mstr = 0; + int msleep = 0, st; + char salt[2]; + +#ifdef POSIX + if (HasWindow) + { + Msg(0, "Cannot ask for password on POSIX systems"); + return; + } +#endif + /* there is a clear screen sequence in the buffer. */ + fflush(stdout); + if (HasWindow) + { + ClearDisplay(); + SetTTY(0, &OldMode); + } + strncpy(Password, getpass("New screen password:"), + sizeof(Password)); + if (strcmp(Password, getpass("Retype new password:"))) + { +#ifdef NETHACK + if (nethackflag) + mstr = "[ Passwords don't match - your armor crumbles away ]"; + else +#endif + mstr = "[ Passwords don't match - checking turned off ]"; + msleep = 1; + CheckPassword = 0; + } + if (Password[0] == '\0') + { + CheckPassword = 0; + mstr = "[ No password - no secure ]"; + msleep = 1; + } + for (st=0; st<2; st++) + salt[st] = 'A' + (int)((time(0) >> 6*st) % 26); + strncpy(Password, crypt(Password, salt), sizeof(Password)); + if (CheckPassword) + { +#ifdef COPY_PASTE + if (copybuffer) + + Free(copybuffer); + copylen = strlen(Password); + if ((copybuffer = (char *) malloc(copylen+1)) == NULL) + { + Msg_nomem; + return; + } + strcpy(copybuffer, Password); + mstr = "[ Password moved into copybuffer ]"; + msleep = 1; +#else /* COPY_PASTE */ + mstr = "[ Crypted password is \"%s\" ]"; + msleep = 5; +#endif /* COPY_PASTE */ + } + if (HasWindow) + { + SetTTY(0, &NewMode); + Activate(0); /* Redraw */ + if (mstr) + { + Msg(0, mstr, Password); + } + } + else + { + if (mstr) + { + printf(mstr, Password); + putchar('\n'); + sleep(msleep); + } + ClearDisplay(); + } + } + debug1("finishrc: our password is: --%s%-- \n", Password); + return; +#endif /* PASSWORD */ + case RC_ALL: + if (!setflag || !HasWindow || *rc_name) + break; + display_help(); + return; + case RC_BIND: + if (setflag) + break; + p = ap[1]; + if (argc < 2 || *p == '\0') + { + DeadlyMsg = 0; + Msg(0, "%s: key expected after bind.", rc_name); + return; + } + if ((p = ParseChar(p, &key)) == NULL || *p) + { + DeadlyMsg = 0; + Msg(0, "%s: bind: character, ^x, or (octal) \\032 expected.", + rc_name); + return; + } + if (ktab[key].type != KEY_IGNORE) + { + ktab[key].type = KEY_IGNORE; + if ((pp = ktab[key].args) != NULL) + { + for (; *pp; pp++) + Free(*pp); + Free(ktab[key].args); + } + } + if (argc > 2) + { + for (pp = KeyNames; *pp; ++pp) + if (strcmp(ap[2], *pp) == 0) + break; + if (*pp) + { + ktab[key].type = (enum keytype) (pp - KeyNames + 1); + if (argc > 3) + { + ktab[key].args = SaveArgs(argc - 3, ap + 3); + } + else + ktab[key].args = NULL; + } + else + { + ktab[key].type = KEY_CREATE; + ktab[key].args = SaveArgs(argc - 2, ap + 2); + } + } + return; + case RC_RCEND: + default: + { + char ibuf[3]; + /* + * now we are user-friendly: + * if anyone typed a key name like "help" or "next" ... + * we did not match anything above. so look in the KeyNames table. + */ + debug1("--ap[0] %s\n", ap[0]); + for (pp = KeyNames; *pp; ++pp) + if (strcmp(ap[0], *pp) == 0) + break; + if (*pp == 0) + break; + + ibuf[0] = Esc; + ibuf[1] = pp - KeyNames +1; + debug1("RcLine: it was a keyname: '%s'\n", *pp); + q = 2; qq = 0; + if (HasWindow) + ProcessInput(ibuf, &q, (char *)0, &qq, 0); + else + { + DeadlyMsg = 0; + Msg(0, "%s: Key '%s' has no effect while no window open...\n", + rc_name, ap[0]); + } + } + return; + } + DeadlyMsg = 0; + Msg(0, "%s: unknown %skeyword \"%s\"", rc_name, + setflag?"'set' ":"", ap[0]); +} + +static int +Parse(buf, args) +char *buf, **args; +{ + register char *p = buf, **ap = args; + register int delim, argc; + + argc = 0; + for (;;) + { + while (*p && (*p == ' ' || *p == '\t')) + ++p; + if (*p == '\0' || *p == '#') + { + *p = '\0'; + return argc; + } + if (argc > MAXARGS - 1) + Msg(0, "%s: too many tokens.", rc_name); + delim = 0; + if (*p == '"' || *p == '\'') + delim = *p++; + argc++; + *ap = p; + *++ap = 0; + while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t'))) + ++p; + if (*p == '\0') + { + if (delim) + { + DeadlyMsg = 0; + Msg(0, "%s: Missing quote.", rc_name); + return 0; + } + return argc; + } + *p++ = '\0'; + } +} + +int +ParseEscape(p) +char *p; +{ + if ((p = ParseChar(p, &Esc)) == NULL || + (p = ParseChar(p, &MetaEsc)) == NULL || *p) + return 0; + return 1; +} + +static void +ParseNum(argc, ap, var) +int argc; +char *ap[]; +int *var; +{ + int i; + char *p; + + if (argc == 2 && ap[1][0] != '\0') + { + i = 0; + p = ap[1]; + while (*p) + { + if (*p >= '0' && *p <= '9') + i = 10 * i + (*p - '0'); + else + { + DeadlyMsg = 0; + Msg(0, "%s: %s: invalid argument. Give numeric argument", + rc_name, ap[0]); + return; + } + p++; + } + } + else + { + DeadlyMsg = 0; + Msg(0, "%s: %s: invalid argument. Give one argument", + rc_name, ap[0]); + return; + } + debug1("ParseNum got %d\n", i); + *var = i; +} + +static void +ParseSaveStr(argc, ap, var, title) +int argc; +char *ap[]; +char **var; +char *title; +{ + if (argc != 2) + { + DeadlyMsg = 0; + Msg(0, "%s: %s: one argument required.", rc_name, title); + return; + } + if (*var) + Free(*var); + *var = SaveStr(ap[1]); + return; +} + +static void +ParseOnOff(argc, ap, var) +int argc; +char *ap[]; +int *var; +{ + register int num = -1; + + if (argc == 2 && ap[1][0] == 'o') + { + if (ap[1][1] == 'f') + num = 0; + else if (ap[1][1] == 'n') + num = 1; + } + if (num < 0) + { + DeadlyMsg = 0; + Msg(0, "%s: %s: invalid argument. Give 'on' or 'off'", rc_name, ap[0]); + return; + } + *var = num; +} + + +static int IsNum(s, base) +register char *s; +register int base; +{ + for (base += '0'; *s; ++s) + if (*s < '0' || *s > base) + return 0; + return 1; +} + +static int IsNumColon(s, base, p, psize) +int base, psize; +char *s, *p; +{ + char *q; + if ((q = rindex(s, ':')) != NULL) + { + strncpy(p, q + 1, psize - 1); + p[psize - 1] = '\0'; + *q = '\0'; + } + else + *p = '\0'; + return IsNum(s, base); +} + +void +SlotToggle(how) +int how; +/* + * how = 0 real toggle mode + * how > 0 do try to set a utmp slot. + * how < 0 try to withdraw a utmp slot + * + * slot = -1 window not logged in. + * slot = 0 window not logged in, but should be logged in. + * (unable to write utmp, or detached). + */ +{ + debug1("SlotToggle %d\n", how); + if (how == 0) + how = (fore->slot == (slot_t) -1)?(1):(-1); + /* + * slot 0 or active -> we try to log out. + * slot -1 -> we try to log in. + */ +#ifdef UTMPOK + if (how > 0) + { + debug(" try to log in\n"); + if ((fore->slot == (slot_t) -1) || (fore->slot == (slot_t) 0)) + { +#ifdef USRLIMIT + if (CountUsers() >= USRLIMIT) + Msg(0, "User limit reached."); + else +#endif + { + if (SetUtmp(fore, ForeNum) == 0) + Msg(0, "This window is now logged in."); + else + Msg(0, "This window should now be logged in."); + } + } + else + Msg(0, "This window is already logged in."); + } + else if (how < 0) + { + debug(" try to log out\n"); + if (fore->slot == (slot_t) -1) + Msg(0, "This window is already logged out\n"); + else if (fore->slot == (slot_t) 0) + { + debug("What a relief! In fact, it was not logged in\n"); + Msg(0, "This window is not logged in."); + fore->slot = (slot_t) -1; + } + else + { + RemoveUtmp(fore); + if (fore->slot != (slot_t) -1) + Msg(0, "What? Cannot remove Utmp slot?"); + else + Msg(0, "This window is no longer logged in."); + } + } +#else /* !UTMPOK */ + Msg(0, "Unable to modify %s.\n", UTMPFILE); +#endif +} + +void +DoScreen(fn, av) +char *fn, **av; +{ + register int flowflag, num, lflag = loginflag, aflag = 0; + register char *aka = NULL; + register int histheight = default_histheight; + char buf[20]; + char termbuf[25]; + char *termp; + char *args[2]; + + flowflag = default_flow; + termbuf[0] = '\0'; + termp = NULL; + while (av && *av && av[0][0] == '-') + { + switch (av[0][1]) + { + case 'f': + switch (av[0][2]) + { + case 'n': + case '0': + flowflag = FLOW_NOW * 0; + break; + case 'y': + case '1': + case '\0': + flowflag = FLOW_NOW * 1; + break; + case 'a': + flowflag = FLOW_AUTOFLAG; + break; + default: + break; + } + break; + case 'k': + case 't': + if (av[0][2]) + aka = &av[0][2]; + else if (*++av) + aka = *av; + else + --av; + break; + case 'T': + if (av[0][2]) + termp = &av[0][2]; + else if (*++av) + termp = *av; + else + --av; + break; + case 'h': + if (av[0][2]) + histheight = atoi(av[0] + 2); + else if (*++av) + histheight = atoi(*av); + else + --av; + break; + case 'l': + switch (av[0][2]) + { + case 'n': + case '0': + lflag = 0; + break; + case 'y': + case '1': + case '\0': + lflag = 1; + break; + default: + break; + } + break; + case 'a': + aflag = 1; + break; + default: + Msg(0, "%s: screen: invalid option -%c.", fn, av[0][1]); + break; + } + ++av; + } + num = 0; + if (av && *av && IsNumColon(*av, 10, buf, sizeof(buf))) + { + if (*buf != '\0') + aka = buf; + num = atoi(*av); + if (num < 0 || num > MAXWIN - 1) + { + Msg(0, "%s: illegal screen number %d.", fn, num); + num = 0; + } + ++av; + } + if (!av || !*av) + { + av = args; + av[0] = ShellProg; + av[1] = NULL; + if (!aka) + aka = shellaka; + } + MakeWindow(aka, av, aflag, flowflag, num, (char *) 0, lflag, histheight, termp); +} + +void +WriteFile(dump) +int dump; +{ + /* dump==0: create .termcap, + * dump==1: hardcopy, + * #ifdef COPY_PASTE + * dump==2: BUFFERFILE + * #endif COPY_PASTE + */ + register int i, j, k; + register char *p; + register FILE *f; + char fn[1024]; + char *mode = "w"; + + switch (dump) + { + case DUMP_TERMCAP: + i = SockNamePtr - SockPath; + strncpy(fn, SockPath, i); + strcpy(fn + i, ".termcap"); + break; + case DUMP_HARDCOPY: + sprintf(fn, "hardcopy.%d", ForeNum); + if (hardcopy_append && !access(fn, W_OK)) + mode = "a"; + break; + case DUMP_EXCHANGE: + sprintf(fn, "%s", BufferFile); + umask(0); + break; + } + + debug2("WriteFile(%d) %s\n", dump, fn); + if (UserContext() > 0) + { + debug("Writefile: usercontext\n"); + if ((f = fopen(fn, mode)) == NULL) + { + debug2("WriteFile: fopen(%s,\"%s\") failed\n", fn, mode); + UserReturn(0); + } + else + { + switch (dump) + { + case DUMP_HARDCOPY: + if (*mode == 'a') + { + putc('>', f); + for (j = screenwidth - 2; j > 0; j--) + putc('=', f); + fputs("<\n", f); + } + for (i = 0; i < screenheight; ++i) + { + p = fore->image[i]; + for (k = screenwidth - 1; k >= 0 && p[k] == ' '; --k) + ; + for (j = 0; j <= k; ++j) + putc(p[j], f); + putc('\n', f); + } + break; + case DUMP_TERMCAP: + if ((p = index(MakeTermcap(fore->aflag), '=')) != NULL) + { + fputs(++p, f); + putc('\n', f); + } + break; +#ifdef COPY_PASTE + case DUMP_EXCHANGE: + p = copybuffer; + for (i = 0; i < copylen; i++) + putc(*p++, f); + break; +#endif + } + (void) fclose(f); + UserReturn(1); + } + } + if (UserStatus() <= 0) + Msg(0, "Cannot open \"%s\"", fn); + else + { + switch (dump) + { + case DUMP_TERMCAP: + Msg(0, "Termcap entry written to \"%s\".", fn); + break; + case DUMP_HARDCOPY: + Msg(0, "Screen image %s to \"%s\".", + (*mode == 'a') ? "appended" : "written", fn); + break; +#ifdef COPY_PASTE + case DUMP_EXCHANGE: + Msg(0, "Copybuffer written to \"%s\".", fn); +#endif + } + } +} + +#ifdef COPY_PASTE + +void +ReadFile() +{ + int i, l, size; + char fn[1024], c; + struct stat stb; + + sprintf(fn, "%s", BufferFile); + debug1("ReadFile(%s)\n", fn); + if ((i = secopen(fn, O_RDONLY, 0)) < 0) + { + Msg(errno, "no %s -- no slurp", fn); + return; + } + if (fstat(i, &stb)) + { + Msg(errno, "no good %s -- no slurp", fn); + close(i); + return; + } + size = stb.st_size; + if (copybuffer) + Free(copybuffer); + copylen = 0; + if ((copybuffer = malloc(size)) == NULL) + { + close(i); + Msg_nomem; + return; + } + errno = 0; + if ((l = read(i, copybuffer, size)) != size) + { + copylen = (l > 0) ? l : 0; +#ifdef NETHACK + if (nethackflag) + Msg(errno, "You choke on your food: %d bytes", copylen); + else +#endif + Msg(errno, "Got only %d bytes from %s", copylen, fn); + close(i); + return; + } + copylen = l; + if (read(i, &c, 1) > 0) + Msg(0, "Slurped only %d characters into buffer - try again", copylen); + else + Msg(0, "Slurped %d characters into buffer", copylen); + close(i); + return; +} + +void +KillBuffers() +{ + char fn[1024]; + sprintf(fn, "%s", BufferFile); + errno = 0; + if (access(fn, W_OK) == -1) + { + Msg(errno, "%s not removed", fn); + return; + } + else + { + unlink(fn); + Msg(errno, "%s removed", fn); + } +} +#endif /* COPY_PASTE */ + +#ifdef USRLIMIT +CountUsers() +{ +#ifdef GETUTENT + struct utmp *ut, *getutent(); +#else + struct utmp utmpbuf; +#endif + int UserCount; + + debug1("CountUsers() - utmp=%d\n",utmp); + if (!utmp) + return(0); + UserCount = 0; +#ifdef GETUTENT + setutent(); + while (ut = getutent()) + if (ut->ut_type == USER_PROCESS) + UserCount++; +#else + (void) lseek(utmpf, (off_t) 0, 0); + while (read(utmpf, &utmpbuf, sizeof(struct utmp)) > 0) + { + if (utmpbuf.ut_name[0] != '\0') + UserCount++; + } +#endif + return(UserCount); +} +#endif + +#ifdef UTMPOK + +static slot_t loginslot; +static struct utmp utmp_logintty; +#ifdef _SEQUENT_ +static char loginhost[100+1]; +#endif + +void +InitUtmp() +{ + debug("InitUtmp testing...\n"); + if ((utmpf = open(UtmpName, O_RDWR)) == -1) + { + if (errno != EACCES) + Msg(errno, UtmpName); + debug("InitUtmp failed.\n"); + utmp = 0; + return; + } +#ifdef GETUTENT + close(utmpf); + utmpf= -1; +#endif +#ifdef MIPS + if ((utmpfappend = open(UtmpName, O_APPEND)) == -1) + { + if (errno != EACCES) + Msg(errno, UtmpName); + return; + } +#endif + utmp = 1; +#ifndef apollo + ReInitUtmp(); +#endif +} + +void +ReInitUtmp() +{ +#ifndef apollo + if (!utmp) + { + debug("Reinitutmp: utmp == 0\n"); + return; + } +#endif + debug("(Re)InitUtmp: removing your logintty\n"); + loginslot = TtyNameSlot(display_tty); + if (loginslot!=(slot_t)0 && loginslot!=(slot_t)-1) + { +#ifdef _SEQUENT_ + if (p=ut_find_host(loginslot)) + strncpy(loginhost, p, 100); +#endif + RemoveLoginSlot(loginslot, &utmp_logintty); + } + debug1(" slot %d zapped\n", loginslot); +} + +void +RestoreLoginSlot() +{ + debug("RestoreLoginSlot()\n"); +#ifdef apollo + InitUtmp(); +#endif + if (utmp && loginslot!=(slot_t)0 && loginslot!=(slot_t)-1) + { +#ifdef GETUTENT +# ifdef _SEQUENT_ + int fail; + debug1(" logging you in again (slot %s)\n", loginslot); +/* + * We have problems if we add the console and use ut_add_user() + * because the id will be 'scon' instead of 'co'. So we + * restore it with pututline(). The reason why we don't use + * pututline all the time is that we want to set the host field. + * Unfortunatelly this can only be done with ut_add_user(). + */ + if (*loginhost) + { + fail = (ut_add_user(LoginName, loginslot, utmp_logintty.ut_pid, + *loginhost?loginhost:(char *)0) == 0); + } + else + { + setutent(); + fail = (pututline(&utmp_logintty) == 0); + } + if (fail) +# else /* _SEQUENT_ */ + debug1(" logging you in again (slot %s)\n", loginslot); + setutent(); + if (pututline(&utmp_logintty)==0) +# endif /* _SEQUENT */ +#else /* GETUTENT */ + debug1(" logging you in again (slot %d)\n", loginslot); +# ifdef sequent + /* call sequent undocumented routine to count logins and add utmp entry if possible */ + if (add_utmp(loginslot, &utmp_logintty) == -1) +# else + (void) lseek(utmpf, (off_t) (loginslot * sizeof(struct utmp)), 0); + if (write(utmpf, (char *) &utmp_logintty, sizeof(struct utmp)) + != sizeof(struct utmp)) +# endif /* sequent */ +#endif /* GETUTENT */ + { +#ifdef NETHACK + if (nethackflag) + Msg(errno, "%s is too hard to dig in.", UTMPFILE); + else +#endif + Msg(errno,"Could not write %s.", UTMPFILE); + } + } +#ifdef apollo + close(utmpf); +#endif + loginslot = (slot_t) 0; +} + +void +RemoveLoginSlot(slot, up) +slot_t slot; +struct utmp *up; +{ +#ifdef GETUTENT + struct utmp *uu; +#endif + struct utmp u; +#ifdef apollo + struct utmp *uq; +#endif + +#ifdef GETUTENT + debug2("RemoveLoginSlot(%s, %08x)\n", (slot == (slot_t) 0 || + slot == (slot_t) -1 ) ? "no slot" : slot, up); +#else + debug2("RemoveLoginSlot(%d, %08x)\n", slot, up); +#endif +#ifdef apollo + InitUtmp(); + bzero((char *)up, sizeof(struct utmp)); + uq = (struct utmp *)malloc(sizeof(struct utmp)); + bzero((char *)uq, sizeof(struct utmp)); +#endif /* apollo */ + if (!utmp) + return; + if (slot != (slot_t) 0 && slot != (slot_t) -1) + { + bzero((char *) &u, sizeof u); +#ifdef GETUTENT + setutent(); + strncpy(u.ut_line, slot, sizeof(u.ut_line)); + if ((uu = getutline(&u)) == 0) + { + DeadlyMsg = 0; + Msg(0, "Utmp slot not found -> not removed"); + return; + } + *up= *uu; +# ifdef _SEQUENT_ + if (ut_delete_user(slot, uu->ut_pid, 0, 0) == 0) +# else + uu->ut_type = DEAD_PROCESS; + uu->ut_exit.e_termination = 0; + uu->ut_exit.e_exit= 0; + if (pututline(uu) == 0) +# endif +#else + (void) lseek(utmpf, (off_t) (slot * sizeof u), 0); + if (read(utmpf, (char *) up, sizeof u) != sizeof u) + { + DeadlyMsg = 0; + Msg(errno, "cannot read %s ???", UTMPFILE); + sleep(1); + } + (void) lseek(utmpf, (off_t) (slot * sizeof u), 0); +# ifdef apollo + bcopy((char *)up, (char *)uq, sizeof(struct utmp)); + bzero(uq->ut_name, sizeof(uq->ut_name)); + bzero(uq->ut_host, sizeof(uq->ut_host)); + if (write(utmpf, (char *)uq, sizeof(struct utmp)) != sizeof(struct utmp)) +# else + if (write(utmpf, (char *) &u, sizeof u) != sizeof u) +# endif /* apollo */ +#endif + { +#ifdef NETHACK + if (nethackflag) + { + DeadlyMsg = 0; + Msg(errno, "%s is too hard to dig in.", UTMPFILE); + } + else +#endif + { + DeadlyMsg = 0; + Msg(errno, "Could not write %s.", UTMPFILE); + } + } + } + else + { + debug1("There is no utmp-slot to be removed(%d)\n", slot); + } +#ifdef apollo + close(utmpf); + free(uq); +#endif +} + +char * +stripdev(nam) +char *nam; +{ +#ifdef apollo + char *p; + + if (nam == NULL) + return NULL; + if (p = strstr(nam,"/dev/")) + return p + 5; +#else + if (nam == NULL) + return NULL; + if (strncmp(nam, "/dev/", 5) == 0) + return nam + 5; +#endif + return nam; +} + +static slot_t TtyNameSlot(nam) +char *nam; +{ + char *name; + register slot_t slot; +#ifndef GETUTENT + register struct ttyent *tp; +#endif +#ifdef apollo + struct utmp *up; +#endif + + debug1("TtyNameSlot(%s)\n", nam); +#ifdef apollo + InitUtmp(); +#endif + if (!utmp || nam == NULL) + return (slot_t)0; + name = stripdev(nam); +#ifdef GETUTENT + slot = name; +#else +# ifdef apollo + slot = 0; + up = (struct utmp *)malloc(sizeof(struct utmp)); + while (1) + { + if ((read(utmpf, (char *)up, sizeof(struct utmp)) == + sizeof(struct utmp)) && (strcmp(up->ut_line, name))) + slot++; + else + break; + } + close(utmpf); + free(up); +# else /* !apollo */ + slot = 1; + setttyent(); + while ((tp = getttyent()) != NULL && strcmp(name, tp->ty_name) != 0) + { + debug2("'%s' %d, ", tp->ty_name, slot); + ++slot; + } + debug("\n"); +# ifdef MIPS + if (tp == NULL) + { + slot = CreateUtmp(name); + } +# endif /* MIPS */ +# endif /* apollo */ +#endif /* GETUTENT */ + return slot; +} + +int +SetUtmp(wi, displaynumber) +struct win *wi; +int displaynumber; +{ + register char *p; + register slot_t slot; + char *line; + struct utmp u; +#ifdef UTHOST +# ifdef _SEQUENT_ + char host[100+5]; +# else + char host[sizeof(utmp_logintty.ut_host)+5]; +# endif +#endif + + wi->slot = (slot_t) 0; + if (!utmp) + return -1; + if ((slot = TtyNameSlot(wi->tty)) == (slot_t) NULL) + { + debug1("SetUtmp failed (tty %s).\n",wi->tty); + return -1; + } + debug2("SetUtmp %d will get slot %d...\n", displaynumber, (int)slot); +#ifdef apollo + InitUtmp(); +#endif + +#ifdef UTHOST + host[sizeof(host)-5] = '\0'; +# ifdef _SEQUENT_ + strncpy(host, loginhost, sizeof(host) - 5); +# else + strncpy(host, utmp_logintty.ut_host, sizeof(host) - 5); +# endif + if (loginslot != (slot_t)0 && loginslot != (slot_t)-1 && host[0] != '\0') + { + /* + * we want to set our ut_host field to something like + * ":ttyhf:s.0" or + * "faui45:s.0" or + * "132.199.81.4:s.0" (even this may hurt..), but not + * "faui45.informati"......:s.0 + */ + for (p = host; *p; p++) + { + if ((*p < '0' || *p > '9') && (*p != '.')) + break; + } + if (*p) + { + for (p = host; *p; p++) + { + if (*p == '.') + { + *p = '\0'; + break; + } + } + } + } + else + { + strncpy(host + 1, stripdev(display_tty), sizeof(host) - 6); + host[0] = ':'; + } + debug1("rlogin hostname: '%s'\n", host); + sprintf(host + strlen(host), ":S.%c", '0' + displaynumber); + debug1("rlogin hostname: '%s'\n", host); +#endif /* UTHOST */ + + line = stripdev(wi->tty); + bzero((char *) &u, sizeof u); + +#ifdef GETUTENT +# ifdef _SEQUENT_ + if (ut_add_user(LoginName, slot, wi->wpid, host)==0) +# else + strncpy(u.ut_user, LoginName, sizeof(u.ut_user)); + strncpy(u.ut_id, line + strlen(line) - 2, sizeof(u.ut_id)); + strncpy(u.ut_line, line, sizeof(u.ut_line)); + u.ut_pid = wi->wpid; + u.ut_type = USER_PROCESS; +# ifdef SVR4 + (void) time(&u.ut_tv.tv_sec); + u.ut_tv.tv_usec=0; +# else + (void) time(&u.ut_time); +# endif /* SVR4 */ +# ifdef UTHOST + strncpy(u.ut_host, host, sizeof(u.ut_host)); +# endif /* UTHOST */ + if (pututline(&u) == 0) +# endif /* _SEQUENT_ */ +#else /* GETUTENT */ + strncpy(u.ut_line, line, sizeof(u.ut_line)); + strncpy(u.ut_name, LoginName, sizeof(u.ut_name)); +# ifdef UTHOST + strncpy(u.ut_host, host, sizeof(u.ut_host)); +# endif /* UTHOST */ +# ifdef MIPS + u.ut_type = 7; /* USER_PROCESS */ + strncpy(u.ut_id, line + 3, 4); +# endif /* MIPS */ + (void) time(&u.ut_time); +# ifdef sequent +/* call sequent undocumented routine to count logins and add utmp entry if possible */ + if (add_utmp(slot, &u) == -1) +# else + (void) lseek(utmpf, (off_t) (slot * sizeof u), 0); + if (write(utmpf, (char *) &u, sizeof u) != sizeof u) +# endif /* sequent */ +#endif /* GETUTENT */ + + { +#ifdef NETHACK + if (nethackflag) + Msg(errno, "%s is too hard to dig in.", UTMPFILE); + else +#endif + Msg(errno,"Could not write %s.", UTMPFILE); +#ifdef apollo + close(utmpf); +#endif + return -1; + } + debug("SetUtmp successful\n"); + wi->slot = slot; +#ifdef apollo + close(utmpf); +#endif + return 0; +} + +#ifdef MIPS + +#define GETTTYENT +static int ttyfd = 0; + +static void setttyent() +{ + if (ttyfd) + close(ttyfd); + ttyfd = open(UtmpName, O_RDONLY); +} + +static struct ttyent *getttyent() +{ + static struct utmp u; + static struct ttyent t; + + if (!ttyfd) + return NULL; + + if (read(ttyfd, &u, sizeof u)) + { + t.ty_name = u.ut_line; + return &t; + } + return NULL; +} + +CreateUtmp(name) +char *name; +{ + int slot; + struct utmp u; + + strncpy(u.ut_line, name, 8); + strncpy(u.ut_name, LoginName, 8); + u.ut_type = 7; /* USER_PROCESS */ + strncpy(u.ut_id, name+3, 4); + (void) time(&u.ut_time); + slot = (lseek(utmpfappend, 0, 2) + 1) / sizeof u; + (void) write(utmpfappend, (char *)&u, sizeof u); + close(utmpfappend); + if ((utmpfappend = open(UtmpName, O_APPEND)) == -1) + { + if (errno != EACCES) + Msg(errno, UtmpName); + return; + } + return slot; +} +#endif /* MIPS */ + +/* + * if slot could be removed or was 0, wi->slot = -1; + * else not changed. + */ +int +RemoveUtmp(wi) +struct win *wi; +{ +#ifdef GETUTENT + struct utmp *uu; +#endif +#ifdef apollo + struct utmp *up; +#endif + struct utmp u; + slot_t slot; + + slot = wi->slot; +#ifdef GETUTENT + debug1("RemoveUtmp(%s)\n", (slot == (slot_t) 0) ? + "no slot (0)":((slot == (slot_t) -1) ? "no slot (-1)" : slot)); +#else + debug1("RemoveUtmp(wi.slot: %d)\n", slot); +#endif +#ifdef apollo + InitUtmp(); + up = (struct utmp *)malloc(sizeof(struct utmp)); + bzero((char *)up, sizeof(struct utmp)); +#endif /* apollo */ + if (!utmp) + return -1; + if (slot == (slot_t) 0 || slot == (slot_t) -1) + { + debug1("There is no utmp-slot to be removed(%d)\n", slot); + wi->slot = (slot_t) -1; + return 0; + } + bzero((char *) &u, sizeof u); +#ifdef GETUTENT + setutent(); + strncpy(u.ut_line, slot, sizeof(u.ut_line)); + if ((uu = getutline(&u)) == 0) + { + Msg(0, "Utmp slot not found -> not removed"); + return -1; + } +# ifdef _SEQUENT_ + if (ut_delete_user(slot, uu->ut_pid, 0, 0) == 0) +# else + uu->ut_type = DEAD_PROCESS; + uu->ut_exit.e_termination = 0; + uu->ut_exit.e_exit= 0; + if (pututline(uu) == 0) +# endif +#else /* GETUTENT */ + (void) lseek(utmpf, (off_t) (slot * sizeof u), 0); +# ifdef apollo + if (read(utmpf, (char *) up, sizeof u) != sizeof u) + { + DeadlyMsg = 0; + Msg(errno, "cannot read %s?", UTMPFILE); + sleep(1); + } + (void) lseek(utmpf, (off_t) (slot * sizeof u), 0); + bzero(up->ut_name, sizeof(u.ut_name)); + bzero(up->ut_host, sizeof(u.ut_host)); + if (write(utmpf, (char *)up, sizeof u) != sizeof u) +# else + if (write(utmpf, (char *) &u, sizeof u) != sizeof u) +# endif /* apollo */ +#endif + { +#ifdef NETHACK + if (nethackflag) + Msg(errno, "%s is too hard to dig in.", UTMPFILE); + else +#endif + Msg(errno,"Could not write %s.", UTMPFILE); +#ifdef apollo + close(utmpf); + free(up); +#endif + return -1; + } + debug("RemoveUtmp successfull\n"); + wi->slot = (slot_t) -1; +#ifdef apollo + close(utmpf); + free(up); +#endif + return 0; +} + +#endif /* UTMPOK */ + +#if !defined(GETTTYENT) && !defined(GETUTENT) + +static void setttyent() +{ + struct stat s; + register int f; + register char *p, *ep; + + if (ttnext) + { + ttnext = tt; + return; + } + if ((f = open(ttys, O_RDONLY)) == -1 || fstat(f, &s) == -1) + Msg(errno, ttys); + if ((tt = malloc((unsigned) s.st_size + 1)) == 0) + Msg_nomem; + if (read(f, tt, s.st_size) != s.st_size) + Msg(errno, ttys); + close(f); + for (p = tt, ep = p + s.st_size; p < ep; ++p) + if (*p == '\n') + *p = '\0'; + *p = '\0'; + ttnext = tt; +} + +static struct ttyent *getttyent() +{ + static struct ttyent t; + + if (*ttnext == '\0') + return NULL; + t.ty_name = ttnext + 2; + ttnext += strlen(ttnext) + 1; + return &t; +} + +#endif /* GETTTYENT */ + +#ifdef LOADAV +# ifdef LOADAV_NEXT +void +InitNeXTLoadAvg() +{ + error = processor_set_default(host_self(), &default_set); + if (error != KERN_SUCCESS) + mach_error("Error calling processor_set_default", error); + else + avenrun = 1; +} + +int +GetAvenrun() +{ + info_count = PROCESSOR_SET_BASIC_INFO_COUNT; + error = processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO, &host, + (processor_set_info_t)&info, &info_count); + if (error != KERN_SUCCESS) + { + mach_error("Error calling processor_set_info", error); + return 0; + } + else + { + loadav = (float)info.load_average / LOAD_SCALE; + return 1; + } +} + +# else /* need kmem for load avg */ + +void +InitKmem() +{ + debug("Init Kmem...\n"); +# ifndef apollo + if ((kmemf = open(KmemName, O_RDONLY)) == -1) + return; + debug("Kmem opened\n"); + nl[0].n_name = AvenrunSym; + debug2("Searching in %s for %s\n", UnixName, nl[0].n_name); + nlist(UnixName, nl); + if (/* nl[0].n_type == 0 || */ nl[0].n_value == 0) + { + close(kmemf); + return; + } +# ifdef sgi + nl[0].n_value &= ~(1 << 31); /* clear upper bit */ +# endif /* sgi */ + debug("AvenrunSym found!!\n"); +# endif /* apollo */ + avenrun = 1; +} + +int +GetAvenrun() +{ +# ifdef apollo + int load[3]; + register int i; + + proc1_$get_loadav(load); + for (i = 0; i < 3; i++) + loadav[i] = (double)load[i] / 65536.0; +# else + if (lseek(kmemf, (off_t) nl[0].n_value, 0) == (off_t) - 1) + return 0; + if (read(kmemf, (char *) loadav, sizeof loadav) != sizeof loadav) + return 0; +# endif /* apollo */ + + return 1; +} + +# endif /* !NeXT, need kmem for load avg */ +#endif /* LOADAV */ + +/* + * (Almost) secure open and fopen... mlschroe. + */ + +FILE * +secfopen(name, mode) +char *name; +char *mode; +{ + FILE *fi; +#ifdef NOREUID + int flags, fd; +#endif + + debug2("secfopen(%s, %s)\n", name, mode); + if (eff_uid == real_uid) + return(fopen(name, mode)); +#ifndef NOREUID + setreuid(eff_uid, real_uid); + setregid(eff_gid, real_gid); + fi = fopen(name, mode); + setreuid(real_uid, eff_uid); + setregid(real_gid, eff_gid); +#else + if (mode[0] && mode[1] == '+') + flags = O_RDWR; + else + flags = (mode[0] == 'r') ? O_RDONLY : O_WRONLY; + if (mode[0] == 'w') + flags |= O_CREAT | O_TRUNC; + else if (mode[0] == 'a') + flags |= O_CREAT | O_APPEND; + else if (mode[0] != 'r') + { + errno = EINVAL; + return(0); + } + if ((fd = secopen(name, flags, 0666)) < 0) + return(0); + if ((fi = fdopen(fd, mode)) == 0) + { + close(fd); + return(0); + } +#endif + return(fi); +} + + +int +secopen(name, flags, mode) +char *name; +int flags; +int mode; +{ + int fd; +#ifdef NOREUID + int q; + struct stat stb; +#endif + + debug3("secopen(%s, 0x%x, 0%03o)\n", name, flags, mode); + if (eff_uid == real_uid) + return(open(name, flags, mode)); +#ifndef NOREUID + setreuid(eff_uid, real_uid); + setregid(eff_gid, real_gid); + fd = open(name, flags, mode); + setreuid(real_uid, eff_uid); + setregid(real_gid, eff_gid); +#else + /* Truncation/creation is done in UserContext */ + if ((flags & O_TRUNC) || ((flags & O_CREAT) && access(name, F_OK))) + { + if (UserContext() > 0) + { + if ((fd = open(name, flags, mode)) >= 0) + { + close(fd); + UserReturn(0); + } + if (errno == 0) + errno = EACCES; + UserReturn(errno); + } + if (q = UserStatus()) + { + if (q > 0) + errno = q; + return(-1); + } + } + if (access(name, F_OK)) + return(-1); + if ((fd = open(name, flags & ~(O_TRUNC | O_CREAT), 0)) < 0) + return(-1); + debug("open successful\n"); + if (fstat(fd, &stb)) + { + close(fd); + return(-1); + } + debug("fstat successful\n"); + if (stb.st_uid != real_uid) + { + switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) + { + case O_RDONLY: + q = 0004; + break; + case O_WRONLY: + q = 0002; + break; + default: + q = 0006; + break; + } + if ((stb.st_mode & q) != q) + { + debug("secopen: permission denied\n"); + close(fd); + errno = EACCES; + return(-1); + } + } +#endif + debug1("secopen ok - returning %d\n", fd); + return(fd); +} + +#ifdef BUGGYGETLOGIN +char * +getlogin() +{ + char *tty; +#ifdef utmp +# undef utmp +#endif + struct utmp u; + static char retbuf[sizeof(u.ut_user)+1]; + int fd; + + for (fd = 0; fd <= 2 && (tty = ttyname(fd)) == NULL; fd++) + ; + if ((tty == NULL) || ((fd = open(UTMP_FILE, O_RDONLY)) < 0)) + return NULL; + tty = stripdev(tty); + retbuf[0] = '\0'; + while (read(fd, &u, sizeof(struct utmp)) == sizeof(struct utmp)) + { + if (!strncmp(tty, u.ut_line, sizeof(u.ut_line))) + { + strncpy(retbuf, u.ut_user, sizeof(u.ut_user)); + retbuf[sizeof(u.ut_user)] = '\0'; + if (u.ut_type == USER_PROCESS) + break; + } + } + close(fd); + + return *retbuf ? retbuf : NULL; +} +#endif diff --git a/usr/othersrc/public/screen-3.2/screen3.2/patchlevel.h b/usr/othersrc/public/screen-3.2/screen3.2/patchlevel.h new file mode 100644 index 0000000000..96fd1d1e14 --- /dev/null +++ b/usr/othersrc/public/screen-3.2/screen3.2/patchlevel.h @@ -0,0 +1,75 @@ +/* Copyright (c) 1991 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * 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; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, write to the + * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Noteworthy contributors to screen's design and implementation: + * Wayne Davison (davison@borland.com) + * Patrick Wolfe (pat@kai.com, kailand!pat) + * Bart Schaefer (schaefer@cse.ogi.edu) + * Nathan Glasser (nathan@brokaw.lcs.mit.edu) + * Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu) + * Howard Chu (hyc@hanauma.jpl.nasa.gov) + * Tim MacKenzie (tym@dibbler.cs.monash.edu.au) + * Markku Jarvinen (mta@{cc,cs,ee}.tut.fi) + * Marc Boucher (marc@CAM.ORG) + * + **************************************************************** + */ + +/**************************************************************** + * $Header: /src.stand/pub/utilities/screen/3.2/RCS/patchlevel.h,v 1.2 92/02/03 02:27:59 jnweiger Exp $ + * + * patchlevel.h: Our life story. + * 8.7.91 -- 3.00.01 -wipe and a 'setenv TERM dumb' bugfix. + * 17.7.91 -- 3.00.02 another patchlevel by Wayne Davison + * 31.7.91 -- 3.00.03 E0, S0, C0 for flexible semi-graphics, nonblocking + * window title input and 'C-a :' command input. + * 10.8.91 -- 3.00.04 scrollback, markkeys and some bugfixes. + * 13.8.91 -- 3.00.05 mark routine improved, ansi prototypes added. + * 20.8.91 -- 3.00.06 screen -h, faster GotoPos in overlay, termcap %. + * instead of %c + * 28.8.91 -- 3.00.07 environment variable support. security. terminfo. + * pyramid and ultrix support. + * 07.9.91 -- 3.00.99 secopen(), MIPS support, SVR4 support. + * 09.9.91 -- 3.01.00 backspace bug fixed. + * 03.10.91 -- 3.01.01 ansi.c: null-ptr fixed, CLS now saves to scrollback. + * Using setresuid on hpux. Memory leak fixed. + * Better GotoPos(). Support for IC. Another resize bug. + * Detach() w/o fore crashed. -T and -A(dapt) option. + * GNU copyleft. + * 19.12.91 -- 3.01.02 flow now really automatic (autoflow killed). + * 7 bit restriction removed from WriteString(). + * 09.01.92 -- 3.01.03 flow reattach bug fixed. VDISCARD bug fixed. + * 13.01.92 -- 3.01.04 new flow concept: ^Af toggles now three states + * 21.01.92 -- 3.01.05 '^A:screen 11' bug fixed. aflag in DoScreen(). + * Some code cleanup. attach_tty and display_tty[] + * added. + * 26.01.92 -- 3.01.06 apollo support, "hardcopy_append on", "bufferfile", + * SECURITY PROBLEM cleared.. + * 28.01.92 -- 3.01.07 screen after su allowed. Pid became part of + * SockName. sysvish 14 character restriction considered. + * 31.01.92 -- 3.02.00 Ultrix port, Irix 3.3 SGI port, shadow pw support, + * data loss on stdin overflow fixed. "refresh off". + */ + +#define ORIGIN "FAU" +#define REV 3 +#define VERS 2 +#define PATCHLEVEL 0 +#define DATE "01/31/92" +#define STATE "" diff --git a/usr/othersrc/public/screen-3.2/screen3.2/putenv.c b/usr/othersrc/public/screen-3.2/screen3.2/putenv.c new file mode 100644 index 0000000000..d150225895 --- /dev/null +++ b/usr/othersrc/public/screen-3.2/screen3.2/putenv.c @@ -0,0 +1,202 @@ +/* Copyright (c) 1991 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * 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; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, write to the + * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Noteworthy contributors to screen's design and implementation: + * Wayne Davison (davison@borland.com) + * Patrick Wolfe (pat@kai.com, kailand!pat) + * Bart Schaefer (schaefer@cse.ogi.edu) + * Nathan Glasser (nathan@brokaw.lcs.mit.edu) + * Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu) + * Howard Chu (hyc@hanauma.jpl.nasa.gov) + * Tim MacKenzie (tym@dibbler.cs.monash.edu.au) + * Markku Jarvinen (mta@{cc,cs,ee}.tut.fi) + * Marc Boucher (marc@CAM.ORG) + * + **************************************************************** + */ + +/* + * putenv -- put value into environment + * + * Usage: i = putenv (string) + * int i; + * char *string; + * + * where string is of the form =. + * If "value" is 0, then "name" will be deleted from the environment. + * Putenv returns 0 normally, -1 on error (not enough core for malloc). + * + * Putenv may need to add a new name into the environment, or to + * associate a value longer than the current value with a particular + * name. So, to make life simpler, putenv() copies your entire + * environment into the heap (i.e. malloc()) from the stack + * (i.e. where it resides when your process is initiated) the first + * time you call it. + * + * HISTORY + * 3-Sep-91 Michael Schroeder (mlschroe). Modified to behave as + * as putenv. + * 16-Aug-91 Tim MacKenzie (tym) at Monash University. Modified for + * use in screen (iScreen) (ignores final int parameter) + * 14-Oct-85 Michael Mauldin (mlm) at Carnegie-Mellon University + * Ripped out of CMU lib for Rob-O-Matic portability + * 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University + * Created for VAX. Too bad Bell Labs didn't provide this. It's + * unfortunate that you have to copy the whole environment onto the + * heap, but the bookkeeping-and-not-so-much-copying approach turns + * out to be much hairier. So, I decided to do the simple thing, + * copying the entire environment onto the heap the first time you + * call putenv(), then doing realloc() uniformly later on. + */ +#include "config.h" + +#if defined(NEEDSETENV) + +#define EXTRASIZE 5 /* increment to add to env. size */ + +char *index(), *malloc(), *realloc(); +int strlen(); + +static int envsize = -1; /* current size of environment */ +extern char **environ; /* the global which is your env. */ + +static int findenv(); /* look for a name in the env. */ +static int newenv(); /* copy env. from stack to heap */ +static int moreenv(); /* incr. size of env. */ + +int unsetenv(name) +char *name; +{ + register int i; + + i = findenv(name); + if (i<0) + return; /* Already here */ + + free(environ[i]); + if (envsize > 0) + envsize--; + for (; environ[i]; i++) + environ[i] = environ[i+1]; +} + +int putenv(string) +char *string; +{ + register int i, j; + register char *p; + + if (envsize < 0) + { /* first time putenv called */ + if (newenv() < 0) /* copy env. to heap */ + return (-1); + } + + i = findenv(string); /* look for name in environment */ + + if (i < 0) + { /* name must be added */ + for (i = 0; environ[i]; i++); + if (i >= (envsize - 1)) + { /* need new slot */ + if (moreenv() < 0) + return (-1); + } + p = malloc(strlen(string) + 1); + if (p == 0) /* not enough core */ + return (-1); + environ[i + 1] = 0; /* new end of env. */ + } + else + { /* name already in env. */ + p = realloc(environ[i], strlen(string) + 1); + if (p == 0) + return (-1); + } + sprintf(p, "%s", string); /* copy into env. */ + environ[i] = p; + + return (0); +} + +static int findenv(name) +char *name; +{ + register char *namechar, *envchar; + register int i, found; + + found = 0; + for (i = 0; environ[i] && !found; i++) + { + envchar = environ[i]; + namechar = name; + while (*namechar && *namechar != '=' && (*namechar == *envchar)) + { + namechar++; + envchar++; + } + found = ((*namechar == '\0' || *namechar == '=') && *envchar == '='); + } + return (found ? i - 1 : -1); +} + +static int newenv() +{ + register char **env, *elem; + register int i, esize; + + for (i = 0; environ[i]; i++) + ; + esize = i + EXTRASIZE + 1; + env = (char **)malloc(esize * sizeof (elem)); + if (env == 0) + return (-1); + + for (i = 0; environ[i]; i++) + { + elem = malloc(strlen(environ[i]) + 1); + if (elem == 0) + return (-1); + env[i] = elem; + strcpy(elem, environ[i]); + } + + env[i] = 0; + environ = env; + envsize = esize; + return (0); +} + +static int moreenv() +{ + register int esize; + register char **env; + + esize = envsize + EXTRASIZE; + env = (char **)realloc(environ, esize * sizeof (*env)); + if (env == 0) + return (-1); + environ = env; + envsize = esize; + return (0); +} + +#endif /* NEEDSETENV */ + + diff --git a/usr/othersrc/public/screen-3.2/screen3.2/screen.1 b/usr/othersrc/public/screen-3.2/screen3.2/screen.1 new file mode 100644 index 0000000000..41c5bf7487 --- /dev/null +++ b/usr/othersrc/public/screen-3.2/screen3.2/screen.1 @@ -0,0 +1,1764 @@ +.if n .ds Q \&" +.if n .ds U \&" +.if t .ds Q `` +.if t .ds U '' +.TH SCREEN 1 "31 January 1992" +.UC 4 +.SH NAME +screen \- screen manager with VT100/ANSI terminal emulation +.SH SYNOPSIS +.B screen +[ +.B -\fIoptions\fP +] [ +.B \fIcmd\fP +[ +.B \fIargs\fP +] ] +.br +.B screen \-r +[ +.BR \fI\fP[\fIpid.\fP]\fItty\fP[\fI.host\fP] +] +.ta .5i 1.8i +.SH DESCRIPTION +.I Screen +is a full-screen window manager that +multiplexes a physical terminal between several processes (typically +interactive shells). +Each virtual terminal provides the functions +of the DEC VT100 terminal and, in addition, several control functions +from the ANSI X3.64 (ISO 6429) and ISO 2022 standards +(e.\|g. insert/delete line and support for multiple character sets). +There is a scrollback history buffer for each virtual terminal and a +copy-and-paste mechanism that allows moving text regions between +windows. +.PP +The name of this program was changed to +.I iScreen +to be able to distinguish it from Oliver Laumann's original +.I screen +program. Despite that fact, this manual will continue to use the name +.IR screen . +This is, because often the binary is installed as 'screen'. +.PP +When +.I screen +is called, it creates a single window with a shell in it (or the specified +command) and then gets out of your way so that you can use the program as you +normally would. +Then, at any time, you can create new (full-screen) windows with other programs +in them (including more shells), kill existing windows, view a list of the +current windows, turn output logging on and off, copy-and-paste text between +windows, view the scrollback history, switch between windows +in whatever manner you wish, etc. +When a program terminates, +.I screen +kills the window that contained it. +If this window was in the foreground, the display switches to the previous +window; if none are left, +.I screen +exits. +.PP +Everything you type is sent to the program running in the current window. +The only exception to this is the one keystroke that is used to initiate +a command to the window manager. +By default, each command begins with a control-a (abbreviated C-a from +now on), and is followed by one other keystroke. +The command character and all the key bindings can be fully customized +to be anything you like, though they are always two characters in length. +.PP +The standard way to create a new window is to type \*QC-a c\*U. +This creates a new window running a shell and switches to that +window immediately, regardless of the state of the process running +in the current window. +Similarly, you can create a new window with a custom command in it by +first binding the command to a keystroke (in your .screenrc file) and +then using it just like the \*QC-a c\*U command. +In addition, new windows can be created by running a command like: +.IP +screen emacs prog.c +.PP +from a shell prompt within a previously created window. +This will not run another copy of +.IR screen , +but will instead supply the +command name and its arguments to the window manager who will use it to +create the new window. +The above example would start the emacs editor (editing prog.c) and switch +to its window. +.PP +If \*Q/etc/utmp\*U is writable by +.IR screen , +an appropriate record will be written to this file for each window, and +removed when the window is terminated. +This is useful for working with \*Qtalk\*U, \*Qscript\*U, \*Qshutdown\*U, +\*Qrsend\*U, \*Qsccs\*U and other similar programs that use the utmp +file to determine who you are. As long as +.I screen +is active on your terminal, +the terminals own record is removed from the utmp file. See also \*QC-a L\*U. +.SH GETTING STARTED +Before you begin to use +.I screen +you'll need to make sure you have correctly selected your terminal type, +just as you would for any other termcap/terminfo program. +(You can do this by using +.IR tset +or +.IR stty , +for example.) +.PP +If you're impatient and want to get started without doing a lot more reading, +you should remember this one command: \*QC-a ?\*U. +Typing these two characters will display a list of the available +.I screen +commands and their bindings. Each keystroke is discussed in +the section \*QCOMMAND KEYS\*U. The manual section \*QCUSTOMIZATION\*U +deals with the contents of your .screenrc. +.PP +If possible, choose a version of your terminal's termcap that has automatic +margins turned \fIoff\fP. +This will ensure an accurate and optimal update of the screen +in all circumstances. +The next best thing is an auto-margin terminal that allows the last position +on the screen to be updated without scrolling the screen (such as a vt100). +This also allows the entire screen to be updated. +Lastly, if all you've got is a \*Qtrue\*U auto-margin terminal +.I screen +will be content to use it, but updating a character put into the last position +on the screen may not be possible until the screen scrolls or the character +is moved into a safe position in some other way. +This delay can be shortened by using a terminal with insert-character +capability. +.PP +If your terminal is of the second type (firm-margined `am'), you will +want to let +.I screen +know about this, since a normal termcap doesn't distinguish this type of +automatic margins from a \*Qtrue\*U `am' terminal. +You do this by specifying the `LP' capability in your termcap +(see the \*Qtermcap\*U .screenrc command), or by using the +.B \-L +command-line option. +.I Screen +needs this information to correctly update the screen. +.PP +If you are using a \*Qtrue\*U auto-margin terminal (no `LP') at low baud +rates, you may want to turn on a more optimal output mode by including the +flag `OP' in your termcap entry, or by specifying the +.B \-O +command-line option. +The trade-off is that +.I screen +will no-longer accurately emulate the vt100's line-end quirks (e.\|g. the +screen will scroll after putting \fIone\fP character in the last screen +position). +.SH "COMMAND KEYS" +As mentioned, each +.I screen +command consists of a +\*QC-a\*U followed by one other character. +For your convenience, all commands that are bound to lower-case letters are +also bound to their control character counterparts (with the exception +of \*QC-a a\*U; see below), thus, \*QC-a c\*U as well as \*QC-a C-c\*U can +be used to create a window. +.PP +The following keystroke commands are available: +.IP "\fBC-a 0\fP - \fBC-a 9\fP (select0 - select9)" +Switch to the window with the number 0 through 9. +When a new window is established, the first available number from the +range 0..9 is assigned to this window. +Thus, the first window can be activated by \*QC-a 0\*U (there can be no more +than 10 windows present at any one time). +.IP "\fBC-a C-a\fP (other)" +Switch to the window displayed previously. +Note that this function defaults to the command character typed twice, +unless overridden; for instance, if you use the option \*Q\fB\-e]x\fP\*U, +this function becomes \*Q]]\*U, not \*Q]C-a\*U. +.IP "\fBC-a a\fP\0\0\0\0\0" +Send the command character (C-a) to the processes in the current window. +.IP "\fBC-a A\fP (aka)" +Allow the user to enter an a.\|k.\|a (also-known-as). for the current window. +.IP "\fBC-a c\fP or \fBC-a C-c\fP (shell)" +Create a new window with a shell and switch to that window. +.IP "\fBC-a C\fP (clear)" +Clear the screen. +.IP "\fBC-a d\fP or \fBC-a C-d\fP (detach)" +Detach +.I screen +(disconnect it from the terminal and put it into the background). +A detached +.I screen +can be resumed by invoking +.I screen +with the +.B \-r +option. (See also section \*QCOMMAND-LINE OPTIONS\*U.) +.IP "\fBC-a D D\fP (pow_detach)" +Power detach. Mainly the same as above, but also sends a HANGUP signal to +the parent process of +.IR screen . +CAUTION: This will result in a logout, when +.I screen +was started from your login shell. +.IP "\fBC-a f\fP or \fBC-a C-f\fP (flow)" +Cycles the current window's flow-control setting from "automatic" to "on" to +"off", see the section titled FLOW-CONTROL for details. +.IP "\fBC-a C-g\fP (vbell)" +Toggles +.I screen's +visual bell mode. If your terminal does not support +a visual bell, a `vbell-message' is displayed in the status line. +.IP "\fBC-a h\fP or \fBC-a C-h\fP (hardcopy)" +Write a hardcopy of the current window to the file \*Qhardcopy.\fIn\fP\*U +in the window's default directory, where \fIn\fP is the number +of the current window. +.IP "\fBC-a H\fP (log)" +Begins/ends logging of the current window to the file \*Qscreenlog.\fIn\fP\*U +in the window's default directory, where \fIn\fP is the number +of the current window. +The session log is appended to the previous contents of the file +if it already exists. +The current contents and the contents of the scrollback history are not +included in the session log. +.IP "\fBC-a i\fP or \fBC-a C-i\fP (info)" +Uses the message line to display some information about the current window: +the cursor position in the form \*Q(column,row)\*U starting with \*Q(1,1)\*U, +the terminal width and height plus the size of the scrollback buffer in lines, +like in \*U(80,24)+50\*U, various flag settings (flow-control, insert mode, +origin +mode, wrap mode, application-keypad mode, output logging, and activity +monitoring (`+' indicates enabled, `-' not)), +the currently active character set (\fIG0\fP, \fIG1\fP, \fIG2\fP, +or \fIG3\fP), and in square brackets the terminal character sets that are +currently designated as \fIG0\fP through \fIG3\fP. +For system information use \*QC-a t\*U. +.IP "\fBC-a k\fP or \fBC-a C-k\fP (kill)" +Kill the current window and switch to the previously displayed window. +Note: +.I Emacs +users should keep this command in mind, when killing a line. +It is recommended not to use \*QC-a\*U as the +.I screen +escape key or that kill is rebound to \*QC-a K\*U. +.IP "\fBC-a l\fP or \fBC-a C-l\fP (redisplay)" +Redisplay the current window. +.IP "\fBC-a L\fP (login)" +Adds or removes the entry in /etc/utmp file for the current window. +This controls if the window is `logged in'. See also above. +Additionally to that toggle, it is convenient having a `log in' and a `log out' +key. E.\|g. `bind I set login on' and `bind O set login off' will map these +keys to be C-a I and C-a O. +.IP "\fBC-a m\fP or \fBC-a C-m\fP (lastmsg)" +Repeat the last message displayed in the message line. +Useful if you're typing when a message appears, because (unless your terminal +has a status line) the message goes away when you press a key. +.IP "\fBC-a M\fP (monitor)" +Toggles monitoring of the current window. +When monitoring is turned on and the affected window is switched into the +background, you will receive the activity notification message in the +status line at the first sign of output and the window will also be marked +with an `@' in the window-status display. +Monitoring is initially off for all windows. +.IP "\fBC-a space\fP or \fBC-a n\fP or \fBC-a C-n\fP (next)" +Switch to the next window. +This function can be used repeatedly to cycle through the list of windows. +(Some terminals require you to release the control key before pressing space.) +.IP "\fBC-a p\fP or \fBC-a C-p\fP (prev)" +Switch to the previous window (the opposite of \fBC-a n\fP). +.IP "\fBC-a q\fP or \fBC-a C-q\fP (xon)" +Send a control-q to the program in the current window. +.IP "\fBC-a r\fP or \fBC-a C-r\fP (wrap)" +Toggle the current window's line-wrap setting (turn the current window's +automatic margins on and off). +.IP "\fBC-a s\fP or \fBC-a C-s\fP (xoff)" +Send a control-s to the program in the current window. +.IP "\fBC-a t\fP or \fBC-a C-t\fP (time)" +Uses the message line to display the time of day, the host name, and the load +averages over 1, 5, and 15 minutes (if this is available on your system). +For window specific information use \*QC-a i\*U. +.IP "\fBC-a v\fP or \fBC-a C-v\fP (version)" +Display the version and compilation date. +.IP "\fBC-a w\fP or \fBC-a C-w\fP (windows)" +Uses the message line to display a list of all the windows. +Each window is listed by number with the name of process that has been +started in the window (or its a.\|k.\|a.); +the current window is marked with a `*'; +the previous window is marked with a `-'; +all the windows that are \*Qlogged in\*U are marked with a `$'; +a background window that has received a bell is marked with a `!'; +a background window that is being monitored and has had activity occur +is marked with an `@'; +a window which has output logging turned on is marked with `(L)'. +.IP "\fBC-a W\fP (width)" +Toggle the window width between 80 and 132 columns. As this command became +a toggle, \*QC-a N\*U is no longer supported. +This requires a capable terminal and the termcap entries \*QZ0\*U and \*QZ1\*U. +See the \*Qtermcap\*U command in section \*QCUSTOMIZATION\*U +for more information. +.IP "\fBC-a x\fP or \fBC-a C-x\fP (lock)" +Call a screenlock program (/local/bin/lck or /usr/bin/lock or a builtin, if no +other is available). Screen does not accept any command keys until this program +terminates. Meanwhile processes in the windows may continue, as the windows +are in the `detached' state. The screenlock program may be changed through the +environment variable $LOCKPRG (which must be set in the shell from which +.I screen +is started) and is executed with the user's uid and gid. +.IP "\fBC-a z\fP or \fBC-a C-z\fP (suspend)" +Suspend +.IR screen . +The windows are in the `detached' state, while +.IR screen +is suspended. +.IP "\fBC-a Z\fP (reset)" +Reset the virtual terminal to its \*Qpower-on\*U values. +.IP "\fBC-a .\fP (termcap)" +Write the termcap entry for the virtual terminal of the currently active +window to the file \*Q.termcap\*U in the user's \*Q$HOME/.screen\*U directory +(or wherever +.I screen +stores its sockets. See the \*QFILES\*U section below). +This termcap entry is identical to the value of the environment variable +$TERMCAP that is set up by +.I screen +for each window. For terminfo based systems you will need to run a converter +like +.IR captoinfo +and then compile the entry with +.IR tic . +.IP "\fBC-a ?\fP (help)" +Displays a help screen showing you all the key bindings. +The first pages list all the internal commands followed by their bindings. +Subsequent pages will display the custom commands, one command per key. +Press space when you're done reading each page, or return to exit early. +All other characters are ignored, except for the command character, which will +exit the help display and begin a command. +.IP "\fBC-a C-\e\fP (quit)" +Kill all windows and terminate +.IR screen . +Note that on vt100-style terminals the keys C-4 and C-\e are identical. +So be careful not to write C-a C-4 when selecting window no. 4. +Use the empty bind command (as in \*Qbind '^\'\*U) to remove a key binding. +.IP "\fBC-a :\fP (colon)" +Allows you to enter \*Q.screenrc\*U command lines as well as names of +key-bound-functions. Useful for on-the-fly modification of key bindings, +specific window creation and changing settings. Settings of the current window +can be changed by prepending the command (if appropriate) with the keyword +\*Qset\*U. If you consider this as the `Ex command mode' of +.IR screen , +you may regard \*QC-a esc\*U as its `Vi command mode'. +.IP "\fBC-a [\fP or \fBC-a C-[\fP or \fBC-a esc\fP (copy)" +Enter copy/scrollback mode. This allows you to copy text from the current +window and its history into the paste buffer. In this mode a vi-like +`full screen editor' is active: +.br +.IR "Movement keys" : +.br +\fBh\fP, \fBj\fP, \fBk\fP, \fBl\fP move the cursor line by line or column by column. +.br +\fB0\fP, \fB^\fP and \fB$\fP move to the leftmost column, to the first or last +non-whitespace +character on the line. +.br +\fBH\fP, \fBM\fP and \fBL\fP move the cursor to the leftmost column +of the top, center or bottom line of the window. +.br +\fB+\fP and \fB-\fP positions one line up and down. +.br +\fBG\fP moves to the specified absolute line (default: end of buffer). +.br +\fB|\fP moves to the specified absolute column. +.br +\fBw\fP, \fBb\fP, \fBe\fP move the cursor word by word. +.br +\fBC-u\fP and \fBC-d\fP scroll the display up/down by the specified amount of +lines while preserving the cursor position. (Default: half screenfull). +.br +\fBC-b\fP and \fBC-f\fP scroll the display up/down a full screen. +.br +\fBg\fP moves to the beginning of the buffer, whereas +.br +\fB%\fP jumps to the specified percentage. +.br +.IR Note : +Emacs style movement keys can be customized by a .screenrc command. (E.\|g. +markkeys "h=^B:l=^F:$=^E") There is no simple method for a full emacs-style +keymap, as this involves multi-character codes. +.br +.IR Marking : +.br +The copy range is specified by setting two marks. The text between these marks +will be highlighted. Press \fBspace\fP to set the first or second mark +respectively. +.br +\fBY\fP and \fBy\fP can be used to mark one whole line or to mark from +start of line. +.br +\fBW\fP marks exactly one word. +.br +.IR "Repeat count" : +.br +Any of these commands can be prefixed with a number (by pressing digits +\fB0\fP..\fB9\fP) which +is taken as a repeat count. Example: \*QC-a C-[ H 10 j 5 Y\*U will copy lines +11 to 15 into the pastebuffer. +.br +.IR Specials : +.br +There are however some keys that act differently here and in +.IR vi . +.I Vi +does not allow to yank rectangular blocks of text, but +.I screen +does. Press +.br +\fBc\fP or \fBC\fP to set the left or right margin respectively. If no repeat count is +given, both default to the current cursor position. Try this on a rather +full text screen as an example: \*QC-A [ S-M 20 l SPACE c 10 l 5 j S-C SPACE\*U. +.br +\fBJ\fP joins lines. It toggles between +3 modes: lines separated by a newline character (012), lines glued seamless, +lines separated by a single whitespace. Note that you can prepend the newline +character with a carriage return character, by issuing a \*Qset crlf on\*U. +.br +\fBv\fP is for all the +.I vi +users with \*Q:set numbers\*U - it toggles the left margin between column 9 +and 1. +.br +Press \fBa\fP before the final space key to toggle in append mode. Thus +the contents of the pastebuffer will not be overwritten, but appended to. +.br +\fBA\fP toggles in append mode and sets a (second) mark. +.br +\fB>\fP sets the (second) mark and writes the contents of the copybuffer to +the screen-exchange file (/tmp/screen-exchange per default) once copy-mode is +finished. This example demonstrates how to dump the whole scrollback buffer +to that file: \*QC-A [ g SPACE G $ >\*U. +.br +\fB?\fP gives information about the current line and column. +.IP "\fBC-a ]\fP or \fBC-a C-]\fP (paste)" +Paste. Write the contents of the paste buffer to the stdin queue of the +current window. +.IP "\fBC-a {\fP (history)" +Usually users work with a shell that allows easy access to previous commands. +E.g. csh has the command \*Q!!\*U to repeat the last command executed. +.I Screen +allows you to have a primitive way of re-calling \*Qthe command that +started ...\*U: You just type the first letter of that command, then hit +`C-a {' and +.I screen +tries to find a previous line that matches with the `prompt character' +to the left of the cursor. This line is pasted into this window's input queue. +Thus you have a crude command history (made up by the visible window and its +scrollback buffer). +.IP "\fBC-a >\fP (write_buffer)" +Writes the contents of the paste buffer to a public accessible screen-exchange +file. This is thought of as a primitive means of communication between +.I screen +users on the same host. See also \*QC-a esc\*U. +.IP "\fBC-a <\fP (read_buffer)" +Reads the screen-exchange file into the pastebuffer. See also \*QC-a ]\*U. +.IP "\fBC-a =\fP (kill_buffer)" +Removes the exchange file used by \*QC-a <\*U and \*QC-a >\*U. +.SH "COMMAND-LINE OPTIONS" +Screen has the following command-line options: +.TP 5 +.B \-a +include \fIall\fP capabilities (with some minor exceptions) in each +window's termcap, even if +.I screen +must redraw parts of the display in order to implement a function. +.TP 5 +.B "\-c \fIfile\fP" +override the default configuration file from \*Q$HOME/.screenrc\*U +to \fIfile\fP. +.TP 5 +.B "\-d\fP|\fB\-D \fP[\fIpid.tty.host\fP\]\fB" +does not start +.IR screen , +but detach the elsewhere running +.I screen +session. It has the same effect as typing \*QC-a d\*U from +.I screen's +controlling terminal. \fB-D\fP is the equivalent to the power detach key. +If no session can be detached, this option is ignored. +The combination \*Qscreen -D -r\*U can be used to `transport' the elsewhere +running session to this terminal and logout there. +Note: It is a good idea to have the status of your sessions checked by means of +\*Qscreen -list\*U. +.TP 5 +.B "\-e \fIxy\fP" +specifies the command character to be \fIx\fP and the character generating a +literal command character to \fIy\fP (when typed after the command character). +The default is \*QC-a\*U and `a', which can be specified as \*Q-e^Aa\*U. +See the \*Qescape\*U .screenrc command for more details. +.TP 5 +.B "\-f\fP, \fB\-fn\fP, and \fB\-fa" +turns flow-control on, off, or \*Qautomatic switching mode\*U. +This can also be defined through the \*Qflow\*U .screenrc command. +.TP 5 +.B "\-h \fInum\fP" +Specifies the history scrollback buffer to be \fInum\fP lines high. +.TP 5 +.B \-i +will cause the interrupt key (usually C-c) to interrupt the display +immediately when flow-control is on. +See the \*Qflow\*U .screenrc command for details. +.TP 5 +.B "\-l \fPand\fB -ln" +turns login mode on or off (for /etc/utmp updating). +This can also be defined through the \*Qlogin\*U .screenrc command. +.TP 5 +.B "\-ls \fPand\fB -list" +does not start +.IR screen , +but prints a list of +.I pid.tty.host +strings identifying your +.I screen +sessions. +Sessions marked `detached' can be resumed with \*Qscreen -r\*U. Those marked +`attached' are running and have a controlling terminal. Sessions marked as +`dead' should be thoroughly checked and removed. Ask your system administrator +if you are not sure. Remove sessions with the \fB-wipe\fP option. +.TP 5 +.B "\-t \fIname\fP" +sets the title (a.\|k.\|a.) for the default shell or specified program. +See also the \*Qshellaka\*U .screenrc command. +.TP 5 +.B "\-wipe" +does the same as \*Qscreen -ls\*U, but removes destroyed sessions instead of +marking them as `dead'. +.TP 5 +.B \-L +tells +.I screen +your auto-margin terminal has a writable last-position on +the screen. +This can also be set in your .screenrc by specifying `LP' in a \*Qtermcap\*U +command. +.TP 5 +.B \-O +selects a more optimal output mode for your terminal rather than true vt100 +emulation (only affects auto-margin terminals without `LP'). +This can also be set in your .screenrc by specifying `OP' in a \*Qtermcap\*U +command. +.TP 5 +.B "\-r \fP[\fIpid.tty.host\fP]" +resumes a detached +.I screen +session. +No other options (except \*Q-d -r\*U or \*Q-D -r\*U) may be specified, though +an optional prefix of [\fIpid.\fP]\fItty.host\fP +may be needed to distinguish between multiple detached +.I screen +sessions. +.TP 5 +.B \-R +attempts to resume the first detached +.I screen +session it finds. +If successful, all other command-line options are ignored. +If no detached session exists, starts a new session using the specified +options, just as if +.B \-R +were not specified. +.TP 5 +.B \-s +sets the default shell to the program specified, instead of the value +in the environment variable $SHELL (or \*Q/bin/sh\*U if not defined). +This can also be defined through the \*Qshell\*U .screenrc command. +.SH CUSTOMIZATION +The \*Qsocket directory\*U defaults either to $HOME/.screen or preferably +to /local/screens. If +.I screen +is installed setuid-root, then the administrator +should compile screen with an adequate (not NFS mounted) SOCKDIR. If +.I screen +is not running setuid-root, the user can specify any mode 777 directory +in the environment variable $SCREENDIR. +.PP +When +.I screen +is invoked, it executes initialization commands from the files +\*Q/local/etc/screenrc\*U and +\*Q.screenrc\*U in the user's home directory. These are the \*Qprogrammer's +defaults\*U that can be overridden in the following ways: For the +global screenrc file +.I screen +searches for the environment variable $SYSSCREENRC. The user specific +screenrc file is searchend in $ISCREENRC, then $SCREENRC, then $HOME/.iscreenrc +and finally defaults to $HOME/.screenrc. The command line option \fB-c\fP takes +precedence over the user specific screenrc file. +Commands in these files are used to set options, bind functions to +keys, and to automatically establish one or more extra windows at the +beginning of your +.I screen +session. +Commands are listed one per line, with empty lines being ignored. +A command's arguments are separated by tabs or spaces, and may be +surrounded by single or double quotes. +A `#' turns the rest of the line into a comment, except in quotes. +Unintelligible lines are warned about and ignored. +Commands may contain references to environment variables. The +syntax is the shell-like "$VAR " or "${VAR}". Note that this causes +incompatibilities with previous +.I screen +versions, as now the '$'-sign has to be protected with '\' if no +variable substitution shall be performed. +.PP +Customization can also be done 'on-line'. To enter the command mode type +`C-a :'. Setting flags (like scrollback, login, etc.) on-line may not have +the desired effect, as e.\|g. the command "C-a : login on" will affect +only the settings for the creation of new windows (just like within .screenrc). +If you intend to change flags +of the current window, then prepend the command with the keyword "set". E.\|g. +"C-a : set login on" will log this window in. +.PP +The following initialization commands are available: +.PP +.ne 3 +.B "activity \fImessage\fP" +.sp +When any activity occurs in a background window that is being monitored, +.I screen +displays a notification in the message line. +The notification message can be re-defined by means of the \*Qactivity\*U +command. +Each occurrence of `%' in \fImessage\fP is replaced by +the number of the window in which activity has occurred, +and each occurrence of `~' is replaced by the definition for bell +in your termcap (usually an audible bell). +The default message is +.sp + 'Activity in window %' +.sp +Note that monitoring is off for all windows by default, but can be altered +by use of the \*Qmonitor\*U command (C-a M). +.PP +.ne 3 +.B "autodetach on\fP|\fBoff" +.sp +Sets whether +.I screen +will automatically detach upon hangup, which +saves all your running programs until they are resumed with a +.B "screen -r" +command. +When turned off, a hangup signal will terminate +.I screen +and all the processes it contains. Autodetach is on by default. +.PP +.ne 3 +.B "bell \fImessage\fP" +.sp +When a bell character is sent to a background window, +.I screen +displays a notification in the message line. +The notification message can be re-defined by means of the \*Qbell\*U +command. +Each occurrence of `%' in \fImessage\fP is replaced by +the number of the window to which a bell has been sent, +and each occurrence of `~' is replaced by the definition for bell +in your termcap (usually an audible bell). +The default message is +.sp + 'Bell in window %' +.sp +An empty message can be supplied to the \*Qbell\*U command to suppress +output of a message line (bell ""). +.PP +.ne 3 +.B "bind \fP\fIkey\fP [\fIfunction\fP [\fIargs\fP]]" +.sp +Bind a function to a key. +By default, each function provided by +.I screen +is bound to one or more keys as indicated by the above table, e.\|g. the +function to create a new window is bound to \*QC-c\*U and \*Qc\*U. +The \*Qbind\*U command can be used to redefine the key bindings and to +define new bindings. +The \fIkey\fP argument is either a single character, a two-character sequence +of the form \*Q^x\*U (meaning \*QC-x\*U), a backslash followed by an octal +number (specifying the ASCII code of the character), or a backslash followed +by a second character, such as \*Q\e^\*U or \*Q\e\e\*U. +The argument can also be quoted, if you like. +If no further argument is given, any previously established binding +for this key is removed. +The \fIfunction\fP argument can be one of the following keywords: +.PP +.nf + select0 Switch to window #0 + \0\0... + select9 Switch to window #9 + aka Change the current window's a.\|k.\|a. + clear Clear the screen + colon Enter one \*Q.screenrc\*U command on-the-fly. + copy Select a piece of text. + detach Detach \fIscreen\fP + flow Toggle the current window's flow-control setting + hardcopy Make hardcopy of current window + history Use cut&paste to fetch a recently displayed line. + help Display a list of the key bindings in effect + info Display the current window's status information + kill Kill the current window + lastmsg Redisplay the last message line + lock Run a screenlock program. + log Begin/end logging of the current window's output + login Toggle the window's \*Qlogin\*U setting (/etc/utmp entry) + monitor Toggle activity monitoring of the current window + next Switch to the next window + other Switch to the window displayed previously + paste Paste in a selected piece of text. + pow_detach Detach \fIscreen\fP and logout. + prev Switch to the previous window + quit Kill all windows and terminate + readbuffer Take text from the exchange file. + redisplay Redisplay current window + reset Reset the window to its \*Qpower-on\*U settings + screen Create a new window with the specified command + shell Create a new window with a shell + suspend Suspend \fIscreen\fP + termcap Write screen's termcap entry to $HOME/.screencap + vbell Toggle the window's \*Qvbell\*U setting. + version Display the version numbers and date last modified + width Toggle the terminal width between 80 and 132 columns + windows Display a list of all windows + wrap Toggle the current window's line-wrap setting + writebuffer Store the selected text in the exchange file. + xoff Send a control-s to the current program + xon Send a control-q to the current program +.fi +.PP +Some examples: +.PP +.nf + bind ' ' windows + bind ^f screen telnet foobar + bind \e033 screen -ln -t root -h 1000 9 su +.fi +.PP +would bind the space key to the function that displays a list +of windows (so that the function usually invoked by \*QC-a C-w\*U +would also be available as \*QC-a space\*U), +bind \*QC-f\*U to the function \*Qcreate a window with a TELNET +connection to foobar\*U, and bind \*Qescape\*U to the function +that creates an non-login window with a.\|k.\|a. \*Qroot\*U in slot #9, with +a super-user shell and a scrollbackbuffer of 1000 lines. +.PP +.ne 3 +.B "bufferfile \fIexchange-file\fP" +.sp +Change the filename used for reading and writing with the copybuffer. +The default is \*Q/tmp/screen-exchange\*U. The following example +will paste the system's password file into the screen window: +.PP +.nf + C-a : bufferfile /etc/passwd + C-a < C-a ] +.fi +.PP +.ne 3 +.B "chdir \fP[\fIdirectory\fP]" +.sp +Change the \fIcurrent directory\fP of +.I screen +to the specified directory or, if called without an argument, +to your home directory (the value of the environment variable $HOME). +All windows that are created by means of the \*Qscreen\*U command +from within \*Q.screenrc\*U or by means of \*QC-a : screen ...\*U +or \*QC-a c\*U use this as their default directory. +Without a chdir command, this would be the directory from which +.I screen +was invoked. +Hardcopy and log files are always written to the \fIwindow's\fP default +directory, \fInot\fP the current directory of the process running in the +window. +You can use this command multiple times in your .screenrc to start various +windows in different default directories, but the last chdir value will +affect all the windows you create interactively. +.PP +.ne 3 +.B "crlf on\fP|\fBoff" +.sp +This affects the copying of text regions with the `C-a [' command. If it is set +to `on', lines will be separated by the two character sequence `CR' - `LF'. +Otherwise only `LF' is used. +.PP +.ne 3 +.B "echo \fP[\fB-n\fP]\fB \fImessage\fP" +.sp +The echo command may be used to annoy +.I screen +users with a 'message of the +day'. Typically installed in a global /usr/local/etc/screenrc. See also +\*Qsleep\*U. +Echo is also useful for online checking of environment variables. +.PP +.ne 3 +.B "escape \fIxy\fP" +.sp +Set the command character to \fIx\fP and the character generating a literal +command character to \fIy\fP (just like in the \-e option). +Each argument is either a single character, a two-character sequence +of the form \*Q^x\*U (meaning \*QC-x\*U), a backslash followed by an octal +number (specifying the ASCII code of the character), or a backslash followed +by a second character, such as \*Q\e^\*U or \*Q\e\e\*U. +The default is \*Q^Aa\*U, but \*Q``\*U is recommended by one of the authors. +.PP +.ne 3 +.B "flow on\fP|\fBoff\fP|\fBauto \fP[\fBinterrupt\fP]\fB" +.sp +Sets the default flow-control mode for new windows. +Specifying \*Qflow auto interrupt\*U is the same as the command-line options +.B \-fa +and +.BR \-i . +See the discussion on FLOW-CONTROL later on in this document for full details +and note, that this is subject to change in future releases. +.PP +.ne 3 +.B "hardcopy_append on\fP|\fBoff" +.sp +If set to "on", +.I screen +will append to the "hardcopy.n" files created by the command \*QC-a h\*U, +otherwise these files are overwritten each time. +.PP +.ne 3 +.B "hardstatus on\fP|\fBoff" +.sp +Toggles the use of the terminals hardware status line. If "on", +.I screen +will use this facility to display one line messages. Otherwise these messages +are overlayed in reverse video mode at the display line. Note that the +hardstatus feature should only be used, if the termcap/terminfo capabilities +"hs", "ts", "fs" and "ds" are set properly. +.PP +.ne 3 +.B "login on\fP|\fBoff" +.sp +Sets the login flag which determines if new windows should have /etc/utmp +entries added for them. +The login state is also changeable on-the-fly by using the bindable version +of the \*Qlogin\*U command (C-a L) of by means of "C-a : set login on|off". +The default should be \*Qon\*U for a +.I screen +that runs under suid-root. +.PP +.ne 3 +.B "markkeys \fIstring\fP" +.sp +This is a method of changing the keymap used for copy/history mode. +The string is made up of \fIoldchar\fP=\fInewchar\fP pairs which are +separated by `:'. Example: The string \*QB=^B:F=^F\*U will change the +keys `C-b' and `C-f' from their (original emacs-style bindings) to the vi style +binding (scroll up/down full page), which is the default meaning of `B' and +`F'. +.PP +.ne 3 +.B "mode \fImode\fP" +.sp +The mode of each newly allocated pseudo-tty is set to \fImode\fP. +\fIMode\fP is an octal number. +When no \*Qmode\*U command is given, mode 0622 is used. +.PP +.ne 3 +.B "nethack on\fP|\fBoff" +.sp +Changes the kind of error messages used by +.IR screen . +When you are familiar with the game \*Qnethack\*U, you may enjoy the +nethack-style messages which will often blur the facts a little, but are +much funnier to read. Anyway, standard messages often tend to be unclear as +well. +.br +This option is only +available, if screen was compiled with the NETHACK flag defined. The +default setting is then determined by the presence of the environment +variable $NETHACKOPTIONS. +.PP +.ne 3 +.B "msgminwait \fIsec\fP" +.sp +Defines the time +.I screen +delays all activity after a message was dispayled. The default is 1 second. +.PP +.ne 3 +.B "msgwait \fIsec\fP" +.sp +Defines the time a message is displayed, if +.I screen +is not disturbed by other activity. The default is 5 seconds. +.PP +.ne 3 +.B "password \fP[\fIcrypted_pw\fP]" +.sp +Present a crypted password in your \*Q.screenrc\*U file and screen will ask +for it, whenever a detached session is tried to be resumed. This is useful, +if you have privileged programs running under +.I screen +and you want to protect your session from reattach attempts by users that +managed to have your uid. (I.e. any superuser.) +.PP +.ne 3 +.B "pow_detach_msg \fImessage\fP" +.sp +The \fImessage\fP specified here is output whenever a `Power detach' was +performed. It may be used as a replacement for a logout message or to reset +baud rate, etc. +.PP +.ne 3 +.B "screen \fP[\fI-opts\fP] [\fIn\fP] [\fIcmd\fP [\fIargs\fP]]" +.sp +Establish a new window. +The flow-control options (\fB\-f\fP, \fB\-fn\fP and \fB\-fa\fP), +title (a.\|k.\|a.) option (\fB\-t\fP), login options (\fB-l\fP and \fB-ln\fP) +, terminal type option (\fB-T \fP) and scrollback option (\fB-h\fP ) +may be specified for each command. +If an optional number \fIn\fP in the range 0..9 is given, the window +number \fIn\fP is assigned to the newly created window (or, if this +number is already in-use, the next available number). +If a command is specified after \*Qscreen\*U, this command (with the given +arguments) is started in the window; otherwise, a shell is created. +Thus, if your \*Q.screenrc\*U contains the lines +.sp +.nf + # example for .screenrc: + screen 1 + screen -fn -t foobar 2 telnet foobar +.fi +.sp +.I screen +creates a shell window (in window #1), a window with a TELNET connection +to the machine foobar (with no flow-control using the a.\|k.\|a. \*Qfoobar\*U +in window #2), and finally, a second shell window +(the default window) which gets a window number of zero. +When the initialization is completed, +.I screen +switches to the last window specified in your .screenrc file or, if none, it +opens a default window #0 that would be displayed in this +case. +.PP +.ne 3 +.B "scrollback \fP\fInum\fP" +.sp +Set the size of the scrollback buffer for new windows to \fInum\fP lines. +The default scrollback is 50 lines. +Use "C-a : set scrollback \fInum\fP" to change the scrollback size of the +current window and use "C-a i" to view the current setting. +.PP +.ne 3 +.B "redraw on\fP|\fBoff" +.sp +Define whether the display should be refreshed (as done with "C-a l") after +switching to the current window. As usual when the "set" keyword is given, +this command only affects the current window. But unlike other commands, +"redraw off" (without "set") affects all windows, the window specific settings +come into effect again when "redraw on" (without "set") is entered. +.PP +.ne 3 +.B "shell \fIcommand\fP" +.sp +Set the command to be used to create a new shell. +This overrides the value of the environment variable $SHELL, or \*Q/bin/sh\*U +if undefined. +This is useful if you'd like to run a tty-enhancer which is expecting to +execute the program specified in $SHELL. +.PP +.ne 3 +.B "shellaka \fIa.\|k.\|a.\fP" +.sp +Set the a.\|k.\|a. for all shells created during startup or by +the C-A C-c command. +For details about what a.\|k.\|a.\|'s are, see the discussion +entitled ALSO KNOWN AS. +.PP +.ne +.B "sleep \fP\fInum\fP" +This command will pause the execution of a .screenrc file for \fInum\fP seconds. +It may be used to give users a chance to read the messages output by \*Qecho\*U. +.PP +.ne 3 +.B "slowpaste \fIusec\fP" +.sp +Define the speed text is inserted by the paste ("C-a ]") command. For each +80 characters (IOSIZE) pasted +.I screen +will make a pause of \fIusec\fP milliseconds to allow the application the +processing of input. Use that, if you have to fear that your underlying system +chokes on large pastes. +.PP +.ne 3 +.B "term \fIterm\fP" +.sp +In each window's environment +.I screen +opens, it sets the $TERM variable to \*Qscreen\*U by default. +But when no description for \*Qscreen\*U is installed in the local termcap +or terminfo data base, you can pretend that the terminal emulator is - say - +\*Qvt100\*U. This won't do much harm, as +.I screen +is VT100/ANSI compatible. +The use of the \*Qterm\*U command is discouraged for non-default purpose. +That is, one may want to specify special $TERM settings (e.g. vt100) for the +next \*Qscreen rlogin othermachine\*U command. Use the command \*Qscreen -T vt100 +rlogin othermachine\*U rather than setting (\*Qterm vt100\*U) and resetting +(\*Qterm screen\*U) the default before and after the \*Qscreen\*U command. +.PP +.ne 3 +.B "termcap \fIterm\fP \fIterminal-tweaks\fP \fP[\fIwindow-tweaks\fP]" +.br +.B "terminfo \fIterm\fP \fIterminal-tweaks\fP \fP[\fIwindow-tweaks\fP]" +.sp +Use this command to modify your terminal's termcap entry without going through +all the hassles involved in creating a custom termcap entry. +Plus, you can optionally customize the termcap generated for the windows. +If your system works with terminfo-database rather than with +termcap +.I screen +will understand the `terminfo' command which has the same effects as +the `termcap' command. Thus users can write one .screenrc file that handles +both cases, although terminfo syntax is slightly different +from termcap syntax. +.PP +The first argument specifies which terminal(s) should be affected by this +definition. +You can specify multiple terminal names by separating them with `|'s. +Use `*' to match all terminals and `vt*' to match all terminals that begin +with \*Qvt\*U. +.PP +Each \fItweak\fP argument contains one or more termcap defines (separated +by `:'s) to be inserted at the start of the appropriate termcap entry, +enhancing it or overriding existing values. +The first tweak modifies your terminal's termcap, and contains definitions +that your terminal uses to perform certain functions. +Specify a null string to leave this unchanged (e.\|g. ''). +The second (optional) tweak modifies all the window termcaps, and should +contain definitions that screen understands (see the \*QVIRTUAL TERMINAL\*U +section). +.PP +Some examples: +.IP +termcap xterm* LP:hs@ +.PP +Informs +.I screen +that all terminals that begin with `xterm' have firm auto-margins that +allow the last position on the screen to be updated (LP), but they don't +really have a status line (no 'hs' -- append `@' to turn entries off). +Note that we assume `LP' for all terminal names that start with \*Qvt\*U, +but only if you don't specify a termcap command for that terminal. +.sp +.nf + termcap vt* LP + termcap vt102|vt220 Z0=\eE[?3h:Z1=\eE[?3l +.fi +.sp +Specifies the firm-margined `LP' capability for all terminals that begin with +`vt', and the second line will also add the escape-sequences to switch +into (Z0) and back out of (Z1) 132-character-per-line mode if this is +a vt102 or vt220. +(You must specify Z0 and Z1 in your termcap to use the width-changing +commands.) +.IP +termcap vt100 "" l0=PF1:l1=PF2:l2=PF3:l3=PF4 +.PP +This leaves your vt100 termcap alone and adds the function key labels to +each window's termcap entry. +.IP +termcap h19|z19 am@:im=\eE@:ei=\eEO dc=\eE[P +.PP +Takes a h19 or z19 termcap and turns off auto-margins (am@) and enables the +insert mode (im) and end-insert (ei) capabilities (the `@' in the `im' +string is after the `=', so it is part of the string). +Having the `im' and `ei' definitions put into your terminal's termcap will +cause screen to automatically advertise the character-insert capability in +each window's termcap. +Each window will also get the delete-character capability (dc) added to its +termcap, which screen will translate into a line-update for the terminal +(we're pretending it doesn't support character deletion). +.PP +If you would like to fully specify each window's termcap entry, you should +instead set the $SCREENCAP variable prior to running +.IR screen . +See the discussion on the \*QVIRTUAL TERMINAL\*U in this manual, and the termcap(5) +man page for more information on termcap definitions. +.PP +.ne 3 +.B "vbell on\fP|\fBoff" +.sp +Sets the visual bell setting for new windows. If your terminal does not support +a visual bell a message is printed to the status line. The default +message is \*QWuff, Wuff!!\*U. +.PP +.ne 3 +.B "vbell_msg \fImessage\fP" +.sp +Sets the visual bell message. \fImessage\fP is printed to the status line if +the window receives a bell character (^G) and vbell is set to \*Qon\*U. +.PP +.ne 3 +.B "vbellwait \fIsec\fP" +.sp +Define a delay in seconds after each display of +.I screen 's +visual bell message. The default is 0 seconds. +.PP +.ne 3 +.B "wrap on\fP|\fBoff" +.sp +Sets the line-wrap setting for new windows. +When line-wrap is on, the second consecutive printable character output at +the last column of a line will wrap to the start of the following line. +As an added feature, backspace (^H) will also wrap through the left margin +to the previous line. +Line-wrap is on by default and can be toggled with the \*Qwrap\*U +command (\*QC-a r\*U) or by means of "C-a : set wrap on|off". +.SH "THE MESSAGE LINE" +.I Screen +displays informational messages and other diagnostics in a \fImessage line\fP +at the bottom of the screen. +If your terminal has a status line defined in its termcap, screen will use +this for displaying its messages, otherwise the last line of the screen will +be temporarily overwritten and output will be momentarily interrupted. +The message line is automatically removed after a few seconds delay, but it +can also be removed early (on terminals without a status line) by beginning +to type. +.PP +The message line facility can be used by an application running in +the current window by means of the ANSI \fIPrivacy message\fP +control sequence. +For instance, from within the shell, try something like: +.IP +echo '^Hello world\e\e' +.PP +where '' is an \fIescape\fP, '^' is a literal up-arrow, +and '\e\e' turns into a single backslash. +.SH "FLOW-CONTROL" +Each window has a flow-control setting that determines how screen deals with +the XON and XOFF characters (and perhaps the interrupt character). +When flow-control is turned off, screen ignores the XON and XOFF characters, +which allows the user to send them to the current program by simply typing +them (useful for the \fIemacs\fP editor, for instance). +The trade-off is that it will take longer for output from a \*Qnormal\*U +program to pause in response to an XOFF. +With flow-control turned on, XON and XOFF characters are used to immediately +pause the output of the current window. +You can still send these characters to the current program, but you must use +the appropriate two-character screen commands (typically \*QC-a q\*U (xon) +and \*QC-a s\*U (xoff)). +The xon/xoff commands are also useful for typing C-s and C-q past a terminal +that intercepts these characters. +.PP +Each window has an initial flow-control value set with either the +.B \-f +option or the \*Qflow\*U .screenrc command. Per default the windows +are set to automatic flow-switching. +It can then be toggled between the three states 'fixed on', 'fixed off' and +'automatic' interactively with the \*Qflow\*U command bound to "C-a f". +.PP +The automatic flow-switching mode deals with +flow control using the TIOCPKT mode (like \*Qrlogin\*U does). If +the tty driver does not support TIOCPKT, screen tries to find out +the right mode based on the current setting of the application +keypad -- when it is enabled, flow-control is turned off and visa versa. +Of course, you can still manipulate flow-control manually when needed. +.PP +If you're running with flow-control enabled and find that pressing the +interrupt key (usually C-c) does not interrupt the display until another +6-8 lines have scrolled by, try running screen with the \*Qinterrupt\*U +option (add the \*Qinterrupt\*U flag to the \*Qflow\*U command in +your .screenrc, or use the +.B \-i +command-line option). +This causes the output that +.I screen +has accumulated from the interrupted program to be flushed. +One disadvantage is that the virtual terminal's memory contains the +non-flushed version of the output, which in rare cases can cause +minor inaccuracies in the output. +For example, if you switch screens and return, or update the screen +with \*QC-a l\*U you would see the version of the output you would +have gotten without \*Qinterrupt\*U being on. +Also, you might need to turn off flow-control (or use auto-flow mode to turn +it off automatically) when running a program that expects you to type the +interrupt character as input, as it is possible to interrupt +the output of the virtual terminal to your physical terminal when flow-control +is enabled. +If this happens, a simple refresh of the screen with \*QC-a l\*U will +restore it. +Give each mode a try, and use whichever mode you find more comfortable. +.SH "ALSO KNOWN AS (A.\|K.\|A.\|s)" +You can customize each window's name in the window display (viewed with the +\*Qwindows\*U command (C-a w)) by setting it with one of +the a.\|k.\|a. commands. +Normally the name displayed is the actual command name of the program +created in the window. +However, it is sometimes useful to distinguish various programs of the same +name or to change the name on-the-fly to reflect the current state of +the window. +.PP +The default name for all shell windows can be set with the \*Qshellaka\*U +command in the .screenrc file, while all other windows are created with +a \*Qscreen\*U command and thus can have their name set with the +.B \-t +option. +Interactively, there is the AKA-string escape-sequence +(k\fIname\fP\e) and the \*Qaka\*U command (C-a A). +The former can be output from an application to control the window's name +under software control, and the latter will prompt for a name when typed. +You can also bind pre-defined names to keys with the \*Qaka\*U command +to set things quickly without prompting. +.PP +Finally, +.I screen +has a shell-specific heuristic that is enabled by setting the window's name +to \*Q\fIsearch|name\fP\*U and arranging to have a null aka escape-sequence +output as a part of your prompt. +The \fIsearch\fP portion specifies an end-of-prompt search string, while +the \fIname\fP portion specifies the default shell name for the window. +If the \fIname\fP ends in a `:' +.I screen +will add what it believes to be the current command running in the window +to the end of the window's shell name (e.\|g. \*Q\fIname:cmd\fP\*U). +Otherwise the current command name supersedes the shell name while it is +running. +.PP +Here's how it works: you must modify your shell prompt to output a null +aka escape-sequence (k\e) as a part of your prompt. +The last part of your prompt must be the same as the string you specified +for the \fIsearch\fP portion of the a.\|k.\|a. +Once this is set up, +.I screen +will use the aka escape-sequence to clear the previous command name and +get ready for the next command. +Then, when a newline is received from the shell, a search is made for the +end of the prompt. +If found, it will grab the first word after the matched string and use it +as the command name. +If the command name begins with either '!', '%', or '^' +.I screen +will use the first word on the following line (if found) in preference to +the just-found name. +This helps csh users get better command names when using job control or +history recall commands. +.PP +Here's some .screenrc examples: +.IP +screen -t top 2 nice top +.PP +Adding this line to your .screenrc would start a niced version of the +\*Qtop\*U command in window 2 name \*Qtop\*U rather than \*Qnice\*U. +.sp +.nf + shellaka '> |csh' + screen 1 +.fi +.sp +This file would start two shells (one specified, one default) using +the given shellaka. +The a.\|k.\|a. specified is an auto-aka that would expect the prompt and +the typed command to look something like the following: +.IP +/usr/joe/src/dir> trn +.PP +(it looks after the '> ' for the command name). +The window status would show the name \*Qtrn\*U while the command was +running, and revert to \*Qcsh\*U upon completion. +.IP +bind R screen -t '% |root:' su +.PP +Having this command in your .screenrc would bind the key +sequence \*QC-a R\*U to the \*Qsu\*U command and give it an +auto-aka name of \*Qroot:\*U. +For this auto-aka to work, the screen could look something +like this: +.sp +.nf + % !em + emacs file.c +.fi +.sp +Here the user typed the csh history command \*Q!em\*U which ran the +previously entered \*Qemacs\*U command. +The window status would show \*Qroot:emacs\*U during the execution +of the command, and revert to simply \*Qroot:\*U at its completion. +.PP +.nf + bind o aka + bind E aka "" + bind u aka (unknown) +.fi +.sp +The first binding doesn't have any arguments, so it would prompt you +for an a.\|k.\|a. when you type \*QC-a o\*U. +The second binding would clear an auto-aka's current setting (C-a E). +The third binding would set the current window's a.\|k.\|a. to \*Q(unknown)\*U +(C-a u). +.PP +One thing to keep in mind when adding a null aka escape-sequence to +your prompt is that some shells (like the csh) count all the non-control +characters as part of the prompt's length. +If these invisible characters aren't a multiple of 8 then backspacing over +a tab will result in an incorrect display. +One way to get around this is to use a prompt like this: +.IP +set prompt='^[[0000m^[k^[\e% ' +.PP +The escape-sequence \*Q[0000m\*U not only normalizes the character +attributes, but all the zeros round the length of the invisible characters +up to 8. +Bash users will probably want to echo the escape sequence in the +PROMPT_COMMAND: +.IP +PROMPT_COMMAND='echo -n -e "\e033k\e033\e134"' +.PP +(I used \*Q\134\*U to output a `\e' because of a bug in v1.04). +.SH "THE VIRTUAL TERMINAL" +Usually +.I screen +tries to emulate as much of the VT100/ANSI standard +as possible. But if your terminal lacks certain capabilities +the emulation may not be complete. In these cases +.I screen +has to tell the applications that some of the features +are missing. This is no problem on machines using termcap, +because +.I screen +can use the $TERMCAP variable to +customize the standard screen termcap. +.br +But if you do a +rlogin on another machine or your machine supports only +terminfo this method fails. Because of this +.I screen +offers a way to deal with these cases. Here is how it works: +.br +When +.I screen +tries to figure out a terminal name for itself, +it first looks +for an entry named \*Qscreen.\*U, where is +the contents of your $TERM variable. +If no such entry exists, +.I screen +tries \*Qscreen\*U (or \*Qscreen-w\*U, if the terminal is wide +(132 cols or more)). +If even this entry cannot be found, \*Qvt100\*U is used as a +substitute. +.br +The idea is that if you have a terminal which doesn't +support an important feature (e.g. delete char or clear to EOS) +you can build a new termcap/terminfo entry for +.I screen +(named \*Qscreen.\*U) in which this capability +has been disabled. If this entry is installed on your +machines you are able to do +a rlogin and still keep the correct termcap/terminfo entry. +The terminal name is put in the $TERM variable +of all new windows. +.I Screen +also sets the $TERMCAP variable reflecting the capabilities +of the virtual terminal emulated. Notice that, however, on machines +using the terminfo database this variable has no effect. +Furthermore, the variable $WINDOW is set to the window number +of each window. +.br +The actual set of capabilities supported by the virtual terminal +depends on the capabilities supported by the physical terminal. +If, for instance, the physical terminal does not support underscore mode, +.I screen +does not put the `us' and `ue' capabilities into the window's $TERMCAP +variable, accordingly. +However, a minimum number of capabilities must be supported by a +terminal in order to run +.IR screen ; +namely scrolling, clear screen, and direct cursor addressing +(in addition, +.I screen +does not run on hardcopy terminals or on terminals that over-strike). +.PP +Also, you can customize the $TERMCAP value used by +.I screen +by using the \*Qtermcap\*U .screenrc command, or +by defining the variable $SCREENCAP prior to startup. +When the latter defined, its value will be copied verbatim into each +window's $TERMCAP variable. +This can either be the full terminal definition, or a filename where the +terminal \*Qscreen\*U (and/or \*Qscreen-w\*U) is defined. +.PP +Note that +.I screen +honors the \*Qterminfo\*U .screenrc command if the system uses the +terminfo database rather than termcap. +.PP +When the boolean `G0' capability is present in the termcap entry +for the terminal on which +.I screen +has been called, the terminal emulation of +.I screen +supports multiple character sets. +This allows an application to make use of, for instance, +the VT100 graphics character set or national character sets. +The following control functions from ISO 2022 are supported: +\fIlock shift G0\fP (\fISI\fP), \fIlock shift G1\fP (\fISO\fP), +\fIlock shift G2\fP, \fIlock shift G3\fP, \fIsingle shift G2\fP, +and \fIsingle shift G3\fP. +When a virtual terminal is created or reset, the ASCII character +set is designated as \fIG0\fP through \fIG3\fP. +When the `G0' capability is present, screen evaluates the capabilities +`S0', `E0', and `C0' if present. `S0' is the sequence the terminal uses +to enable and start the graphics character set rather than \fISI\fP. +`E0' is the corresponding replacement for \fISO\fP. `C0' gives a character +by character translation string that is used during semi-graphics mode. This +string is built like the `acsc' terminfo capability. +.PP +When the `po' and `pf' capabilities are present in the terminal's +termcap entry, applications running in a +.I screen +window can send output to the printer port of the terminal. +This allows a user to have an application in one window +sending output to a printer connected to the terminal, while all +other windows are still active (the printer port is enabled +and disabled again for each chunk of output). +As a side-effect, programs running in different windows can +send output to the printer simultaneously. +Data sent to the printer is not displayed in the window. +.PP +Some capabilities are only put into the $TERMCAP +variable of the virtual terminal if they can be efficiently +implemented by the physical terminal. +For instance, `dl' (delete line) is only put into the $TERMCAP +variable if the terminal supports either delete line itself or +scrolling regions. Note that this may provoke confusion, when +the session is reattached on a different terminal, as the value +of $TERMCAP cannot be modified by parent processes. +.PP +The following is a list of control sequences recognized by +.IR screen . +\*Q(V)\*U and \*Q(A)\*U indicate VT100-specific and ANSI- or +ISO-specific functions, respectively. +.PP +.nf +.TP 20 +.B "ESC E" + Next Line +.TP 20 +.B "ESC D" + Index +.TP 20 +.B "ESC M" + Reverse Index +.TP 20 +.B "ESC H" + Horizontal Tab Set +.TP 20 +.B "ESC 7" +(V) Save Cursor and Attributes +.TP 20 +.B "ESC 8" +(V) Restore Cursor and Attributes +.TP 20 +.B "ESC [s" +(A) Save Cursor and Attributes +.TP 20 +.B "ESC [u" +(A) Restore Cursor and Attributes +.TP 20 +.B "ESC c" + Reset to Initial State +.TP 20 +.B "ESC =" +(V) Application Keypad Mode +.TP 20 +.B "ESC >" +(V) Numeric Keypad Mode +.TP 20 +.B "ESC # 8" +(V) Fill Screen with E's +.TP 20 +.B "ESC \e" +(A) String Terminator +.TP 20 +.B "ESC ^" +(A) Privacy Message String (Message Line) +.TP 20 +.B "ESC k" + A.\|k.\|a. Definition String +.TP 20 +.B "ESC P" +(A) Device Control String + Outputs a string directly to the host + terminal without interpretation. +.TP 20 +.B "ESC _" +(A) Application Program Command (not used) +.TP 20 +.B "ESC ]" +(A) Operating System Command (not used) +.TP 20 +.B "Control-N" +(A) Lock Shift G1 (SO) +.TP 20 +.B "Control-O" +(A) Lock Shift G0 (SI) +.TP 20 +.B "ESC n" +(A) Lock Shift G2 +.TP 20 +.B "ESC o" +(A) Lock Shift G3 +.TP 20 +.B "ESC N" +(A) Single Shift G2 +.TP 20 +.B "ESC O" +(A) Single Shift G3 +.TP 20 +.B "ESC ( \fPPcs" +(A) Designate character set as G0 +.TP 20 +.B "ESC ) \fPPcs" +(A) Designate character set as G1 +.TP 20 +.B "ESC * \fPPcs" +(A) Designate character set as G2 +.TP 20 +.B "ESC + \fPPcs" +(A) Designate character set as G3 +.TP 20 +.B "ESC [ \fPPn\fB ; \fPPn\fB H" + Direct Cursor Addressing +.TP 20 +.B "ESC [ \fPPn\fB ; \fPPn\fB f" + Direct Cursor Addressing +.TP 20 +.B "ESC [ \fPPn\fB J" + Erase in Display +.TP 20 +\h'\w'ESC 'u'Pn = None or \fB0\fP + From Cursor to End of Screen +.TP 20 +\h'\w'ESC Pn = 'u'\fB1\fP + From Beginning of Screen to Cursor +.TP 20 +\h'\w'ESC Pn = 'u'\fB2\fP + Entire Screen +.TP 20 +.B "ESC [ \fPPn\fB K" + Erase in Line +.TP 20 +\h'\w'ESC 'u'Pn = None or \fB0\fP + From Cursor to End of Line +.TP 20 +\h'\w'ESC Pn = 'u'\fB1\fP + From Beginning of Line to Cursor +.TP 20 +\h'\w'ESC Pn = 'u'\fB2\fP + Entire Line +.TP 20 +.B "ESC [ \fPPn\fB A" + Cursor Up +.TP 20 +.B "ESC [ \fPPn\fB B" + Cursor Down +.TP 20 +.B "ESC [ \fPPn\fB C" + Cursor Right +.TP 20 +.B "ESC [ \fPPn\fB D" + Cursor Left +.TP 20 +.B "ESC [ \fPPs\fB ;\fP...\fB; \fPPs\fB m" + Select Graphic Rendition +.TP 20 +\h'\w'ESC 'u'Ps = None or \fB0\fP + Default Rendition +.TP 20 +\h'\w'ESC Ps = 'u'\fB1\fP + Bold +.TP 20 +\h'\w'ESC Ps = 'u'\fB2\fP +(A) Faint +.TP 20 +\h'\w'ESC Ps = 'u'\fB3\fP +(A) \fIStandout\fP Mode (ANSI: Italicized) +.TP 20 +\h'\w'ESC Ps = 'u'\fB4\fP + Underlined +.TP 20 +\h'\w'ESC Ps = 'u'\fB5\fP + Blinking +.TP 20 +\h'\w'ESC Ps = 'u'\fB7\fP + Negative Image +.TP 20 +\h'\w'ESC Ps = 'u'\fB22\fP +(A) Normal Intensity +.TP 20 +\h'\w'ESC Ps = 'u'\fB23\fP +(A) \fIStandout\fP Mode off (ANSI: Italicized off) +.TP 20 +\h'\w'ESC Ps = 'u'\fB24\fP +(A) Not Underlined +.TP 20 +\h'\w'ESC Ps = 'u'\fB25\fP +(A) Not Blinking +.TP 20 +\h'\w'ESC Ps = 'u'\fB27\fP +(A) Positive Image +.TP 20 +.B "ESC [ \fPPn\fB g" + Tab Clear +.TP 20 +\h'\w'ESC 'u'Pn = None or \fB0\fP + Clear Tab at Current Position +.TP 20 +\h'\w'ESC Ps = 'u'\fB3\fP + Clear All Tabs +.TP 20 +.B "ESC [ \fPPn\fB ; \fPPn\fB r" +(V) Set Scrolling Region +.TP 20 +.B "ESC [ \fPPn\fB I" +(A) Horizontal Tab +.TP 20 +.B "ESC [ \fPPn\fB Z" +(A) Backward Tab +.TP 20 +.B "ESC [ \fPPn\fB L" +(A) Insert Line +.TP 20 +.B "ESC [ \fPPn\fB M" +(A) Delete Line +.TP 20 +.B "ESC [ \fPPn\fB @" +(A) Insert Character +.TP 20 +.B "ESC [ \fPPn\fB P" +(A) Delete Character +.TP 20 +.B "ESC [ \fPPs\fB ;\fP...\fB; \fPPs\fB h" + Set Mode +.TP 20 +.B "ESC [ \fPPs\fB ;\fP...\fB; \fPPs\fB l" + Reset Mode +.TP 20 +\h'\w'ESC 'u'Ps = \fB4\fP +(A) Insert Mode +.TP 20 +\h'\w'ESC Ps = 'u'\fB?3\fP +(V) Change Terminal Width +.TP 20 +\h'\w'ESC Ps = 'u'\fB?5\fP +(V) Visible Bell (\fIOn\fP followed by \fIOff\fP) +.TP 20 +\h'\w'ESC Ps = 'u'\fB?6\fP +(V) \fIOrigin\fP Mode +.TP 20 +\h'\w'ESC Ps = 'u'\fB?7\fP +(V) \fIWrap\fP Mode +.TP 20 +.B "ESC [ 5 i" +(A) Start relay to printer (ANSI Media Copy) +.TP 20 +.B "ESC [ 4 i" +(A) Stop relay to printer (ANSI Media Copy) +.fi +.SH FILES +.nf +.ta 2i +$SYSSCREENRC +.br +/local/etc/screenrc \fIscreen\fP initialization commands +.br +$ISCREENRC +.br +$SCREENRC +.br +$HOME/.iscreenrc +.br +$HOME/.screenrc Read in after /usr/local/etc/screenrc +.br +$ISCREENDIR/S- +.br +$SCREENDIR/S- +.br +/local/screens/S- Socket directories (default) +.br +/usr/tmp/screens/S- Alternate socket directories. +.br +/.termcap Written by the "termcap" output function +.br +/usr/tmp/screens/screen-exchange or +.br +/tmp/screen-exchange \fIscreen\fP `interprocess communication buffer' +.br +hardcopy.[0-9] Screen images created by the hardcopy function +.br +screenlog.[0-9] Output log files created by the log function +.br +/usr/lib/terminfo/?/* or +.br +/etc/termcap Terminal capability databases +.br +/etc/utmp Login records +.br +$LOCKPRG Program that locks a terminal. +.fi +.SH "SEE ALSO" +termcap(5), utmp(5), vi(1), captoinfo(1), tic(1) +.SH AUTHORS +Originally created by Oliver Laumann, this latest version was +produced by Wayne Davison, Juergen Weigert and Michael Schroeder. +.SH CONTRIBUTORS +Allan Ball, +Bart Schaefer, +Doug Siebert, +Howard Chu, +Jay Vassos-Libove, +John Kraft, +Larry Virden, +Marc Boucher, +Nathan Glasser, +Patrick Wolfe, +Rudolf Koenig. +Toerless Eckert, +.SH VERSION +This is version 3.2. It's roots are a merge of a custom version +2.3PR7 by Wayne Davison +and several enhancements to Oliver Laumann's version 2.0. Note that all versions +numbered 2.x are copyright by Oliver Laumann. + +.SH BUGS +`dm' (delete mode), `xn', and `xs' are not handled +correctly (they are ignored). +.PP +The \fIGR\fP set of ISO 2022 is not supported. +.PP +There is no keyboard input translation to VT100 sequences. +.PP +It is not possible to change the environment variable $TERMCAP when +reattaching under a different terminal type. +.PP +The support of terminfo based systems is very limited. Adding extra +capabilities to $TERMCAP may not have any effects. +.PP +.I Screen +does not make use of hardware tabs. +.PP +.I Screen +must be installed as set-uid with owner root in order to be able +to correctly change the owner of the tty device file for each +window. +Special permission may also be required to write the file \*Q/etc/utmp\*U. +.PP +Entries in \*Q/etc/utmp\*U are not removed when +.I screen +is killed with SIGKILL. +This will cause some programs (like "w" or "rwho") +to advertise that a user is logged on who really isn't.