diff options
Diffstat (limited to 'src/window.c')
-rw-r--r-- | src/window.c | 2322 |
1 files changed, 2322 insertions, 0 deletions
diff --git a/src/window.c b/src/window.c new file mode 100644 index 0000000..8509b7d --- /dev/null +++ b/src/window.c @@ -0,0 +1,2322 @@ +/* Copyright (c) 2010 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * 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 3, 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, see + * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <fcntl.h> +#ifndef sun +# include <sys/ioctl.h> +#endif + +#include "config.h" + +#include "screen.h" +#include "extern.h" +#include "logfile.h" /* logfopen() */ + +extern struct display *displays, *display; +extern struct win *windows, *fore, *console_window; +extern char *ShellArgs[]; +extern char *ShellProg; +extern char screenterm[]; +extern char *screenlogfile; +extern char HostName[]; +extern int TtyMode; +extern int SilenceWait; +extern int real_uid, real_gid, eff_uid, eff_gid; +extern char Termcap[]; +extern char **NewEnv; +extern int visual_bell, maxwin; +extern struct event logflushev; +extern int log_flush, logtstamp_after; +extern int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror; +extern struct layer *flayer; +extern int maxusercount; +extern int pty_preopen; +#ifdef ZMODEM +extern int zmodem_mode; +extern struct mchar mchar_blank; +extern char *zmodem_sendcmd; +extern char *zmodem_recvcmd; +#endif + +#if defined(TIOCSWINSZ) || defined(TIOCGWINSZ) +extern struct winsize glwz; +#endif + +#ifdef O_NOCTTY +extern int separate_sids; +#endif + +static void WinProcess __P((char **, int *)); +static void WinRedisplayLine __P((int, int, int, int)); +static void WinClearLine __P((int, int, int, int)); +static int WinRewrite __P((int, int, int, struct mchar *, int)); +static int WinResize __P((int, int)); +static void WinRestore __P((void)); +static int DoAutolf __P((char *, int *, int)); +static void ZombieProcess __P((char **, int *)); +static void win_readev_fn __P((struct event *, char *)); +static void win_writeev_fn __P((struct event *, char *)); +static int muchpending __P((struct win *, struct event *)); +#ifdef COPY_PASTE +static void paste_slowev_fn __P((struct event *, char *)); +#endif +#ifdef PSEUDOS +static void pseu_readev_fn __P((struct event *, char *)); +static void pseu_writeev_fn __P((struct event *, char *)); +#endif +static void win_silenceev_fn __P((struct event *, char *)); +static void win_destroyev_fn __P((struct event *, char *)); + +static int OpenDevice __P((char **, int, int *, char **)); +static int ForkWindow __P((struct win *, char **, char *)); +#ifdef ZMODEM +static void zmodem_found __P((struct win *, int, char *, int)); +static void zmodem_fin __P((char *, int, char *)); +static int zmodem_parse __P((struct win *, char *, int)); +#endif + + +struct win **wtab; /* window table */ + +int VerboseCreate = 0; /* XXX move this to user.h */ + +char DefaultShell[] = "/bin/sh"; +static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin"; + +/* keep this in sync with the structure definition in window.h */ +struct NewWindow nwin_undef = +{ + -1, /* StartAt */ + (char *)0, /* aka */ + (char **)0, /* args */ + (char *)0, /* dir */ + (char *)0, /* term */ + -1, /* aflag */ + -1, /* flowflag */ + -1, /* lflag */ + -1, /* histheight */ + -1, /* monitor */ + -1, /* wlock */ + -1, /* silence */ + -1, /* wrap */ + -1, /* logging */ + -1, /* slowpaste */ + -1, /* gr */ + -1, /* c1 */ + -1, /* bce */ + -1, /* encoding */ + (char *)0, /* hstatus */ + (char *)0 /* charset */ +}; + +struct NewWindow nwin_default = +{ + 0, /* StartAt */ + 0, /* aka */ + ShellArgs, /* args */ + 0, /* dir */ + screenterm, /* term */ + 0, /* aflag */ + 1*FLOW_NOW, /* flowflag */ + LOGINDEFAULT, /* lflag */ + DEFAULTHISTHEIGHT, /* histheight */ + MON_OFF, /* monitor */ + WLOCK_OFF, /* wlock */ + 0, /* silence */ + 1, /* wrap */ + 0, /* logging */ + 0, /* slowpaste */ + 0, /* gr */ + 1, /* c1 */ + 0, /* bce */ + 0, /* encoding */ + (char *)0, /* hstatus */ + (char *)0 /* charset */ +}; + +struct NewWindow nwin_options; + +static int const_IOSIZE = IOSIZE; +static int const_one = 1; + +void +nwin_compose(def, new, res) +struct NewWindow *def, *new, *res; +{ +#define COMPOSE(x) res->x = new->x != nwin_undef.x ? new->x : def->x + COMPOSE(StartAt); + COMPOSE(aka); + COMPOSE(args); + COMPOSE(dir); + COMPOSE(term); + COMPOSE(aflag); + COMPOSE(flowflag); + COMPOSE(lflag); + COMPOSE(histheight); + COMPOSE(monitor); + COMPOSE(wlock); + COMPOSE(silence); + COMPOSE(wrap); + COMPOSE(Lflag); + COMPOSE(slow); + COMPOSE(gr); + COMPOSE(c1); + COMPOSE(bce); + COMPOSE(encoding); + COMPOSE(hstatus); + COMPOSE(charset); +#undef COMPOSE +} + +/***************************************************************** + * + * The window layer functions + */ + +struct LayFuncs WinLf = +{ + WinProcess, + 0, + WinRedisplayLine, + WinClearLine, + WinRewrite, + WinResize, + WinRestore, + 0 +}; + +static int +DoAutolf(buf, lenp, fr) +char *buf; +int *lenp; +int fr; +{ + char *p; + int len = *lenp; + int trunc = 0; + + for (p = buf; len > 0; p++, len--) + { + if (*p != '\r') + continue; + if (fr-- <= 0) + { + trunc++; + len--; + } + if (len == 0) + break; + bcopy(p, p + 1, len++); + p[1] = '\n'; + } + *lenp = p - buf; + return trunc; +} + +static void +WinProcess(bufpp, lenp) +char **bufpp; +int *lenp; +{ + int l2 = 0, f, *ilen, l = *lenp, trunc; + char *ibuf; + + debug1("WinProcess: %d bytes\n", *lenp); + fore = (struct win *)flayer->l_data; + + if (fore->w_type == W_TYPE_GROUP) + { + *bufpp += *lenp; + *lenp = 0; + return; + } + if (fore->w_ptyfd < 0) /* zombie? */ + { + ZombieProcess(bufpp, lenp); + return; + } +#ifdef MULTIUSER + /* a pending writelock is this: + * fore->w_wlock == WLOCK_AUTO, fore->w_wlockuse = NULL + * The user who wants to use this window next, will get the lock, if he can. + */ + if (display && fore->w_wlock == WLOCK_AUTO && + !fore->w_wlockuser && !AclCheckPermWin(D_user, ACL_WRITE, fore)) + { + fore->w_wlockuser = D_user; + debug2("window %d: pending writelock grabbed by user %s\n", + fore->w_number, fore->w_wlockuser->u_name); + } + /* if w_wlock is set, only one user may write, else we check acls */ + if (display && ((fore->w_wlock == WLOCK_OFF) ? + AclCheckPermWin(D_user, ACL_WRITE, fore) : + (D_user != fore->w_wlockuser))) + { + debug2("window %d, user %s: ", fore->w_number, D_user->u_name); + debug2("writelock %d (wlockuser %s)\n", fore->w_wlock, + fore->w_wlockuser ? fore->w_wlockuser->u_name : "NULL"); + Msg(0, "write: permission denied (user %s)", D_user->u_name); + *bufpp += *lenp; + *lenp = 0; + return; + } +#endif /* MULTIUSER */ + +#ifdef BUILTIN_TELNET + if (fore->w_type == W_TYPE_TELNET && TelIsline(fore) && *bufpp != fore->w_telbuf) + { + TelProcessLine(bufpp, lenp); + return; + } +#endif + +#ifdef PSEUDOS + if (W_UWP(fore)) + { + /* we send the user input to our pseudowin */ + ibuf = fore->w_pwin->p_inbuf; ilen = &fore->w_pwin->p_inlen; + f = sizeof(fore->w_pwin->p_inbuf) - *ilen; + } + else +#endif /* PSEUDOS */ + { + /* we send the user input to the window */ + ibuf = fore->w_inbuf; ilen = &fore->w_inlen; + f = sizeof(fore->w_inbuf) - *ilen; + } + + if (l > f) + l = f; +#ifdef BUILTIN_TELNET + while (l > 0) +#else + if (l > 0) +#endif + { + l2 = l; + bcopy(*bufpp, ibuf + *ilen, l2); + if (fore->w_autolf && (trunc = DoAutolf(ibuf + *ilen, &l2, f - l2))) + l -= trunc; +#ifdef BUILTIN_TELNET + if (fore->w_type == W_TYPE_TELNET && (trunc = DoTelnet(ibuf + *ilen, &l2, f - l2))) + { + l -= trunc; + if (fore->w_autolf) + continue; /* need exact value */ + } +#endif + *ilen += l2; + *bufpp += l; + *lenp -= l; + return; + } +} + +static void +ZombieProcess(bufpp, lenp) +char **bufpp; +int *lenp; +{ + int l = *lenp; + char *buf = *bufpp, b1[10], b2[10]; + + debug1("ZombieProcess: %d bytes\n", *lenp); + fore = (struct win *)flayer->l_data; + + ASSERT(fore->w_ptyfd < 0); + *bufpp += *lenp; + *lenp = 0; + for (; l-- > 0; buf++) + { + if (*(unsigned char *)buf == ZombieKey_destroy) + { + debug1("Turning undead: %d\n", fore->w_number); + KillWindow(fore); + return; + } + if (*(unsigned char *)buf == ZombieKey_resurrect) + { + debug1("Resurrecting Zombie: %d\n", fore->w_number); + WriteString(fore, "\r\n", 2); + RemakeWindow(fore); + return; + } + } + b1[AddXChar(b1, ZombieKey_destroy)] = '\0'; + b2[AddXChar(b2, ZombieKey_resurrect)] = '\0'; + Msg(0, "Press %s to destroy or %s to resurrect window", b1, b2); +} + +static void +WinRedisplayLine(y, from, to, isblank) +int y, from, to, isblank; +{ + debug3("WinRedisplayLine %d %d %d\n", y, from, to); + if (y < 0) + return; + fore = (struct win *)flayer->l_data; + if (from == 0 && y > 0 && fore->w_mlines[y - 1].image[fore->w_width] == 0) + LCDisplayLineWrap(&fore->w_layer, &fore->w_mlines[y], y, from, to, isblank); + else + LCDisplayLine(&fore->w_layer, &fore->w_mlines[y], y, from, to, isblank); +} + +static int +WinRewrite(y, x1, x2, rend, doit) +int y, x1, x2, doit; +struct mchar *rend; +{ + register int cost, dx; + register unsigned char *p, *i; +#ifdef FONT + register unsigned char *f; +#endif +#ifdef COLOR + register unsigned char *c; +# ifdef COLORS256 + register unsigned char *cx; +# endif +#endif + + debug3("WinRewrite %d, %d-%d\n", y, x1, x2); + fore = (struct win *)flayer->l_data; + dx = x2 - x1 + 1; + if (doit) + { + i = fore->w_mlines[y].image + x1; + while (dx-- > 0) + PUTCHAR(*i++); + return 0; + } + p = fore->w_mlines[y].attr + x1; +#ifdef FONT + f = fore->w_mlines[y].font + x1; +# ifdef DW_CHARS + if (is_dw_font(rend->font)) + return EXPENSIVE; +# endif +# ifdef UTF8 + if (fore->w_encoding && fore->w_encoding != UTF8 && D_encoding == UTF8 && ContainsSpecialDeffont(fore->w_mlines + y, x1, x2, fore->w_encoding)) + return EXPENSIVE; +# endif +#endif +#ifdef COLOR + c = fore->w_mlines[y].color + x1; +# ifdef COLORS256 + cx = fore->w_mlines[y].colorx + x1; +# endif +#endif + + cost = dx = x2 - x1 + 1; + while(dx-- > 0) + { + if (*p++ != rend->attr) + return EXPENSIVE; +#ifdef FONT + if (*f++ != rend->font) + return EXPENSIVE; +#endif +#ifdef COLOR + if (*c++ != rend->color) + return EXPENSIVE; +# ifdef COLORS256 + if (*cx++ != rend->colorx) + return EXPENSIVE; +# endif +#endif + } + return cost; +} + +static void +WinClearLine(y, xs, xe, bce) +int y, xs, xe, bce; +{ + fore = (struct win *)flayer->l_data; + debug3("WinClearLine %d %d-%d\n", y, xs, xe); + LClearLine(flayer, y, xs, xe, bce, &fore->w_mlines[y]); +} + +static int +WinResize(wi, he) +int wi, he; +{ + fore = (struct win *)flayer->l_data; + ChangeWindowSize(fore, wi, he, fore->w_histheight); + return 0; +} + +static void +WinRestore() +{ + struct canvas *cv; + fore = (struct win *)flayer->l_data; + debug1("WinRestore: win %p\n", fore); + for (cv = flayer->l_cvlist; cv; cv = cv->c_next) + { + display = cv->c_display; + if (cv != D_forecv) + continue; + /* ChangeScrollRegion(fore->w_top, fore->w_bot); */ + KeypadMode(fore->w_keypad); + CursorkeysMode(fore->w_cursorkeys); + SetFlow(fore->w_flow & FLOW_NOW); + InsertMode(fore->w_insert); + ReverseVideo(fore->w_revvid); + CursorVisibility(fore->w_curinv ? -1 : fore->w_curvvis); + MouseMode(fore->w_mouse); + } +} + +/*****************************************************************/ + + +/* + * DoStartLog constructs a path for the "want to be logfile" in buf and + * attempts logfopen. + * + * returns 0 on success. + */ +int +DoStartLog(w, buf, bufsize) +struct win *w; +char *buf; +int bufsize; +{ + int n; + if (!w || !buf) + return -1; + + strncpy(buf, MakeWinMsg(screenlogfile, w, '%'), bufsize - 1); + buf[bufsize - 1] = 0; + + debug2("DoStartLog: win %d, file %s\n", w->w_number, buf); + + if (w->w_log != NULL) + logfclose(w->w_log); + + if ((w->w_log = logfopen(buf, islogfile(buf) ? NULL : secfopen(buf, "a"))) == NULL) + return -2; + if (!logflushev.queued) + { + n = log_flush ? log_flush : (logtstamp_after + 4) / 5; + if (n) + { + SetTimeout(&logflushev, n * 1000); + evenq(&logflushev); + } + } + return 0; +} + +/* + * Umask & wlock are set for the user of the display, + * The display d (if specified) switches to that window. + */ +int +MakeWindow(newwin) +struct NewWindow *newwin; +{ + register struct win **pp, *p; + register int n, i; + int f = -1; + struct NewWindow nwin; + int type, startat; + char *TtyName; +#ifdef MULTIUSER + extern struct acluser *users; +#endif + + if (!wtab) + { + if (!maxwin) + maxwin = MAXWIN; + wtab = calloc(maxwin, sizeof(struct win *)); + } + + debug1("NewWindow: StartAt %d\n", newwin->StartAt); + debug1("NewWindow: aka %s\n", newwin->aka?newwin->aka:"NULL"); + debug1("NewWindow: dir %s\n", newwin->dir?newwin->dir:"NULL"); + debug1("NewWindow: term %s\n", newwin->term?newwin->term:"NULL"); + + nwin_compose(&nwin_default, newwin, &nwin); + debug1("NWin: aka %s\n", nwin.aka ? nwin.aka : "NULL"); + debug1("NWin: wlock %d\n", nwin.wlock); + debug1("NWin: Lflag %d\n", nwin.Lflag); + + startat = nwin.StartAt < maxwin ? nwin.StartAt : 0; + pp = wtab + startat; + + do + { + if (*pp == 0) + break; + if (++pp == wtab + maxwin) + pp = wtab; + } + while (pp != wtab + startat); + if (*pp) + { + Msg(0, "No more windows."); + return -1; + } + +#if defined(USRLIMIT) && defined(UTMPOK) + /* + * Count current number of users, if logging windows in. + */ + if (nwin.lflag && CountUsers() >= USRLIMIT) + { + Msg(0, "User limit reached. Window will not be logged in."); + nwin.lflag = 0; + } +#endif + n = pp - wtab; + debug1("Makewin creating %d\n", n); + + if ((f = OpenDevice(nwin.args, nwin.lflag, &type, &TtyName)) < 0) + return -1; + if (type == W_TYPE_GROUP) + f = -1; + + if ((p = (struct win *)calloc(1, sizeof(struct win))) == 0) + { + close(f); + Msg(0, "%s", strnomem); + return -1; + } + +#ifdef UTMPOK + if (type != W_TYPE_PTY) + nwin.lflag = 0; +#endif + + p->w_type = type; + + /* save the command line so that zombies can be resurrected */ + for (i = 0; nwin.args[i] && i < MAXARGS - 1; i++) + p->w_cmdargs[i] = SaveStr(nwin.args[i]); + p->w_cmdargs[i] = 0; + if (nwin.dir) + p->w_dir = SaveStr(nwin.dir); + if (nwin.term) + p->w_term = SaveStr(nwin.term); + + p->w_number = n; + p->w_group = 0; + if (fore && fore->w_type == W_TYPE_GROUP) + p->w_group = fore; + else if (fore && fore->w_group) + p->w_group = fore->w_group; +#ifdef MULTIUSER + /* + * This is dangerous: without a display we use creators umask + * This is intended to be useful for detached startup. + * But is still better than default bits with a NULL user. + */ + if (NewWindowAcl(p, display ? D_user : users)) + { + free((char *)p); + close(f); + Msg(0, "%s", strnomem); + return -1; + } +#endif + p->w_layer.l_next = 0; + p->w_layer.l_bottom = &p->w_layer; + p->w_layer.l_layfn = &WinLf; + p->w_layer.l_data = (char *)p; + p->w_savelayer = &p->w_layer; + p->w_pdisplay = 0; + p->w_lastdisp = 0; + +#ifdef MULTIUSER + if (display && !AclCheckPermWin(D_user, ACL_WRITE, p)) + p->w_wlockuser = D_user; + p->w_wlock = nwin.wlock; +#endif + p->w_ptyfd = f; + p->w_aflag = nwin.aflag; + p->w_flow = nwin.flowflag | ((nwin.flowflag & FLOW_AUTOFLAG) ? (FLOW_AUTO|FLOW_NOW) : FLOW_AUTO); + if (!nwin.aka) + nwin.aka = Filename(nwin.args[0]); + strncpy(p->w_akabuf, nwin.aka, sizeof(p->w_akabuf) - 1); + if ((nwin.aka = rindex(p->w_akabuf, '|')) != NULL) + { + p->w_autoaka = 0; + *nwin.aka++ = 0; + p->w_title = nwin.aka; + p->w_akachange = nwin.aka + strlen(nwin.aka); + } + else + p->w_title = p->w_akachange = p->w_akabuf; + if (nwin.hstatus) + p->w_hstatus = SaveStr(nwin.hstatus); + p->w_monitor = nwin.monitor; +#ifdef MULTIUSER + if (p->w_monitor == MON_ON) + { + /* always tell all users */ + for (i = 0; i < maxusercount; i++) + ACLBYTE(p->w_mon_notify, i) |= ACLBIT(i); + } +#endif + /* + * defsilence by Lloyd Zusman (zusman_lloyd@jpmorgan.com) + */ + p->w_silence = nwin.silence; + p->w_silencewait = SilenceWait; +#ifdef MULTIUSER + if (p->w_silence == SILENCE_ON) + { + /* always tell all users */ + for (i = 0; i < maxusercount; i++) + ACLBYTE(p->w_lio_notify, i) |= ACLBIT(i); + } +#endif +#ifdef COPY_PASTE + p->w_slowpaste = nwin.slow; +#else + nwin.histheight = 0; +#endif + + p->w_norefresh = 0; + strncpy(p->w_tty, TtyName, MAXSTR - 1); + +#if 0 + /* XXX Fixme display resize */ + if (ChangeWindowSize(p, display ? D_defwidth : 80, + display ? D_defheight : 24, + nwin.histheight)) + { + FreeWindow(p); + return -1; + } +#else + if (ChangeWindowSize(p, display ? D_forecv->c_xe - D_forecv->c_xs + 1: 80, + display ? D_forecv->c_ye - D_forecv->c_ys + 1 : 24, + nwin.histheight)) + { + FreeWindow(p); + return -1; + } +#endif + + p->w_encoding = nwin.encoding; + ResetWindow(p); /* sets w_wrap, w_c1, w_gr, w_bce */ + +#ifdef FONT + if (nwin.charset) + SetCharsets(p, nwin.charset); +#endif + + if (VerboseCreate && type != W_TYPE_GROUP) + { + struct display *d = display; /* WriteString zaps display */ + + WriteString(p, ":screen (", 9); + WriteString(p, p->w_title, strlen(p->w_title)); + WriteString(p, "):", 2); + for (f = 0; p->w_cmdargs[f]; f++) + { + WriteString(p, " ", 1); + WriteString(p, p->w_cmdargs[f], strlen(p->w_cmdargs[f])); + } + WriteString(p, "\r\n", 2); + display = d; + } + + p->w_deadpid = 0; + p->w_pid = 0; +#ifdef PSEUDOS + p->w_pwin = 0; +#endif + +#ifdef BUILTIN_TELNET + if (type == W_TYPE_TELNET) + { + if (TelConnect(p)) + { + FreeWindow(p); + return -1; + } + } + else +#endif + if (type == W_TYPE_PTY) + { + p->w_pid = ForkWindow(p, nwin.args, TtyName); + if (p->w_pid < 0) + { + FreeWindow(p); + return -1; + } + } + + /* + * Place the new window at the head of the most-recently-used list. + */ + if (display && D_fore) + D_other = D_fore; + *pp = p; + p->w_next = windows; + windows = p; + + if (type == W_TYPE_GROUP) + { + SetForeWindow(p); + Activate(p->w_norefresh); + WindowChanged((struct win*)0, 'w'); + WindowChanged((struct win*)0, 'W'); + WindowChanged((struct win*)0, 0); + return n; + } + + p->w_lflag = nwin.lflag; +#ifdef UTMPOK + p->w_slot = (slot_t)-1; +# ifdef LOGOUTOK + debug1("MakeWindow will %slog in.\n", nwin.lflag?"":"not "); + if (nwin.lflag & 1) +# else /* LOGOUTOK */ + debug1("MakeWindow will log in, LOGOUTOK undefined in config.h%s.\n", + nwin.lflag?"":" (although lflag=0)"); +# endif /* LOGOUTOK */ + { + p->w_slot = (slot_t)0; + if (display || (p->w_lflag & 2)) + SetUtmp(p); + } +# ifdef CAREFULUTMP + CarefulUtmp(); /* If all 've been zombies, we've had no slot */ +# endif +#endif /* UTMPOK */ + + if (nwin.Lflag) + { + char buf[1024]; + DoStartLog(p, buf, sizeof(buf)); + } + + p->w_readev.fd = p->w_writeev.fd = p->w_ptyfd; + p->w_readev.type = EV_READ; + p->w_writeev.type = EV_WRITE; + p->w_readev.data = p->w_writeev.data = (char *)p; + p->w_readev.handler = win_readev_fn; + p->w_writeev.handler = win_writeev_fn; + p->w_writeev.condpos = &p->w_inlen; + evenq(&p->w_readev); + evenq(&p->w_writeev); +#ifdef COPY_PASTE + p->w_paster.pa_slowev.type = EV_TIMEOUT; + p->w_paster.pa_slowev.data = (char *)&p->w_paster; + p->w_paster.pa_slowev.handler = paste_slowev_fn; +#endif + p->w_silenceev.type = EV_TIMEOUT; + p->w_silenceev.data = (char *)p; + p->w_silenceev.handler = win_silenceev_fn; + if (p->w_silence > 0) + { + debug("New window has silence enabled.\n"); + SetTimeout(&p->w_silenceev, p->w_silencewait * 1000); + evenq(&p->w_silenceev); + } + p->w_destroyev.type = EV_TIMEOUT; + p->w_destroyev.data = 0; + p->w_destroyev.handler = win_destroyev_fn; + + SetForeWindow(p); + Activate(p->w_norefresh); + WindowChanged((struct win*)0, 'w'); + WindowChanged((struct win*)0, 'W'); + WindowChanged((struct win*)0, 0); + return n; +} + +/* + * Resurrect a window from Zombie state. + * The command vector is therefore stored in the window structure. + * Note: The terminaltype defaults to screenterm again, the current + * working directory is lost. + */ +int +RemakeWindow(p) +struct win *p; +{ + char *TtyName; + int lflag, f; + + lflag = nwin_default.lflag; + if ((f = OpenDevice(p->w_cmdargs, lflag, &p->w_type, &TtyName)) < 0) + return -1; + + evdeq(&p->w_destroyev); /* no re-destroy of resurrected zombie */ + + strncpy(p->w_tty, *TtyName ? TtyName : p->w_title, MAXSTR - 1); + p->w_ptyfd = f; + p->w_readev.fd = f; + p->w_writeev.fd = f; + evenq(&p->w_readev); + evenq(&p->w_writeev); + + if (VerboseCreate) + { + struct display *d = display; /* WriteString zaps display */ + + WriteString(p, ":screen (", 9); + WriteString(p, p->w_title, strlen(p->w_title)); + WriteString(p, "):", 2); + for (f = 0; p->w_cmdargs[f]; f++) + { + WriteString(p, " ", 1); + WriteString(p, p->w_cmdargs[f], strlen(p->w_cmdargs[f])); + } + WriteString(p, "\r\n", 2); + display = d; + } + + p->w_deadpid = 0; + p->w_pid = 0; +#ifdef BUILTIN_TELNET + if (p->w_type == W_TYPE_TELNET) + { + if (TelConnect(p)) + return -1; + } + else +#endif + if (p->w_type == W_TYPE_PTY) + { + p->w_pid = ForkWindow(p, p->w_cmdargs, TtyName); + if (p->w_pid < 0) + return -1; + } + +#ifdef UTMPOK + if (p->w_slot == (slot_t)0 && (display || (p->w_lflag & 2))) + SetUtmp(p); +# ifdef CAREFULUTMP + CarefulUtmp(); /* If all 've been zombies, we've had no slot */ +# endif +#endif + WindowChanged(p, 'f'); + return p->w_number; +} + +void +CloseDevice(wp) +struct win *wp; +{ + if (wp->w_ptyfd < 0) + return; + if (wp->w_type == W_TYPE_PTY) + { + /* pty 4 SALE */ + (void)chmod(wp->w_tty, 0666); + (void)chown(wp->w_tty, 0, 0); + } + close(wp->w_ptyfd); + wp->w_ptyfd = -1; + wp->w_tty[0] = 0; + evdeq(&wp->w_readev); + evdeq(&wp->w_writeev); +#ifdef BUILTIN_TELNET + evdeq(&wp->w_telconnev); +#endif + wp->w_readev.fd = wp->w_writeev.fd = -1; +} + +void +FreeWindow(wp) +struct win *wp; +{ + struct display *d; + int i; + struct canvas *cv, *ncv; + struct layer *l; + + debug1("FreeWindow %d\n", wp ? wp->w_number: -1); +#ifdef PSEUDOS + if (wp->w_pwin) + FreePseudowin(wp); +#endif +#ifdef UTMPOK + RemoveUtmp(wp); +#endif + CloseDevice(wp); + + if (wp == console_window) + { + TtyGrabConsole(-1, -1, "free"); + console_window = 0; + } + if (wp->w_log != NULL) + logfclose(wp->w_log); + ChangeWindowSize(wp, 0, 0, 0); + + if (wp->w_type == W_TYPE_GROUP) + { + struct win *win; + for (win = windows; win; win = win->w_next) + if (win->w_group == wp) + win->w_group = wp->w_group; + } + + if (wp->w_hstatus) + free(wp->w_hstatus); + for (i = 0; wp->w_cmdargs[i]; i++) + free(wp->w_cmdargs[i]); + if (wp->w_dir) + free(wp->w_dir); + if (wp->w_term) + free(wp->w_term); + for (d = displays; d; d = d->d_next) + { + if (d->d_other == wp) + d->d_other = d->d_fore && d->d_fore->w_next != wp ? d->d_fore->w_next : wp->w_next; + if (d->d_fore == wp) + d->d_fore = NULL; + for (cv = d->d_cvlist; cv; cv = cv->c_next) + { + for (l = cv->c_layer; l; l = l->l_next) + if (l->l_layfn == &WinLf) + break; + if (!l) + continue; + if ((struct win *)l->l_data != wp) + continue; + if (cv->c_layer == wp->w_savelayer) + wp->w_savelayer = 0; + KillLayerChain(cv->c_layer); + } + } + if (wp->w_savelayer) + KillLayerChain(wp->w_savelayer); + for (cv = wp->w_layer.l_cvlist; cv; cv = ncv) + { + ncv = cv->c_lnext; + cv->c_layer = &cv->c_blank; + cv->c_blank.l_cvlist = cv; + cv->c_lnext = 0; + cv->c_xoff = cv->c_xs; + cv->c_yoff = cv->c_ys; + RethinkViewportOffsets(cv); + } + wp->w_layer.l_cvlist = 0; + if (flayer == &wp->w_layer) + flayer = 0; + LayerCleanupMemory(&wp->w_layer); + +#ifdef MULTIUSER + FreeWindowAcl(wp); +#endif /* MULTIUSER */ + evdeq(&wp->w_readev); /* just in case */ + evdeq(&wp->w_writeev); /* just in case */ + evdeq(&wp->w_silenceev); + evdeq(&wp->w_destroyev); +#ifdef COPY_PASTE + FreePaster(&wp->w_paster); +#endif + free((char *)wp); +} + +static int +OpenDevice(args, lflag, typep, namep) +char **args; +int lflag; +int *typep; +char **namep; +{ + char *arg = args[0]; + struct stat st; + int f; + + if (!arg) + return -1; + if (strcmp(arg, "//group") == 0) + { + *typep = W_TYPE_GROUP; + *namep = "telnet"; + return 0; + } +#ifdef BUILTIN_TELNET + if (strcmp(arg, "//telnet") == 0) + { + f = TelOpen(args + 1); + lflag = 0; + *typep = W_TYPE_TELNET; + *namep = "telnet"; + } + else +#endif + if (strncmp(arg, "//", 2) == 0) + { + Msg(0, "Invalid argument '%s'", arg); + return -1; + } + else if ((stat(arg, &st)) == 0 && S_ISCHR(st.st_mode)) + { + if (access(arg, R_OK | W_OK) == -1) + { + Msg(errno, "Cannot access line '%s' for R/W", arg); + return -1; + } + debug("OpenDevice: OpenTTY\n"); + if ((f = OpenTTY(arg, args[1])) < 0) + return -1; + lflag = 0; + *typep = W_TYPE_PLAIN; + *namep = arg; + } + else + { + *typep = W_TYPE_PTY; + f = OpenPTY(namep); + if (f == -1) + { + Msg(0, "No more PTYs."); + return -1; + } +#ifdef TIOCPKT + { + int flag = 1; + if (ioctl(f, TIOCPKT, (char *)&flag)) + { + Msg(errno, "TIOCPKT ioctl"); + close(f); + return -1; + } + } +#endif /* TIOCPKT */ + } + debug1("fcntl(%d, F_SETFL, FNBLOCK)\n", f); + (void) fcntl(f, F_SETFL, FNBLOCK); +#ifdef linux + /* + * Tenebreux (zeus@ns.acadiacom.net) has Linux 1.3.70 where select + * gets confused in the following condition: + * Open a pty-master side, request a flush on it, then set packet + * mode and call select(). Select will return a possible read, where + * the one byte response to the flush can be found. Select will + * thereafter return a possible read, which yields I/O error. + * + * If we request another flush *after* switching into packet mode, + * this I/O error does not occur. We receive a single response byte + * although we send two flush requests now. + * + * Maybe we should not flush at all. + * + * 10.5.96 jw. + */ + if (*typep == W_TYPE_PTY || *typep == W_TYPE_PLAIN) + tcflush(f, TCIOFLUSH); +#endif + + if (*typep != W_TYPE_PTY) + return f; + +#ifndef PTYROFS +#ifdef PTYGROUP + if (chown(*namep, real_uid, PTYGROUP) && !eff_uid) +#else + if (chown(*namep, real_uid, real_gid) && !eff_uid) +#endif + { + Msg(errno, "chown tty"); + close(f); + return -1; + } +#ifdef UTMPOK + if (chmod(*namep, lflag ? TtyMode : (TtyMode & ~022)) && !eff_uid) +#else + if (chmod(*namep, TtyMode) && !eff_uid) +#endif + { + Msg(errno, "chmod tty"); + close(f); + return -1; + } +#endif + return f; +} + +/* + * Fields w_width, w_height, aflag, number (and w_tty) + * are read from struct win *win. No fields written. + * If pwin is nonzero, filedescriptors are distributed + * between win->w_tty and open(ttyn) + * + */ +static int +ForkWindow(win, args, ttyn) +struct win *win; +char **args, *ttyn; +{ + int pid; + char tebuf[25]; + char ebuf[20]; + char shellbuf[7 + MAXPATHLEN]; + char *proc; +#ifndef TIOCSWINSZ + char libuf[20], cobuf[20]; +#endif + int newfd; + int w = win->w_width; + int h = win->w_height; +#ifdef PSEUDOS + int i, pat, wfdused; + struct pseudowin *pwin = win->w_pwin; +#endif + int slave = -1; + +#ifdef O_NOCTTY + if (pty_preopen) + { + debug("pre-opening slave...\n"); + if ((slave = open(ttyn, O_RDWR|O_NOCTTY)) == -1) + { + Msg(errno, "ttyn"); + return -1; + } + } +#endif + debug("forking...\n"); + proc = *args; + if (proc == 0) + { + args = ShellArgs; + proc = *args; + } + fflush(stdout); + fflush(stderr); + switch (pid = fork()) + { + case -1: + Msg(errno, "fork"); + break; + case 0: + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); +#ifdef BSDJOBS + signal(SIGTTIN, SIG_DFL); + signal(SIGTTOU, SIG_DFL); +#endif +#ifdef SIGPIPE + signal(SIGPIPE, SIG_DFL); +#endif +#ifdef SIGXFSZ + signal(SIGXFSZ, SIG_DFL); +#endif + + displays = 0; /* beware of Panic() */ + if (setgid(real_gid) || setuid(real_uid)) + Panic(errno, "Setuid/gid"); + eff_uid = real_uid; + eff_gid = real_gid; +#ifdef PSEUDOS + if (!pwin) /* ignore directory if pseudo */ +#endif + if (win->w_dir && *win->w_dir && chdir(win->w_dir)) + Panic(errno, "Cannot chdir to %s", win->w_dir); + + if (display) + { + brktty(D_userfd); + freetty(); + } + else + brktty(-1); +#ifdef DEBUG + if (dfp && dfp != stderr) + fclose(dfp); +#endif + if (slave != -1) + { + close(0); + dup(slave); + close(slave); + closeallfiles(win->w_ptyfd); + slave = dup(0); + } + else + closeallfiles(win->w_ptyfd); +#ifdef DEBUG + if (dfp) /* do not produce child debug, when debug is "off" */ + { + char buf[256]; + + sprintf(buf, "%s/screen.child", DEBUGDIR); + if ((dfp = fopen(buf, "a")) == 0) + dfp = stderr; + else + (void) chmod(buf, 0666); + } + debug1("=== ForkWindow: pid %d\n", (int)getpid()); +#endif + /* Close the three /dev/null descriptors */ + close(0); + close(1); + close(2); + newfd = -1; + /* + * distribute filedescriptors between the ttys + */ +#ifdef PSEUDOS + pat = pwin ? pwin->p_fdpat : + ((F_PFRONT<<(F_PSHIFT*2)) | (F_PFRONT<<F_PSHIFT) | F_PFRONT); + debug1("Using window pattern 0x%x\n", pat); + wfdused = 0; + for(i = 0; i < 3; i++) + { + if (pat & F_PFRONT << F_PSHIFT * i) + { + if (newfd < 0) + { +# ifdef O_NOCTTY + if (separate_sids) + newfd = open(ttyn, O_RDWR); + else + newfd = open(ttyn, O_RDWR|O_NOCTTY); +# else + newfd = open(ttyn, O_RDWR); +# endif + if (newfd < 0) + Panic(errno, "Cannot open %s", ttyn); + } + else + dup(newfd); + } + else + { + dup(win->w_ptyfd); + wfdused = 1; + } + } + if (wfdused) + { + /* + * the pseudo window process should not be surprised with a + * nonblocking filedescriptor. Poor Backend! + */ + debug1("Clearing NBLOCK on window-fd(%d)\n", win->w_ptyfd); + if (fcntl(win->w_ptyfd, F_SETFL, 0)) + Msg(errno, "Warning: clear NBLOCK fcntl failed"); + } +#else /* PSEUDOS */ +# ifdef O_NOCTTY + if (separate_sids) + newfd = open(ttyn, O_RDWR); + else + newfd = open(ttyn, O_RDWR|O_NOCTTY); +# else + newfd = open(ttyn, O_RDWR); +# endif + if (newfd != 0) + Panic(errno, "Cannot open %s", ttyn); + dup(0); + dup(0); +#endif /* PSEUDOS */ + close(win->w_ptyfd); + if (slave != -1) + close(slave); + if (newfd >= 0) + { + struct mode fakemode, *modep; + InitPTY(newfd); + if (fgtty(newfd)) + Msg(errno, "fgtty"); + if (display) + { + debug("ForkWindow: using display tty mode for new child.\n"); + modep = &D_OldMode; + } + else + { + debug("No display - creating tty setting\n"); + modep = &fakemode; + InitTTY(modep, 0); +#ifdef DEBUG + DebugTTY(modep); +#endif + } + /* We only want echo if the users input goes to the pseudo + * and the pseudo's stdout is not send to the window. + */ +#ifdef PSEUDOS + if (pwin && (!(pat & F_UWP) || (pat & F_PBACK << F_PSHIFT))) + { + debug1("clearing echo on pseudywin fd (pat %x)\n", pat); +# if defined(POSIX) || defined(TERMIO) + modep->tio.c_lflag &= ~ECHO; + modep->tio.c_iflag &= ~ICRNL; +# else + modep->m_ttyb.sg_flags &= ~ECHO; +# endif + } +#endif + SetTTY(newfd, modep); +#ifdef TIOCSWINSZ + glwz.ws_col = w; + glwz.ws_row = h; + (void) ioctl(newfd, TIOCSWINSZ, (char *)&glwz); +#endif + /* Always turn off nonblocking mode */ + (void)fcntl(newfd, F_SETFL, 0); + } +#ifndef TIOCSWINSZ + sprintf(libuf, "LINES=%d", h); + sprintf(cobuf, "COLUMNS=%d", w); + NewEnv[5] = libuf; + NewEnv[6] = cobuf; +#endif +#ifdef MAPKEYS + NewEnv[2] = MakeTermcap(display == 0 || win->w_aflag); +#else + if (win->w_aflag) + NewEnv[2] = MakeTermcap(1); + else + NewEnv[2] = Termcap; +#endif + strcpy(shellbuf, "SHELL="); + strncpy(shellbuf + 6, ShellProg + (*ShellProg == '-'), sizeof(shellbuf) - 7); + shellbuf[sizeof(shellbuf) - 1] = 0; + NewEnv[4] = shellbuf; + debug1("ForkWindow: NewEnv[4] = '%s'\n", shellbuf); + if (win->w_term && *win->w_term && strcmp(screenterm, win->w_term) && + (strlen(win->w_term) < 20)) + { + char *s1, *s2, tl; + + sprintf(tebuf, "TERM=%s", win->w_term); + debug2("Makewindow %d with %s\n", win->w_number, tebuf); + tl = strlen(win->w_term); + NewEnv[1] = tebuf; + if ((s1 = index(NewEnv[2], '|'))) + { + if ((s2 = index(++s1, '|'))) + { + if (strlen(NewEnv[2]) - (s2 - s1) + tl < 1024) + { + bcopy(s2, s1 + tl, strlen(s2) + 1); + bcopy(win->w_term, s1, tl); + } + } + } + } + sprintf(ebuf, "WINDOW=%d", win->w_number); + NewEnv[3] = ebuf; + + if (*proc == '-') + proc++; + if (!*proc) + proc = DefaultShell; + debug1("calling execvpe %s\n", proc); + execvpe(proc, args, NewEnv); + debug1("exec error: %d\n", errno); + Panic(errno, "Cannot exec '%s'", proc); + default: + break; + } + if (slave != -1) + close(slave); + return pid; +} + +#ifndef HAVE_EXECVPE +void +execvpe(prog, args, env) +char *prog, **args, **env; +{ + register char *path = NULL, *p; + char buf[1024]; + char *shargs[MAXARGS + 1]; + register int i, eaccess = 0; + + if (rindex(prog, '/')) + path = ""; + if (!path && !(path = getenv("PATH"))) + path = DefaultPath; + do + { + for (p = buf; *path && *path != ':'; path++) + if (p - buf < (int)sizeof(buf) - 2) + *p++ = *path; + if (p > buf) + *p++ = '/'; + if (p - buf + strlen(prog) >= sizeof(buf) - 1) + continue; + strcpy(p, prog); + execve(buf, args, env); + switch (errno) + { + case ENOEXEC: + shargs[0] = DefaultShell; + shargs[1] = buf; + for (i = 1; (shargs[i + 1] = args[i]) != NULL; ++i) + ; + execve(DefaultShell, shargs, env); + return; + case EACCES: + eaccess = 1; + break; + case ENOMEM: + case E2BIG: + case ETXTBSY: + return; + } + } while (*path++); + if (eaccess) + errno = EACCES; +} +#endif + +#ifdef PSEUDOS + +int +winexec(av) +char **av; +{ + char **pp; + char *p, *s, *t; + int i, r = 0, l = 0; + struct win *w; + extern struct display *display; + extern struct win *windows; + struct pseudowin *pwin; + int type; + + if ((w = display ? fore : windows) == NULL) + return -1; + if (!*av || w->w_pwin) + { + Msg(0, "Filter running: %s", w->w_pwin ? w->w_pwin->p_cmd : "(none)"); + return -1; + } + if (w->w_ptyfd < 0) + { + Msg(0, "You feel dead inside."); + return -1; + } + if (!(pwin = (struct pseudowin *)calloc(1, sizeof(struct pseudowin)))) + { + Msg(0, "%s", strnomem); + return -1; + } + + /* allow ^a:!!./ttytest as a short form for ^a:exec !.. ./ttytest */ + for (s = *av; *s == ' '; s++) + ; + for (p = s; *p == ':' || *p == '.' || *p == '!'; p++) + ; + if (*p != '|') + while (*p && p > s && p[-1] == '.') + p--; + if (*p == '|') + { + l = F_UWP; + p++; + } + if (*p) + av[0] = p; + else + av++; + + t = pwin->p_cmd; + for (i = 0; i < 3; i++) + { + *t = (s < p) ? *s++ : '.'; + switch (*t++) + { + case '.': + case '|': + l |= F_PFRONT << (i * F_PSHIFT); + break; + case '!': + l |= F_PBACK << (i * F_PSHIFT); + break; + case ':': + l |= F_PBOTH << (i * F_PSHIFT); + break; + } + } + + if (l & F_UWP) + { + *t++ = '|'; + if ((l & F_PMASK) == F_PFRONT) + { + *pwin->p_cmd = '!'; + l ^= F_PFRONT | F_PBACK; + } + } + if (!(l & F_PBACK)) + l |= F_UWP; + *t++ = ' '; + pwin->p_fdpat = l; + debug1("winexec: '%#x'\n", pwin->p_fdpat); + + l = MAXSTR - 4; + for (pp = av; *pp; pp++) + { + p = *pp; + while (*p && l-- > 0) + *t++ = *p++; + if (l <= 0) + break; + *t++ = ' '; + } + *--t = '\0'; + debug1("%s\n", pwin->p_cmd); + + if ((pwin->p_ptyfd = OpenDevice(av, 0, &type, &t)) < 0) + { + free((char *)pwin); + return -1; + } + strncpy(pwin->p_tty, t, MAXSTR - 1); + w->w_pwin = pwin; + if (type != W_TYPE_PTY) + { + FreePseudowin(w); + Msg(0, "Cannot only use commands as pseudo win."); + return -1; + } + if (!(pwin->p_fdpat & F_PFRONT)) + evdeq(&w->w_readev); +#ifdef TIOCPKT + { + int flag = 0; + + if (ioctl(pwin->p_ptyfd, TIOCPKT, (char *)&flag)) + { + Msg(errno, "TIOCPKT pwin ioctl"); + FreePseudowin(w); + return -1; + } + if (w->w_type == W_TYPE_PTY && !(pwin->p_fdpat & F_PFRONT)) + { + if (ioctl(w->w_ptyfd, TIOCPKT, (char *)&flag)) + { + Msg(errno, "TIOCPKT win ioctl"); + FreePseudowin(w); + return -1; + } + } + } +#endif /* TIOCPKT */ + + pwin->p_readev.fd = pwin->p_writeev.fd = pwin->p_ptyfd; + pwin->p_readev.type = EV_READ; + pwin->p_writeev.type = EV_WRITE; + pwin->p_readev.data = pwin->p_writeev.data = (char *)w; + pwin->p_readev.handler = pseu_readev_fn; + pwin->p_writeev.handler = pseu_writeev_fn; + pwin->p_writeev.condpos = &pwin->p_inlen; + if (pwin->p_fdpat & (F_PFRONT << F_PSHIFT * 2 | F_PFRONT << F_PSHIFT)) + evenq(&pwin->p_readev); + evenq(&pwin->p_writeev); + r = pwin->p_pid = ForkWindow(w, av, t); + if (r < 0) + FreePseudowin(w); + return r; +} + +void +FreePseudowin(w) +struct win *w; +{ + struct pseudowin *pwin = w->w_pwin; + + ASSERT(pwin); + if (fcntl(w->w_ptyfd, F_SETFL, FNBLOCK)) + Msg(errno, "Warning: FreePseudowin: NBLOCK fcntl failed"); +#ifdef TIOCPKT + if (w->w_type == W_TYPE_PTY && !(pwin->p_fdpat & F_PFRONT)) + { + int flag = 1; + if (ioctl(w->w_ptyfd, TIOCPKT, (char *)&flag)) + Msg(errno, "Warning: FreePseudowin: TIOCPKT win ioctl"); + } +#endif + /* should be able to use CloseDevice() here */ + (void)chmod(pwin->p_tty, 0666); + (void)chown(pwin->p_tty, 0, 0); + if (pwin->p_ptyfd >= 0) + close(pwin->p_ptyfd); + evdeq(&pwin->p_readev); + evdeq(&pwin->p_writeev); + if (w->w_readev.condneg == &pwin->p_inlen) + w->w_readev.condpos = w->w_readev.condneg = 0; + evenq(&w->w_readev); + free((char *)pwin); + w->w_pwin = NULL; +} + +#endif /* PSEUDOS */ + + +#ifdef MULTIUSER +/* + * returns 0, if the lock really has been released + */ +int +ReleaseAutoWritelock(dis, w) +struct display *dis; +struct win *w; +{ + debug2("ReleaseAutoWritelock: user %s, window %d\n", + dis->d_user->u_name, w->w_number); + + /* release auto writelock when user has no other display here */ + if (w->w_wlock == WLOCK_AUTO && w->w_wlockuser == dis->d_user) + { + struct display *d; + + for (d = displays; d; d = d->d_next) + if (( d != dis) && (d->d_fore == w) && (d->d_user == dis->d_user)) + break; + debug3("%s %s autolock on win %d\n", + dis->d_user->u_name, d ? "keeps" : "releases", w->w_number); + if (!d) + { + w->w_wlockuser = NULL; + return 0; + } + } + return 1; +} + +/* + * returns 0, if the lock really could be obtained + */ +int +ObtainAutoWritelock(d, w) +struct display *d; +struct win *w; +{ + if ((w->w_wlock == WLOCK_AUTO) && + !AclCheckPermWin(d->d_user, ACL_WRITE, w) && + !w->w_wlockuser) + { + debug2("%s obtained auto writelock for exported window %d\n", + d->d_user->u_name, w->w_number); + w->w_wlockuser = d->d_user; + return 0; + } + return 1; +} + +#endif /* MULTIUSER */ + + + +/********************************************************************/ + +#ifdef COPY_PASTE +static void +paste_slowev_fn(ev, data) +struct event *ev; +char *data; +{ + struct paster *pa = (struct paster *)data; + struct win *p; + + int l = 1; + flayer = pa->pa_pastelayer; + if (!flayer) + pa->pa_pastelen = 0; + if (!pa->pa_pastelen) + return; + p = Layer2Window(flayer); + DoProcess(p, &pa->pa_pasteptr, &l, pa); + pa->pa_pastelen -= 1 - l; + if (pa->pa_pastelen > 0) + { + SetTimeout(&pa->pa_slowev, p->w_slowpaste); + evenq(&pa->pa_slowev); + } +} +#endif + + +static int +muchpending(p, ev) +struct win *p; +struct event *ev; +{ + struct canvas *cv; + for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext) + { + display = cv->c_display; + if (D_status == STATUS_ON_WIN && !D_status_bell) + { + /* wait 'til status is gone */ + debug("BLOCKING because of status\n"); + ev->condpos = &const_one; + ev->condneg = &D_status; + return 1; + } + debug2("muchpending %s %d: ", D_usertty, D_blocked); + debug3("%d %d %d\n", D_obufp - D_obuf, D_obufmax, D_blocked_fuzz); + if (D_blocked) + continue; + if (D_obufp - D_obuf > D_obufmax + D_blocked_fuzz) + { + if (D_nonblock == 0) + { + debug1("obuf is full, stopping output to display %s\n", D_usertty); + D_blocked = 1; + continue; + } + debug("BLOCKING because of full obuf\n"); + ev->condpos = &D_obuffree; + ev->condneg = &D_obuflenmax; + if (D_nonblock > 0 && !D_blockedev.queued) + { + debug1("created timeout of %g secs\n", D_nonblock/1000.); + SetTimeout(&D_blockedev, D_nonblock); + evenq(&D_blockedev); + } + return 1; + } + } + return 0; +} + +static void +win_readev_fn(ev, data) +struct event *ev; +char *data; +{ + struct win *p = (struct win *)data; + char buf[IOSIZE], *bp; + int size, len; +#ifdef PSEUDOS + int wtop; +#endif + + bp = buf; + size = IOSIZE; + +#ifdef PSEUDOS + wtop = p->w_pwin && W_WTOP(p); + if (wtop) + { + ASSERT(sizeof(p->w_pwin->p_inbuf) == IOSIZE); + size = IOSIZE - p->w_pwin->p_inlen; + if (size <= 0) + { + ev->condpos = &const_IOSIZE; + ev->condneg = &p->w_pwin->p_inlen; + return; + } + } +#endif + if (p->w_layer.l_cvlist && muchpending(p, ev)) + return; +#ifdef ZMODEM + if (!p->w_zdisplay) +#endif + if (p->w_blocked) + { + ev->condpos = &const_one; + ev->condneg = &p->w_blocked; + return; + } + if (ev->condpos) + ev->condpos = ev->condneg = 0; + + if ((len = p->w_outlen)) + { + p->w_outlen = 0; + WriteString(p, p->w_outbuf, len); + return; + } + + debug1("going to read from window fd %d\n", ev->fd); + if ((len = read(ev->fd, buf, size)) < 0) + { + if (errno == EINTR || errno == EAGAIN) + return; +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + if (errno == EWOULDBLOCK) + return; +#endif + debug2("Window %d: read error (errno %d) - killing window\n", p->w_number, errno); +#ifdef BSDWAIT + WindowDied(p, (union wait)0, 0); +#else + WindowDied(p, 0, 0); +#endif + return; + } + if (len == 0) + { + debug1("Window %d: EOF - killing window\n", p->w_number); +#ifdef BSDWAIT + WindowDied(p, (union wait)0, 0); +#else + WindowDied(p, 0, 0); +#endif + return; + } + debug1(" -> %d bytes\n", len); +#ifdef TIOCPKT + if (p->w_type == W_TYPE_PTY) + { + if (buf[0]) + { + debug1("PAKET %x\n", buf[0]); + if (buf[0] & TIOCPKT_NOSTOP) + WNewAutoFlow(p, 0); + if (buf[0] & TIOCPKT_DOSTOP) + WNewAutoFlow(p, 1); + } + bp++; + len--; + } +#endif +#ifdef BUILTIN_TELNET + if (p->w_type == W_TYPE_TELNET) + len = TelIn(p, bp, len, buf + sizeof(buf) - (bp + len)); +#endif + if (len == 0) + return; +#ifdef ZMODEM + if (zmodem_mode && zmodem_parse(p, bp, len)) + return; +#endif +#ifdef PSEUDOS + if (wtop) + { + debug("sending input to pwin\n"); + bcopy(bp, p->w_pwin->p_inbuf + p->w_pwin->p_inlen, len); + p->w_pwin->p_inlen += len; + } +#endif + + LayPause(&p->w_layer, 1); + WriteString(p, bp, len); + LayPause(&p->w_layer, 0); + + return; +} + + +static void +win_writeev_fn(ev, data) +struct event *ev; +char *data; +{ + struct win *p = (struct win *)data; + int len; + if (p->w_inlen) + { + debug2("writing %d bytes to win %d\n", p->w_inlen, p->w_number); + if ((len = write(ev->fd, p->w_inbuf, p->w_inlen)) <= 0) + len = p->w_inlen; /* dead window */ + if ((p->w_inlen -= len)) + bcopy(p->w_inbuf + len, p->w_inbuf, p->w_inlen); + } +#ifdef COPY_PASTE + if (p->w_paster.pa_pastelen && !p->w_slowpaste) + { + struct paster *pa = &p->w_paster; + flayer = pa->pa_pastelayer; + if (flayer) + DoProcess(p, &pa->pa_pasteptr, &pa->pa_pastelen, pa); + } +#endif + return; +} + + + +#ifdef PSEUDOS + +static void +pseu_readev_fn(ev, data) +struct event *ev; +char *data; +{ + struct win *p = (struct win *)data; + char buf[IOSIZE]; + int size, ptow, len; + + size = IOSIZE; + + ptow = W_PTOW(p); + if (ptow) + { + ASSERT(sizeof(p->w_inbuf) == IOSIZE); + size = IOSIZE - p->w_inlen; + if (size <= 0) + { + ev->condpos = &const_IOSIZE; + ev->condneg = &p->w_inlen; + return; + } + } + if (p->w_layer.l_cvlist && muchpending(p, ev)) + return; + if (p->w_blocked) + { + ev->condpos = &const_one; + ev->condneg = &p->w_blocked; + return; + } + if (ev->condpos) + ev->condpos = ev->condneg = 0; + + if ((len = p->w_outlen)) + { + p->w_outlen = 0; + WriteString(p, p->w_outbuf, len); + return; + } + + if ((len = read(ev->fd, buf, size)) <= 0) + { + if (errno == EINTR || errno == EAGAIN) + return; +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + if (errno == EWOULDBLOCK) + return; +#endif + debug2("Window %d: pseudowin read error (errno %d) -- removing pseudowin\n", p->w_number, len ? errno : 0); + FreePseudowin(p); + return; + } + /* no packet mode on pseudos! */ + if (ptow) + { + bcopy(buf, p->w_inbuf + p->w_inlen, len); + p->w_inlen += len; + } + WriteString(p, buf, len); + return; +} + +static void +pseu_writeev_fn(ev, data) +struct event *ev; +char *data; +{ + struct win *p = (struct win *)data; + struct pseudowin *pw = p->w_pwin; + int len; + + ASSERT(pw); + if (pw->p_inlen == 0) + return; + if ((len = write(ev->fd, pw->p_inbuf, pw->p_inlen)) <= 0) + len = pw->p_inlen; /* dead pseudo */ + if ((p->w_pwin->p_inlen -= len)) + bcopy(p->w_pwin->p_inbuf + len, p->w_pwin->p_inbuf, p->w_pwin->p_inlen); +} + + +#endif /* PSEUDOS */ + +static void +win_silenceev_fn(ev, data) +struct event *ev; +char *data; +{ + struct win *p = (struct win *)data; + struct canvas *cv; + debug1("FOUND silence win %d\n", p->w_number); + for (display = displays; display; display = display->d_next) + { + for (cv = D_cvlist; cv; cv = cv->c_next) + if (cv->c_layer->l_bottom == &p->w_layer) + break; + if (cv) + continue; /* user already sees window */ +#ifdef MULTIUSER + if (!(ACLBYTE(p->w_lio_notify, D_user->u_id) & ACLBIT(D_user->u_id))) + continue; +#endif + Msg(0, "Window %d: silence for %d seconds", p->w_number, p->w_silencewait); + p->w_silence = SILENCE_FOUND; + WindowChanged(p, 'f'); + } +} + +static void +win_destroyev_fn(ev, data) +struct event *ev; +char *data; +{ + struct win *p = (struct win *)ev->data; + WindowDied(p, p->w_exitstatus, 1); +} + +#ifdef ZMODEM + +static int +zmodem_parse(p, bp, len) +struct win *p; +char *bp; +int len; +{ + int i; + char *b2 = bp; + for (i = 0; i < len; i++, b2++) + { + if (p->w_zauto == 0) + { + for (; i < len; i++, b2++) + if (*b2 == 030) + break; + if (i == len) + break; + if (i > 1 && b2[-1] == '*' && b2[-2] == '*') + p->w_zauto = 3; + continue; + } + if (p->w_zauto > 5 || *b2 == "**\030B00"[p->w_zauto] || (p->w_zauto == 5 && *b2 == '1') || (p->w_zauto == 5 && p->w_zdisplay && *b2 == '8')) + { + if (++p->w_zauto < 6) + continue; + if (p->w_zauto == 6) + p->w_zauto = 0; + if (!p->w_zdisplay) + { + if (i > 6) + WriteString(p, bp, i + 1 - 6); + WriteString(p, "\r\n", 2); + zmodem_found(p, *b2 == '1', b2 + 1, len - i - 1); + return 1; + } + else if (p->w_zauto == 7 || *b2 == '8') + { + int se = p->w_zdisplay->d_blocked == 2 ? 'O' : '\212'; + for (; i < len; i++, b2++) + if (*b2 == se) + break; + if (i < len) + { + zmodem_abort(p, 0); + D_blocked = 0; + D_readev.condpos = D_readev.condneg = 0; + while (len-- > 0) + AddChar(*bp++); + Flush(0); + Activate(D_fore ? D_fore->w_norefresh : 0); + return 1; + } + p->w_zauto = 6; + } + } + else + p->w_zauto = *b2 == '*' ? (p->w_zauto == 2 ? 2 : 1) : 0; + } + if (p->w_zauto == 0 && bp[len - 1] == '*') + p->w_zauto = len > 1 && bp[len - 2] == '*' ? 2 : 1; + if (p->w_zdisplay) + { + display = p->w_zdisplay; + while (len-- > 0) + AddChar(*bp++); + return 1; + } + return 0; +} + +static void +zmodem_fin(buf, len, data) +char *buf; +int len; +char *data; +{ + char *s; + int n; + + if (len) + RcLine(buf, strlen(buf) + 1); + else + { + s = "\030\030\030\030\030\030\030\030\030\030"; + n = strlen(s); + LayProcess(&s, &n); + } +} + +static void +zmodem_found(p, send, bp, len) +struct win *p; +int send; +char *bp; +int len; +{ + char *s; + int i, n; + extern int zmodem_mode; + + /* check for abort sequence */ + n = 0; + for (i = 0; i < len ; i++) + if (bp[i] != 030) + n = 0; + else if (++n > 4) + return; + if (zmodem_mode == 3 || (zmodem_mode == 1 && p->w_type != W_TYPE_PLAIN)) + { + struct display *d, *olddisplay; + + olddisplay = display; + d = p->w_lastdisp; + if (!d || d->d_fore != p) + for (d = displays; d; d = d->d_next) + if (d->d_fore == p) + break; + if (!d && p->w_layer.l_cvlist) + d = p->w_layer.l_cvlist->c_display; + if (!d) + d = displays; + if (!d) + return; + display = d; + RemoveStatus(); + p->w_zdisplay = display; + D_blocked = 2 + send; + flayer = &p->w_layer; + ZmodemPage(); + display = d; + evdeq(&D_blockedev); + D_readev.condpos = &const_IOSIZE; + D_readev.condneg = &p->w_inlen; + ClearAll(); + GotoPos(0, 0); + SetRendition(&mchar_blank); + AddStr("Zmodem active\r\n\r\n"); + AddStr(send ? "**\030B01" : "**\030B00"); + while (len-- > 0) + AddChar(*bp++); + display = olddisplay; + return; + } + flayer = &p->w_layer; + Input(":", 100, INP_COOKED, zmodem_fin, NULL, 0); + s = send ? zmodem_sendcmd : zmodem_recvcmd; + n = strlen(s); + LayProcess(&s, &n); +} + +void +zmodem_abort(p, d) +struct win *p; +struct display *d; +{ + struct display *olddisplay = display; + struct layer *oldflayer = flayer; + if (p) + { + if (p->w_savelayer && p->w_savelayer->l_next) + { + if (oldflayer == p->w_savelayer) + oldflayer = flayer->l_next; + flayer = p->w_savelayer; + ExitOverlayPage(); + } + p->w_zdisplay = 0; + p->w_zauto = 0; + LRefreshAll(&p->w_layer, 0); + } + if (d) + { + display = d; + D_blocked = 0; + D_readev.condpos = D_readev.condneg = 0; + Activate(D_fore ? D_fore->w_norefresh : 0); + } + display = olddisplay; + flayer = oldflayer; +} + +#endif + +int +WindowChangeNumber(struct win *win, int n) +{ + struct win *p; + int old = win->w_number; + + if (n < 0 || n >= maxwin) + { + Msg(0, "Given window position is invalid."); + return 0; + } + + p = wtab[n]; + wtab[n] = win; + win->w_number = n; + wtab[old] = p; + if (p) + p->w_number = old; +#ifdef MULTIUSER + /* exchange the acls for these windows. */ + AclWinSwap(old, n); +#endif +#ifdef UTMPOK + /* exchange the utmp-slots for these windows */ + if ((win->w_slot != (slot_t) -1) && (win->w_slot != (slot_t) 0)) + { + RemoveUtmp(win); + SetUtmp(win); + } + if (p && (p->w_slot != (slot_t) -1) && (p->w_slot != (slot_t) 0)) + { + /* XXX: first display wins? */ +#if 0 + /* Does this make more sense? */ + display = p->w_lastdisp ? p->w_lastdisp : p->w_layer.l_cvlist ? p->w_layer.l_cvlist->c_display : 0; +#else + display = win->w_layer.l_cvlist ? win->w_layer.l_cvlist->c_display : 0; +#endif + RemoveUtmp(p); + SetUtmp(p); + } +#endif + + WindowChanged(win, 'n'); + WindowChanged((struct win *)0, 'w'); + WindowChanged((struct win *)0, 'W'); + WindowChanged((struct win *)0, 0); + return 1; +} + |