/* 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 * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA * **************************************************************** */ #include "config.h" #include "window.h" #include #include #include #include #include #include #include #include #include "fileio.h" #include "help.h" #include "input.h" #include "mark.h" #include "misc.h" #include "process.h" #include "pty.h" #include "resize.h" #include "telnet.h" #include "termcap.h" #include "tty.h" #include "utmp.h" #include "winmsg.h" static void WinProcess(char **, size_t *); static void WinRedisplayLine(int, int, int, int); static void WinClearLine(int, int, int, int); static int WinResize(int, int); static void WinRestore(void); static int DoAutolf(char *, size_t *, int); static void ZombieProcess(char **, size_t *); static void win_readev_fn(Event *, void *); static void win_writeev_fn(Event *, void *); static void win_resurrect_zombie_fn(Event *, void *); static int muchpending(Window *, Event *); static void paste_slowev_fn(Event *, void *); static void pseu_readev_fn(Event *, void *); static void pseu_writeev_fn(Event *, void *); static void win_silenceev_fn(Event *, void *); static void win_destroyev_fn(Event *, void *); static int ForkWindow(Window *, char **, char *); static void zmodem_found(Window *, int, char *, size_t); static void zmodemFin(char *, size_t, void *); static int zmodem_parse(Window *, char *, size_t); bool VerboseCreate = false; /* XXX move this to user.h */ char DefaultShell[] = "/bin/sh"; #ifndef HAVE_EXECVPE static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin"; #endif struct NewWindow nwin_undef = { .StartAt = -1, .aka = NULL, .args = NULL, .dir = NULL, .term = NULL, .aflag = false, .dynamicaka = false, .flowflag = -1, .list_order = false, .list_nested = false, .lflag = -1, .histheight = -1, .monitor = -1, .wlock = -1, .silence = -1, .wrap = false, .Lflag = false, .slow = -1, .gr = -1, .c1 = false, .bce = -1, .encoding = -1, .hstatus = NULL, .charset = NULL, .poll_zombie_timeout = 0 }; struct NewWindow nwin_default = { .StartAt = 0, .aka = NULL, .args = ShellArgs, .dir = NULL, .term = screenterm, .aflag = false, .dynamicaka = true, .flowflag = FLOW_ON, .list_order = false, .list_nested = false, .lflag = 1, .histheight = DEFAULTHISTHEIGHT, .monitor = MON_OFF, .wlock = WLOCK_OFF, .silence = 0, .wrap = true, .Lflag = false, .slow = 0, .gr = 0, .c1 = true, .bce = 0, .encoding = 0, .hstatus = NULL, .charset = NULL }; struct NewWindow nwin_options; static int const_IOSIZE = IOSIZE; static int const_one = 1; void nwin_compose(struct NewWindow *def, struct NewWindow *new, struct NewWindow *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(dynamicaka); COMPOSE(flowflag); COMPOSE(list_order); COMPOSE(list_nested); 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); COMPOSE(poll_zombie_timeout); #undef COMPOSE } /***************************************************************** * * The window layer functions */ const struct LayFuncs WinLf = { WinProcess, NULL, WinRedisplayLine, WinClearLine, WinResize, WinRestore, NULL }; static int DoAutolf(char *buf, size_t *lenp, int fr) { char *p; size_t 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; memmove(p + 1, p, len++); p[1] = '\n'; } *lenp = p - buf; return trunc; } static void WinProcess(char **bufpp, size_t *lenp) { size_t l2 = 0, f, *ilen, l = *lenp, trunc; char *ibuf; fore = (Window *)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; } /* 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; } /* 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))) { Msg(0, "write: permission denied (user %s)", D_user->u_name); *bufpp += *lenp; *lenp = 0; return; } #ifdef ENABLE_TELNET if (fore->w_type == W_TYPE_TELNET && TelIsline(fore) && *bufpp != fore->w_telbuf) { TelProcessLine(bufpp, lenp); return; } #endif 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 = ARRAY_SIZE(fore->w_pwin->p_inbuf) - *ilen; } else { /* we send the user input to the window */ ibuf = fore->w_inbuf; ilen = &fore->w_inlen; f = ARRAY_SIZE(fore->w_inbuf) - *ilen; } if (l > f) l = f; #ifdef ENABLE_TELNET while (l > 0) #else if (l > 0) #endif { l2 = l; memmove(ibuf + *ilen, *bufpp, l2); if (fore->w_autolf && (trunc = DoAutolf(ibuf + *ilen, &l2, f - l2))) l -= trunc; #ifdef ENABLE_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(char **bufpp, size_t *lenp) { size_t l = *lenp; char *buf = *bufpp, b1[10], b2[10]; fore = (Window *)flayer->l_data; *bufpp += *lenp; *lenp = 0; for (; l-- > 0; buf++) { if (*(unsigned char *)buf == ZombieKey_destroy) { KillWindow(fore); return; } if (*(unsigned char *)buf == ZombieKey_resurrect) { 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(int y, int from, int to, int isblank) { if (y < 0) return; fore = (Window *)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 void WinClearLine(int y, int xs, int xe, int bce) { fore = (Window *)flayer->l_data; LClearLine(flayer, y, xs, xe, bce, &fore->w_mlines[y]); } static int WinResize(int wi, int he) { fore = (Window *)flayer->l_data; ChangeWindowSize(fore, wi, he, fore->w_histheight); return 0; } static void WinRestore(void) { fore = (Window *)flayer->l_data; for (Canvas *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_ON); InsertMode(fore->w_insert); ReverseVideo(fore->w_revvid); CursorVisibility(fore->w_curinv ? -1 : fore->w_curvvis); MouseMode(fore->w_mouse); ExtMouseMode(fore->w_extmouse); BracketedPasteMode(fore->w_bracketed); CursorStyle(fore->w_cursorstyle); } } /*****************************************************************/ /* * DoStartLog constructs a path for the "want to be logfile" in buf and * attempts logfopen. * * returns 0 on success. */ int DoStartLog(Window *window, char *buf, int bufsize) { int n; if (!window || !buf) return -1; strncpy(buf, MakeWinMsg(screenlogfile, window, '%'), bufsize - 1); buf[bufsize - 1] = 0; if (window->w_log != NULL) logfclose(window->w_log); if ((window->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; } static void remove_window_from_list(Window *win) { if (win) { Window *tmp = win->w_next; if (win->w_prev) { win->w_prev->w_next = tmp; } else { first_window = tmp; } if (tmp) { tmp->w_prev = win->w_prev; } else { last_window = win->w_prev; } win->w_next = NULL; win->w_prev = NULL; if (win == mru_window) { mru_window = win->w_prev_mru; } else { for (Window *w = mru_window; w; w = w->w_prev_mru) { if (w->w_prev_mru == win) { w->w_prev_mru = w->w_prev_mru->w_prev_mru; break; } } } win->w_prev_mru = NULL; } } /* * helper function to insert window into window list * we insert p before win, if win == NULL we are either at the end of list or list is empty */ static void add_window_to_list(Window *p, Window *win) { if (!p) return; /* maybe we should just Panic? but we should always get window to place */ /* most recently used list */ p->w_prev_mru = mru_window; mru_window = p; /* * place the new window in proper place on window list */ if (win) { /* if we are not at the end of list, we point after insertion point */ if (win == first_window) { /* we insert at the beginning! */ first_window->w_prev = p; p->w_next = first_window; p->w_prev = NULL; first_window = p; } else { /* we insert in the middle */ p->w_next = win->w_prev->w_next; p->w_prev = win->w_prev; win->w_prev->w_next = p; win->w_prev = p; } } else { /* if win is NULL, then we are either at the end of the list or there are no windows */ win = last_window; if (win) { /* we have last window, so add new window at the end of list */ last_window->w_next = p; p->w_prev = last_window; p->w_next = NULL; last_window = p; } else { /* there are no windows */ first_window = p; last_window = p; p->w_next = NULL; p->w_prev = NULL; } } } /* * Umask & wlock are set for the user of the display, * The display d (if specified) switches to that window. */ int MakeWindow(struct NewWindow *newwin) { Window *p; int i; int f = -1; struct NewWindow nwin; int type, startat; char *TtyName; Window *win = first_window; nwin_compose(&nwin_default, newwin, &nwin); startat = nwin.StartAt; /* skip windows with lower number */ while (win && win->w_number < startat) win = win->w_next; /* if there is no free spot, look for one (we can reach end of list) */ while (win && win->w_number == startat) { win = win->w_next; startat++; } #ifdef ENABLE_TELNET if (!strcmp(nwin.args[0], "//telnet")) { type = W_TYPE_TELNET; TtyName = "telnet"; } else #endif if ((f = OpenDevice(nwin.args, nwin.lflag, &type, &TtyName)) < 0) return -1; if (type == W_TYPE_GROUP) f = -1; if ((p = calloc(1, sizeof(Window))) == NULL) { if (type == W_TYPE_PTY) ClosePTY(f); else close(f); Msg(0, "%s", strnomem); return -1; } #ifdef ENABLE_UTMP 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] = NULL; if (nwin.dir) p->w_dir = SaveStr(nwin.dir); if (nwin.term) p->w_term = SaveStr(nwin.term); p->w_number = startat; p->w_group = NULL; if (fore && fore->w_type == W_TYPE_GROUP) p->w_group = fore; else if (fore && fore->w_group) p->w_group = fore->w_group; /* * 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); if (type == W_TYPE_PTY) ClosePTY(f); else close(f); Msg(0, "%s", strnomem); return -1; } p->w_layer.l_next = NULL; 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 = NULL; p->w_lastdisp = NULL; p->w_list_order = nwin.list_order; p->w_list_nested = nwin.list_nested; if (display && !AclCheckPermWin(D_user, ACL_WRITE, p)) p->w_wlockuser = D_user; p->w_wlock = nwin.wlock; p->w_ptyfd = f; p->w_aflag = nwin.aflag; p->w_dynamicaka = nwin.dynamicaka; p->w_flow = nwin.flowflag | ((nwin.flowflag & FLOW_AUTOFLAG) ? (FLOW_AUTO | FLOW_ON) : FLOW_AUTO); if (!nwin.aka) nwin.aka = Filename(nwin.args[0]); strncpy(p->w_akabuf, nwin.aka, ARRAY_SIZE(p->w_akabuf) - 1); if ((nwin.aka = strrchr(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; if (p->w_monitor == MON_ON) { /* always tell all users */ for (i = 0; i < maxusercount; i++) ACLBYTE(p->w_mon_notify, i) |= ACLBIT(i); } /* * defsilence by Lloyd Zusman (zusman_lloyd@jpmorgan.com) */ p->w_silence = nwin.silence; p->w_silencewait = SilenceWait; if (p->w_silence == SILENCE_ON) { /* always tell all users */ for (i = 0; i < maxusercount; i++) ACLBYTE(p->w_lio_notify, i) |= ACLBIT(i); } p->w_slowpaste = nwin.slow; p->w_norefresh = 0; strncpy(p->w_tty, TtyName, MAXSTR - 1); 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; } p->w_encoding = nwin.encoding; ResetWindow(p); /* sets w_wrap, w_c1, w_gr */ if (nwin.charset) SetCharsets(p, nwin.charset); if (VerboseCreate && type != W_TYPE_GROUP) { 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; p->w_pwin = NULL; #ifdef ENABLE_TELNET if (type == W_TYPE_TELNET) { if (TelOpenAndConnect(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; add_window_to_list(p, win); if (type == W_TYPE_GROUP) { SetForeWindow(p); Activate(p->w_norefresh); WindowChanged(NULL, WINESC_WIN_NAMES); WindowChanged(NULL, WINESC_WIN_NAMES_NOCUR); WindowChanged(NULL, 0); return startat; } #ifdef ENABLE_UTMP p->w_lflag = nwin.lflag; p->w_slot = (slot_t) - 1; if (nwin.lflag & 1) { 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 /* ENABLE_UTMP */ if (nwin.Lflag) { char buf[1024]; DoStartLog(p, buf, ARRAY_SIZE(buf)); } /* Is this all where I have to init window poll timeout? */ if (nwin.poll_zombie_timeout) p->w_poll_zombie_timeout = nwin.poll_zombie_timeout; p->w_zombieev.type = EV_TIMEOUT; p->w_zombieev.data = (char *)p; p->w_zombieev.handler = win_resurrect_zombie_fn; 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 = (int *)&p->w_inlen; evenq(&p->w_readev); evenq(&p->w_writeev); 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; p->w_silenceev.type = EV_TIMEOUT; p->w_silenceev.data = (char *)p; p->w_silenceev.handler = win_silenceev_fn; if (p->w_silence > 0) { SetTimeout(&p->w_silenceev, p->w_silencewait * 1000); evenq(&p->w_silenceev); } p->w_destroyev.type = EV_TIMEOUT; p->w_destroyev.data = NULL; p->w_destroyev.handler = win_destroyev_fn; SetForeWindow(p); Activate(p->w_norefresh); WindowChanged(NULL, WINESC_WIN_NAMES); WindowChanged(NULL, WINESC_WIN_NAMES_NOCUR); WindowChanged(NULL, 0); return startat; } /* * 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(Window *window) { char *TtyName; int lflag; int fd = -1; lflag = nwin_default.lflag; #ifdef ENABLE_TELNET if (!strcmp(window->w_cmdargs[0], "//telnet")) { window->w_type = W_TYPE_TELNET; TtyName = "telnet"; } else #endif { /* see above #ifdef */ if ((fd = OpenDevice(window->w_cmdargs, lflag, &window->w_type, &TtyName)) < 0) return -1; } evdeq(&window->w_destroyev); /* no re-destroy of resurrected zombie */ strncpy(window->w_tty, *TtyName ? TtyName : window->w_title, MAXSTR - 1); window->w_ptyfd = fd; window->w_readev.fd = fd; window->w_writeev.fd = fd; evenq(&window->w_readev); evenq(&window->w_writeev); if (VerboseCreate) { Display *d = display; /* WriteString zaps display */ WriteString(window, ":screen (", 9); WriteString(window, window->w_title, strlen(window->w_title)); WriteString(window, "):", 2); for (int i = 0; window->w_cmdargs[i]; i++) { WriteString(window, " ", 1); WriteString(window, window->w_cmdargs[i], strlen(window->w_cmdargs[i])); } WriteString(window, "\r\n", 2); display = d; } window->w_deadpid = 0; window->w_pid = 0; #ifdef ENABLE_TELNET if (window->w_type == W_TYPE_TELNET) { if (TelOpenAndConnect(window)) return -1; } else #endif if (window->w_type == W_TYPE_PTY) { window->w_pid = ForkWindow(window, window->w_cmdargs, TtyName); if (window->w_pid < 0) return -1; } #ifdef ENABLE_UTMP if (window->w_slot == (slot_t) 0 && (display || (window->w_lflag & 2))) SetUtmp(window); #ifdef CAREFULUTMP CarefulUtmp(); /* If all 've been zombies, we've had no slot */ #endif #endif WindowChanged(window, WINESC_WFLAGS); return window->w_number; } void CloseDevice(Window *window) { if (window->w_ptyfd < 0) { return; } switch (window->w_type) { case W_TYPE_PTY: /* pty 4 SALE */ (void)chmod(window->w_tty, 0666); (void)chown(window->w_tty, 0, 0); ClosePTY(window->w_ptyfd); break; case W_TYPE_PLAIN: CloseTTY(window->w_ptyfd); break; default: close(window->w_ptyfd); break; } window->w_ptyfd = -1; window->w_tty[0] = 0; evdeq(&window->w_readev); evdeq(&window->w_writeev); #ifdef ENABLE_TELNET evdeq(&window->w_telconnev); #endif window->w_readev.fd = window->w_writeev.fd = -1; } void FreeWindow(Window *window) { if (window->w_pwin) FreePseudowin(window); #ifdef ENABLE_UTMP RemoveUtmp(window); #endif CloseDevice(window); if (window == console_window) { TtyGrabConsole(-1, false, "free"); console_window = NULL; } if (window->w_log != NULL) logfclose(window->w_log); ChangeWindowSize(window, 0, 0, 0); if (window->w_type == W_TYPE_GROUP) { for (Window *win = mru_window; win; win = win->w_prev_mru) if (win->w_group == window) win->w_group = window->w_group; } if (window->w_hstatus) free(window->w_hstatus); for (int i = 0; window->w_cmdargs[i]; i++) free(window->w_cmdargs[i]); if (window->w_dir) free(window->w_dir); if (window->w_term) free(window->w_term); for (Display *display = displays; display; display = display->d_next) { if (display->d_other == window) display->d_other = display->d_fore && display->d_fore->w_prev_mru != window ? display->d_fore->w_prev_mru : window->w_prev_mru; if (display->d_fore == window) display->d_fore = NULL; for (Canvas *canvas = display->d_cvlist; canvas; canvas = canvas->c_next) { Layer *layer; for (layer = canvas->c_layer; layer; layer = layer->l_next) if (layer->l_layfn == &WinLf) break; if (!layer) continue; if ((Window *)layer->l_data != window) continue; if (canvas->c_layer == window->w_savelayer) window->w_savelayer = NULL; KillLayerChain(canvas->c_layer); } } if (window->w_savelayer) KillLayerChain(window->w_savelayer); for (Canvas *canvas = window->w_layer.l_cvlist; canvas; canvas = canvas->c_lnext) { canvas->c_layer = &canvas->c_blank; canvas->c_blank.l_cvlist = canvas; canvas->c_lnext = NULL; canvas->c_xoff = canvas->c_xs; canvas->c_yoff = canvas->c_ys; RethinkViewportOffsets(canvas); } window->w_layer.l_cvlist = NULL; if (flayer == &window->w_layer) flayer = NULL; LayerCleanupMemory(&window->w_layer); FreeWindowAcl(window); evdeq(&window->w_readev); /* just in case */ evdeq(&window->w_writeev); /* just in case */ evdeq(&window->w_silenceev); evdeq(&window->w_zombieev); evdeq(&window->w_destroyev); FreePaster(&window->w_paster); free((char *)window); } int OpenDevice(char **args, int lflag, int *typep, char **namep) { char *arg = args[0]; struct stat st; int fd; #ifndef ENABLE_UTMP (void)lflag; /* unused */ #endif if (!arg) return -1; if (strcmp(arg, "//group") == 0) { *typep = W_TYPE_GROUP; *namep = "telnet"; return 0; } 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; } if ((fd = OpenTTY(arg, args[1])) < 0) return -1; #ifdef ENABLE_UTMP lflag = 0; #endif *typep = W_TYPE_PLAIN; *namep = arg; } else { *typep = W_TYPE_PTY; fd = OpenPTY(namep); if (fd == -1) { Msg(0, "No more PTYs."); return -1; } #ifdef TIOCPKT { int flag = 1; if (ioctl(fd, TIOCPKT, (char *)&flag)) { Msg(errno, "TIOCPKT ioctl"); ClosePTY(fd); return -1; } } #endif /* TIOCPKT */ } (void)fcntl(fd, F_SETFL, FNBLOCK); /* * 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(fd, TCIOFLUSH); if (*typep != W_TYPE_PTY) return fd; #ifndef PTY_ROFS #ifdef PTY_GROUP if (chown(*namep, real_uid, PTY_GROUP) && !eff_uid) #else if (chown(*namep, real_uid, real_gid) && !eff_uid) #endif { Msg(errno, "chown tty"); if (*typep == W_TYPE_PTY) ClosePTY(fd); else close(fd); return -1; } #ifdef ENABLE_UTMP if (chmod(*namep, lflag ? TtyMode : (TtyMode & ~022)) && !eff_uid) #else if (chmod(*namep, TtyMode) && !eff_uid) #endif { Msg(errno, "chmod tty"); if (*typep == W_TYPE_PTY) ClosePTY(fd); else close(fd); return -1; } #endif return fd; } /* * Fields w_width, w_height, aflag, number (and w_tty) * are read from Window *win. No fields written. * If pwin is nonzero, filedescriptors are distributed * between win->w_tty and open(ttyn) * */ static int ForkWindow(Window *win, char **args, char *ttyn) { pid_t pid; char tebuf[MAXTERMLEN + 5 + 1]; /* MAXTERMLEN + strlen("TERM=") + '\0' */ char ebuf[20]; char shellbuf[7 + MAXPATHLEN]; char *proc; int newfd; int w = win->w_width; int h = win->w_height; int pat, wfdused; struct pseudowin *pwin = win->w_pwin; int slave = -1; #ifdef O_NOCTTY if (pty_preopen) { if ((slave = open(ttyn, O_RDWR | O_NOCTTY)) == -1) { Msg(errno, "ttyn"); return -1; } } #endif proc = *args; if (proc == NULL) { args = ShellArgs; proc = *args; } fflush(stdout); fflush(stderr); switch (pid = fork()) { case -1: Msg(errno, "fork"); break; case 0: xsignal(SIGHUP, SIG_DFL); xsignal(SIGINT, SIG_DFL); xsignal(SIGQUIT, SIG_DFL); xsignal(SIGTERM, SIG_DFL); xsignal(SIGTTIN, SIG_DFL); xsignal(SIGTTOU, SIG_DFL); #ifdef SIGXFSZ xsignal(SIGXFSZ, SIG_DFL); #endif displays = NULL; /* beware of Panic() */ ServerSocket = -1; if (setgid(real_gid) || setuid(real_uid)) Panic(errno, "Setuid/gid"); eff_uid = real_uid; eff_gid = real_gid; if (!pwin) /* ignore directory if pseudo */ 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); if (slave != -1) { close(0); if(dup(slave) < 0) Panic(errno, "Cannot duplicate file descriptor"); close(slave); closeallfiles(win->w_ptyfd); slave = dup(0); } else closeallfiles(win->w_ptyfd); /* Close the three /dev/null descriptors */ close(0); close(1); close(2); newfd = -1; /* * distribute filedescriptors between the ttys */ pat = pwin ? pwin->p_fdpat : ((F_PFRONT << (F_PSHIFT * 2)) | (F_PFRONT << F_PSHIFT) | F_PFRONT); wfdused = 0; for (int 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 { if (dup(newfd) < 0) Panic(errno, "Cannot duplicate file descriptor"); } if (fgtty(newfd)) Msg(errno, "fgtty"); } else { if(dup(win->w_ptyfd) < 0) Panic(errno, "Cannot duplicate file descriptor"); wfdused = 1; } } if (wfdused) { /* * the pseudo window process should not be surprised with a * nonblocking filedescriptor. Poor Backend! */ if (fcntl(win->w_ptyfd, F_SETFL, 0)) Msg(errno, "Warning: clear NBLOCK fcntl failed"); } close(win->w_ptyfd); if (slave != -1) close(slave); if (newfd >= 0) { struct mode fakemode, *modep; if (display) { modep = &D_OldMode; } else { modep = &fakemode; InitTTY(modep, 0); } /* We only want echo if the users input goes to the pseudo * and the pseudo's stdout is not send to the window. */ if (pwin && (!(pat & F_UWP) || (pat & F_PBACK << F_PSHIFT))) { modep->tio.c_lflag &= ~ECHO; modep->tio.c_iflag &= ~ICRNL; } SetTTY(newfd, modep); glwz.ws_col = w; glwz.ws_row = h; (void)ioctl(newfd, TIOCSWINSZ, (char *)&glwz); /* Always turn off nonblocking mode */ (void)fcntl(newfd, F_SETFL, 0); } NewEnv[2] = MakeTermcap(display == NULL || win->w_aflag); strcpy(shellbuf, "SHELL="); strncpy(shellbuf + 6, ShellProg + (*ShellProg == '-'), ARRAY_SIZE(shellbuf) - 7); shellbuf[ARRAY_SIZE(shellbuf) - 1] = 0; NewEnv[4] = shellbuf; if (win->w_term && *win->w_term && strcmp(screenterm, win->w_term) && (strlen(win->w_term) < MAXTERMLEN)) { char *s1, *s2, tl; snprintf(tebuf, ARRAY_SIZE(tebuf), "TERM=%s", win->w_term); tl = strlen(win->w_term); NewEnv[1] = tebuf; if ((s1 = strchr(NewEnv[2], '|'))) { if ((s2 = strchr(++s1, '|'))) { if (strlen(NewEnv[2]) - (s2 - s1) + tl < 1024) { memmove(s1 + tl, s2, strlen(s2) + 1); memmove(s1, win->w_term, tl); } } } } snprintf(ebuf, ARRAY_SIZE(ebuf), "WINDOW=%d", win->w_number); NewEnv[3] = ebuf; if (*proc == '-') proc++; if (!*proc) proc = DefaultShell; execvpe(proc, args, NewEnv); Panic(errno, "Cannot exec '%s'", proc); default: break; } if (slave != -1) close(slave); return pid; } #ifndef HAVE_EXECVPE void execvpe(char *prog, char **args, char **env) { char *path = NULL; char buf[1024]; char *shargs[MAXARGS + 1]; int eaccess = 0; if (strrchr(prog, '/')) path = ""; if (!path && !(path = getenv("PATH"))) path = DefaultPath; do { char *p; for (p = buf; *path && *path != ':'; path++) if (p - buf < (int)ARRAY_SIZE(buf) - 2) *p++ = *path; if (p > buf) *p++ = '/'; if (p - buf + strlen(prog) >= ARRAY_SIZE(buf) - 1) continue; strcpy(p, prog); execve(buf, args, env); switch (errno) { case ENOEXEC: shargs[0] = DefaultShell; shargs[1] = buf; for (int 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 int winexec(char **av) { char *p, *s, *t; int r = 0, l = 0; Window *w; struct pseudowin *pwin; int type; if ((w = display ? fore : mru_window) == 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 = 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 (int 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; l = MAXSTR - 4; for (char **pp = av; *pp; pp++) { p = *pp; while (*p && l-- > 0) *t++ = *p++; if (l <= 0) break; *t++ = ' '; } *--t = '\0'; 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 = (int *)&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(Window *w) { struct pseudowin *pwin = w->w_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) { if (w->w_type == W_TYPE_PTY) ClosePTY(pwin->p_ptyfd); else close(pwin->p_ptyfd); } evdeq(&pwin->p_readev); evdeq(&pwin->p_writeev); if (w->w_readev.condneg == (int *)&pwin->p_inlen) w->w_readev.condpos = w->w_readev.condneg = NULL; evenq(&w->w_readev); free((char *)pwin); w->w_pwin = NULL; } /* * returns 0, if the lock really has been released */ int ReleaseAutoWritelock(Display *dis, Window *w) { /* release auto writelock when user has no other display here */ if (w->w_wlock == WLOCK_AUTO && w->w_wlockuser == dis->d_user) { Display *d; for (d = displays; d; d = d->d_next) if ((d != dis) && (d->d_fore == w) && (d->d_user == dis->d_user)) break; if (!d) { w->w_wlockuser = NULL; return 0; } } return 1; } /* * returns 0, if the lock really could be obtained */ int ObtainAutoWritelock(Display *d, Window *w) { if ((w->w_wlock == WLOCK_AUTO) && !AclCheckPermWin(d->d_user, ACL_WRITE, w) && !w->w_wlockuser) { w->w_wlockuser = d->d_user; return 0; } return 1; } /********************************************************************/ static void paste_slowev_fn(Event *event, void *data) { struct paster *pa = (struct paster *)data; Window *p; (void)event; /* unused */ size_t len = 1; flayer = pa->pa_pastelayer; if (!flayer) pa->pa_pastelen = 0; if (!pa->pa_pastelen) return; p = Layer2Window(flayer); DoProcess(p, &pa->pa_pasteptr, &len, pa); pa->pa_pastelen -= 1 - len; if (pa->pa_pastelen > 0) { SetTimeout(&pa->pa_slowev, p->w_slowpaste); evenq(&pa->pa_slowev); } } static int muchpending(Window *p, Event *event) { for (Canvas *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 */ event->condpos = &const_one; event->condneg = (int *)&D_status; return 1; } if (D_blocked) continue; if (D_obufp - D_obuf > D_obufmax + D_blocked_fuzz) { if (D_nonblock == 0) { D_blocked = 1; continue; } event->condpos = &D_obuffree; event->condneg = &D_obuflenmax; if (D_nonblock > 0 && !D_blockedev.queued) { SetTimeout(&D_blockedev, D_nonblock); evenq(&D_blockedev); } return 1; } } return 0; } static void win_readev_fn(Event *event, void *data) { Window *p = (Window *)data; char buf[IOSIZE], *bp; int size, len; int wtop; bp = buf; size = IOSIZE; wtop = p->w_pwin && W_WTOP(p); if (wtop) { size = IOSIZE - p->w_pwin->p_inlen; if (size <= 0) { event->condpos = &const_IOSIZE; event->condneg = (int *)&p->w_pwin->p_inlen; return; } } if (p->w_layer.l_cvlist && muchpending(p, event)) return; if (!p->w_zdisplay) if (p->w_blocked) { event->condpos = &const_one; event->condneg = &p->w_blocked; return; } if (event->condpos) event->condpos = event->condneg = NULL; if ((len = p->w_outlen)) { p->w_outlen = 0; WriteString(p, p->w_outbuf, len); return; } if ((len = read(event->fd, buf, size)) <= 0) { if (errno == EINTR || errno == EAGAIN) return; #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) if (errno == EWOULDBLOCK) return; #endif WindowDied(p, 0, 0); return; } #ifdef TIOCPKT if (p->w_type == W_TYPE_PTY) { if (buf[0]) { if (buf[0] & TIOCPKT_NOSTOP) WNewAutoFlow(p, 0); if (buf[0] & TIOCPKT_DOSTOP) WNewAutoFlow(p, 1); } bp++; len--; } #endif #ifdef ENABLE_TELNET if (p->w_type == W_TYPE_TELNET) len = TelIn(p, bp, len, buf + ARRAY_SIZE(buf) - (bp + len)); #endif if (len == 0) return; if (zmodem_mode && zmodem_parse(p, bp, len)) return; if (wtop) { memmove(p->w_pwin->p_inbuf + p->w_pwin->p_inlen, bp, len); p->w_pwin->p_inlen += len; } LayPause(&p->w_layer, 1); WriteString(p, bp, len); LayPause(&p->w_layer, 0); return; } static void win_resurrect_zombie_fn(Event *event, void *data) { Window *p = (Window *)data; (void)event; /* unused */ /* Already reconnected? */ if (p->w_deadpid != p->w_pid) return; WriteString(p, "\r\n", 2); RemakeWindow(p); } static void win_writeev_fn(Event *event, void *data) { Window *p = (Window *)data; size_t len; if (p->w_inlen) { if ((len = write(event->fd, p->w_inbuf, p->w_inlen)) <= 0) len = p->w_inlen; /* dead window */ if (p->w_miflag) { /* don't loop if not needed */ for (Window *win = mru_window; win; win = win->w_prev_mru) { if (win != p && win->w_miflag) write(win->w_ptyfd, p->w_inbuf, p->w_inlen); } } if ((p->w_inlen -= len)) memmove(p->w_inbuf, p->w_inbuf + len, p->w_inlen); } 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); } return; } static void pseu_readev_fn(Event *event, void *data) { Window *p = (Window *)data; char buf[IOSIZE]; int size, ptow, len; size = IOSIZE; ptow = W_PTOW(p); if (ptow) { size = IOSIZE - p->w_inlen; if (size <= 0) { event->condpos = &const_IOSIZE; event->condneg = (int *)&p->w_inlen; return; } } if (p->w_layer.l_cvlist && muchpending(p, event)) return; if (p->w_blocked) { event->condpos = &const_one; event->condneg = &p->w_blocked; return; } if (event->condpos) event->condpos = event->condneg = NULL; if ((len = p->w_outlen)) { p->w_outlen = 0; WriteString(p, p->w_outbuf, len); return; } if ((len = read(event->fd, buf, size)) <= 0) { if (errno == EINTR || errno == EAGAIN) return; #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) if (errno == EWOULDBLOCK) return; #endif FreePseudowin(p); return; } /* no packet mode on pseudos! */ if (ptow) { memmove(p->w_inbuf + p->w_inlen, buf, len); p->w_inlen += len; } WriteString(p, buf, len); return; } static void pseu_writeev_fn(Event *event, void *data) { Window *p = (Window *)data; struct pseudowin *pw = p->w_pwin; size_t len; if (pw->p_inlen == 0) return; if ((len = write(event->fd, pw->p_inbuf, pw->p_inlen)) <= 0) len = pw->p_inlen; /* dead pseudo */ if ((p->w_pwin->p_inlen -= len)) memmove(p->w_pwin->p_inbuf, p->w_pwin->p_inbuf + len, p->w_pwin->p_inlen); } static void win_silenceev_fn(Event *event, void *data) { Window *p = (Window *)data; Canvas *cv; (void)event; /* unused */ 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 */ if (!(ACLBYTE(p->w_lio_notify, D_user->u_id) & ACLBIT(D_user->u_id))) continue; Msg(0, "Window %d: silence for %d seconds", p->w_number, p->w_silencewait); p->w_silence = SILENCE_FOUND; WindowChanged(p, WINESC_WFLAGS); } } static void win_destroyev_fn(Event *event, void *data) { Window *p = (Window *)event->data; (void)data; /* unused */ WindowDied(p, p->w_exitstatus, 1); } static int zmodem_parse(Window *p, char *bp, size_t len) { char *b2 = bp; for (size_t 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, NULL); D_blocked = 0; D_readev.condpos = D_readev.condneg = NULL; 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 zmodemFin(char *buf, size_t len, void *data) { char *s; size_t l; (void)data; /* unused */ if (len) RcLine(buf, strlen(buf) + 1); else { s = "\030\030\030\030\030\030\030\030\030\030"; l = strlen(s); LayProcess(&s, &l); } } static void zmodem_found(Window *p, int send, char *bp, size_t len) { char *s; size_t n; /* check for abort sequence */ n = 0; for (size_t 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)) { 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 = (int *)&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(":", MAXSTR, INP_COOKED, zmodemFin, NULL, 0); s = send ? zmodem_sendcmd : zmodem_recvcmd; n = strlen(s); LayProcess(&s, &n); } void zmodem_abort(Window *p, Display *d) { Display *olddisplay = display; 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 = NULL; p->w_zauto = 0; LRefreshAll(&p->w_layer, 0); } if (d) { display = d; D_blocked = 0; D_readev.condpos = D_readev.condneg = NULL; Activate(D_fore ? D_fore->w_norefresh : 0); } display = olddisplay; flayer = oldflayer; } int SwapWindows(int old, int dest) { Window *win_a, *win_b; Window *tmp; if (dest < 0) { Msg(0, "Given window position is invalid."); return 0; } if (old == dest) return 1; win_a = GetWindowByNumber(old); win_b = GetWindowByNumber(dest); remove_window_from_list(win_a); win_a->w_number = dest; if (win_b) { remove_window_from_list(win_b); win_b->w_number = old; } tmp = first_window; while (tmp && tmp->w_number < win_a->w_number) tmp = tmp->w_next; add_window_to_list(win_a, tmp); if (win_b) { tmp = first_window; while (tmp && tmp->w_number < win_b->w_number) tmp = tmp->w_next; add_window_to_list(win_b, tmp); } /* exchange the acls for these windows. */ #ifdef ENABLE_UTMP /* exchange the utmp-slots for these windows */ if ((win_a->w_slot != (slot_t) - 1) && (win_a->w_slot != (slot_t) 0)) { RemoveUtmp(win_a); SetUtmp(win_a); } if (win_b && (win_b->w_slot != (slot_t) - 1) && (win_b->w_slot != (slot_t) 0)) { display = win_a->w_layer.l_cvlist ? win_a->w_layer.l_cvlist->c_display : NULL; RemoveUtmp(win_b); SetUtmp(win_b); } #endif WindowChanged(win_a, WINESC_WIN_NUM); WindowChanged(NULL, WINESC_WIN_NAMES); WindowChanged(NULL, WINESC_WIN_NAMES_NOCUR); WindowChanged(NULL, 0); return 1; } void WindowDied(Window *p, int wstat, int wstat_valid) { int killit = 0; if (p->w_destroyev.data == (char *)p) { wstat = p->w_exitstatus; wstat_valid = 1; evdeq(&p->w_destroyev); p->w_destroyev.data = NULL; } if (!wstat_valid && p->w_pid > 0) { /* EOF on file descriptor. The process is probably also dead. * try a waitpid */ if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid) { p->w_pid = 0; wstat_valid = 1; } } if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid && WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0) killit = 1; if (ZombieKey_destroy && !killit) { char buf[100], *s, reason[60]; time_t now; if (wstat_valid) { if (WIFEXITED(wstat)) if (WEXITSTATUS(wstat)) sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat)); else sprintf(reason, "terminated normally"); else if (WIFSIGNALED(wstat)) sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat), #ifdef WCOREDUMP WCOREDUMP(wstat) ? " (core file generated)" : ""); #else ""); #endif } else sprintf(reason, "detached from window"); (void)time(&now); s = ctime(&now); if (s && *s) s[strlen(s) - 1] = '\0'; #ifdef ENABLE_UTMP if (p->w_slot != (slot_t) 0 && p->w_slot != (slot_t) - 1) { RemoveUtmp(p); p->w_slot = NULL; /* "detached" */ } #endif CloseDevice(p); p->w_deadpid = p->w_pid; p->w_pid = 0; ResetWindow(p); /* p->w_y = p->w_bot; */ p->w_y = MFindUsedLine(p, p->w_bot, 1); sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?"); WriteString(p, buf, strlen(buf)); if (p->w_poll_zombie_timeout) { SetTimeout(&p->w_zombieev, p->w_poll_zombie_timeout * 1000); evenq(&p->w_zombieev); } WindowChanged(p, WINESC_WFLAGS); } else KillWindow(p); #ifdef ENABLE_UTMP CarefulUtmp(); #endif } void ResetWindow(Window *win) { win->w_wrap = nwin_default.wrap; win->w_origin = 0; win->w_insert = false; win->w_revvid = 0; win->w_mouse = 0; win->w_bracketed = false; win->w_cursorstyle = 0; win->w_curinv = 0; win->w_curvvis = 0; win->w_autolf = 0; win->w_keypad = 0; win->w_cursorkeys = 0; win->w_top = 0; win->w_bot = win->w_height - 1; win->w_saved.on = 0; win->w_x = win->w_y = 0; win->w_state = LIT; win->w_StringType = NONE; memset(win->w_tabs, 0, win->w_width); for (int i = 8; i < win->w_width; i += 8) win->w_tabs[i] = 1; win->w_rend = mchar_null; ResetCharsets(win); } Window *GetWindowByNumber(uint16_t n) { if (first_window->w_number <= n && n <= last_window->w_number) { Window *w; if ((first_window->w_number + (last_window->w_number - first_window->w_number) / 2) > n) { /* look from the start */ w = first_window; while (w && w->w_number < n) w = w->w_next; } else { /* look from the end */ w = last_window; while (w && w->w_number > n) w = w->w_prev; } if (w->w_number == n) return w; } return NULL; }