Implement window.onclose event, which happens just before window close.
[screen-lua.git] / src / window.c
blobfa4500f725c7d1a101262a35c0955e91bb04a444
1 /* Copyright (c) 2008, 2009
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Micah Cowan (micah@cowan.name)
5 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
6 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
7 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
8 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
9 * Copyright (c) 1987 Oliver Laumann
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3, or (at your option)
14 * any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program (see the file COPYING); if not, see
23 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 ****************************************************************
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 #ifndef sun
34 # include <sys/ioctl.h>
35 #endif
37 #include "config.h"
39 #include "screen.h"
40 #include "extern.h"
41 #include "logfile.h" /* logfopen() */
43 extern struct display *displays, *display;
44 extern struct win *windows, *fore, *wtab[], *console_window;
45 extern char *ShellArgs[];
46 extern char *ShellProg;
47 extern char screenterm[];
48 extern char *screenlogfile;
49 extern char HostName[];
50 extern int TtyMode;
51 extern int SilenceWait;
52 extern int real_uid, real_gid, eff_uid, eff_gid;
53 extern char Termcap[];
54 extern char **NewEnv;
55 extern int visual_bell, maxwin;
56 extern struct event logflushev;
57 extern int log_flush, logtstamp_after;
58 extern int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
59 extern struct layer *flayer;
60 extern int maxusercount;
61 extern int pty_preopen;
62 #ifdef ZMODEM
63 extern int zmodem_mode;
64 extern struct mchar mchar_blank;
65 extern char *zmodem_sendcmd;
66 extern char *zmodem_recvcmd;
67 #endif
69 #if defined(TIOCSWINSZ) || defined(TIOCGWINSZ)
70 extern struct winsize glwz;
71 #endif
73 #ifdef O_NOCTTY
74 extern int separate_sids;
75 #endif
77 static void WinProcess __P((char **, int *));
78 static void WinRedisplayLine __P((int, int, int, int));
79 static void WinClearLine __P((int, int, int, int));
80 static int WinRewrite __P((int, int, int, struct mchar *, int));
81 static int WinResize __P((int, int));
82 static void WinRestore __P((void));
83 static int DoAutolf __P((char *, int *, int));
84 static void ZombieProcess __P((char **, int *));
85 static void win_readev_fn __P((struct event *, char *));
86 static void win_writeev_fn __P((struct event *, char *));
87 static int muchpending __P((struct win *, struct event *));
88 #ifdef COPY_PASTE
89 static void paste_slowev_fn __P((struct event *, char *));
90 #endif
91 #ifdef PSEUDOS
92 static void pseu_readev_fn __P((struct event *, char *));
93 static void pseu_writeev_fn __P((struct event *, char *));
94 #endif
95 static void win_silenceev_fn __P((struct event *, char *));
97 static int OpenDevice __P((char **, int, int *, char **));
98 static int ForkWindow __P((struct win *, char **, char *));
99 #ifdef ZMODEM
100 static void zmodem_found __P((struct win *, int, char *, int));
101 static void zmodem_fin __P((char *, int, char *));
102 static int zmodem_parse __P((struct win *, char *, int));
103 #endif
107 int VerboseCreate = 0; /* XXX move this to user.h */
109 char DefaultShell[] = "/bin/sh";
110 static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
112 /* keep this in sync with the structure definition in window.h */
113 struct NewWindow nwin_undef =
115 -1, /* StartAt */
116 (char *)0, /* aka */
117 (char **)0, /* args */
118 (char *)0, /* dir */
119 (char *)0, /* term */
120 -1, /* aflag */
121 -1, /* flowflag */
122 -1, /* lflag */
123 -1, /* histheight */
124 -1, /* monitor */
125 -1, /* wlock */
126 -1, /* silence */
127 -1, /* wrap */
128 -1, /* logging */
129 -1, /* slowpaste */
130 -1, /* gr */
131 -1, /* c1 */
132 -1, /* bce */
133 -1, /* encoding */
134 (char *)0, /* hstatus */
135 (char *)0 /* charset */
138 struct NewWindow nwin_default =
140 0, /* StartAt */
141 0, /* aka */
142 ShellArgs, /* args */
143 0, /* dir */
144 screenterm, /* term */
145 0, /* aflag */
146 1*FLOW_NOW, /* flowflag */
147 LOGINDEFAULT, /* lflag */
148 DEFAULTHISTHEIGHT, /* histheight */
149 MON_OFF, /* monitor */
150 WLOCK_OFF, /* wlock */
151 0, /* silence */
152 1, /* wrap */
153 0, /* logging */
154 0, /* slowpaste */
155 0, /* gr */
156 1, /* c1 */
157 0, /* bce */
158 0, /* encoding */
159 (char *)0, /* hstatus */
160 (char *)0 /* charset */
163 struct NewWindow nwin_options;
165 static int const_IOSIZE = IOSIZE;
166 static int const_one = 1;
168 void
169 nwin_compose(def, new, res)
170 struct NewWindow *def, *new, *res;
172 #define COMPOSE(x) res->x = new->x != nwin_undef.x ? new->x : def->x
173 COMPOSE(StartAt);
174 COMPOSE(aka);
175 COMPOSE(args);
176 COMPOSE(dir);
177 COMPOSE(term);
178 COMPOSE(aflag);
179 COMPOSE(flowflag);
180 COMPOSE(lflag);
181 COMPOSE(histheight);
182 COMPOSE(monitor);
183 COMPOSE(wlock);
184 COMPOSE(silence);
185 COMPOSE(wrap);
186 COMPOSE(Lflag);
187 COMPOSE(slow);
188 COMPOSE(gr);
189 COMPOSE(c1);
190 COMPOSE(bce);
191 COMPOSE(encoding);
192 COMPOSE(hstatus);
193 COMPOSE(charset);
194 #undef COMPOSE
197 /*****************************************************************
199 * The window layer functions
202 struct LayFuncs WinLf =
204 WinProcess,
206 WinRedisplayLine,
207 WinClearLine,
208 WinRewrite,
209 WinResize,
210 WinRestore
213 static int
214 DoAutolf(buf, lenp, fr)
215 char *buf;
216 int *lenp;
217 int fr;
219 char *p;
220 int len = *lenp;
221 int trunc = 0;
223 for (p = buf; len > 0; p++, len--)
225 if (*p != '\r')
226 continue;
227 if (fr-- <= 0)
229 trunc++;
230 len--;
232 if (len == 0)
233 break;
234 bcopy(p, p + 1, len++);
235 p[1] = '\n';
237 *lenp = p - buf;
238 return trunc;
241 static void
242 WinProcess(bufpp, lenp)
243 char **bufpp;
244 int *lenp;
246 int l2 = 0, f, *ilen, l = *lenp, trunc;
247 char *ibuf;
249 debug1("WinProcess: %d bytes\n", *lenp);
250 fore = (struct win *)flayer->l_data;
252 if (fore->w_type == W_TYPE_GROUP)
254 *bufpp += *lenp;
255 *lenp = 0;
256 return;
258 if (fore->w_ptyfd < 0) /* zombie? */
260 ZombieProcess(bufpp, lenp);
261 return;
263 #ifdef MULTIUSER
264 /* a pending writelock is this:
265 * fore->w_wlock == WLOCK_AUTO, fore->w_wlockuse = NULL
266 * The user who wants to use this window next, will get the lock, if he can.
268 if (display && fore->w_wlock == WLOCK_AUTO &&
269 !fore->w_wlockuser && !AclCheckPermWin(D_user, ACL_WRITE, fore))
271 fore->w_wlockuser = D_user;
272 debug2("window %d: pending writelock grabbed by user %s\n",
273 fore->w_number, fore->w_wlockuser->u_name);
275 /* if w_wlock is set, only one user may write, else we check acls */
276 if (display && ((fore->w_wlock == WLOCK_OFF) ?
277 AclCheckPermWin(D_user, ACL_WRITE, fore) :
278 (D_user != fore->w_wlockuser)))
280 debug2("window %d, user %s: ", fore->w_number, D_user->u_name);
281 debug2("writelock %d (wlockuser %s)\n", fore->w_wlock,
282 fore->w_wlockuser ? fore->w_wlockuser->u_name : "NULL");
283 /* XXX FIXME only display !*/
284 WBell(fore, visual_bell);
285 *bufpp += *lenp;
286 *lenp = 0;
287 return;
289 #endif /* MULTIUSER */
291 #ifdef BUILTIN_TELNET
292 if (fore->w_type == W_TYPE_TELNET && TelIsline(fore) && *bufpp != fore->w_telbuf)
294 TelProcessLine(bufpp, lenp);
295 return;
297 #endif
299 #ifdef PSEUDOS
300 if (W_UWP(fore))
302 /* we send the user input to our pseudowin */
303 ibuf = fore->w_pwin->p_inbuf; ilen = &fore->w_pwin->p_inlen;
304 f = sizeof(fore->w_pwin->p_inbuf) - *ilen;
306 else
307 #endif /* PSEUDOS */
309 /* we send the user input to the window */
310 ibuf = fore->w_inbuf; ilen = &fore->w_inlen;
311 f = sizeof(fore->w_inbuf) - *ilen;
314 if (l > f)
315 l = f;
316 #ifdef BUILTIN_TELNET
317 while (l > 0)
318 #else
319 if (l > 0)
320 #endif
322 l2 = l;
323 bcopy(*bufpp, ibuf + *ilen, l2);
324 if (fore->w_autolf && (trunc = DoAutolf(ibuf + *ilen, &l2, f - l2)))
325 l -= trunc;
326 #ifdef BUILTIN_TELNET
327 if (fore->w_type == W_TYPE_TELNET && (trunc = DoTelnet(ibuf + *ilen, &l2, f - l2)))
329 l -= trunc;
330 if (fore->w_autolf)
331 continue; /* need exact value */
333 #endif
334 *ilen += l2;
335 *bufpp += l;
336 *lenp -= l;
337 return;
341 static void
342 ZombieProcess(bufpp, lenp)
343 char **bufpp;
344 int *lenp;
346 int l = *lenp;
347 char *buf = *bufpp, b1[10], b2[10];
349 debug1("ZombieProcess: %d bytes\n", *lenp);
350 fore = (struct win *)flayer->l_data;
352 ASSERT(fore->w_ptyfd < 0);
353 *bufpp += *lenp;
354 *lenp = 0;
355 for (; l-- > 0; buf++)
357 if (*(unsigned char *)buf == ZombieKey_destroy)
359 debug1("Turning undead: %d\n", fore->w_number);
360 KillWindow(fore);
361 return;
363 if (*(unsigned char *)buf == ZombieKey_resurrect)
365 debug1("Resurrecting Zombie: %d\n", fore->w_number);
366 WriteString(fore, "\r\n", 2);
367 RemakeWindow(fore);
368 return;
371 b1[AddXChar(b1, ZombieKey_destroy)] = '\0';
372 b2[AddXChar(b2, ZombieKey_resurrect)] = '\0';
373 Msg(0, "Press %s to destroy or %s to resurrect window", b1, b2);
376 static void
377 WinRedisplayLine(y, from, to, isblank)
378 int y, from, to, isblank;
380 debug3("WinRedisplayLine %d %d %d\n", y, from, to);
381 if (y < 0)
382 return;
383 fore = (struct win *)flayer->l_data;
384 if (from == 0 && y > 0 && fore->w_mlines[y - 1].image[fore->w_width] == 0)
385 LCDisplayLineWrap(&fore->w_layer, &fore->w_mlines[y], y, from, to, isblank);
386 else
387 LCDisplayLine(&fore->w_layer, &fore->w_mlines[y], y, from, to, isblank);
390 static int
391 WinRewrite(y, x1, x2, rend, doit)
392 int y, x1, x2, doit;
393 struct mchar *rend;
395 register int cost, dx;
396 register unsigned char *p, *i;
397 #ifdef FONT
398 register unsigned char *f;
399 #endif
400 #ifdef COLOR
401 register unsigned char *c;
402 # ifdef COLORS256
403 register unsigned char *cx;
404 # endif
405 #endif
407 debug3("WinRewrite %d, %d-%d\n", y, x1, x2);
408 fore = (struct win *)flayer->l_data;
409 dx = x2 - x1 + 1;
410 if (doit)
412 i = fore->w_mlines[y].image + x1;
413 while (dx-- > 0)
414 PUTCHAR(*i++);
415 return 0;
417 p = fore->w_mlines[y].attr + x1;
418 #ifdef FONT
419 f = fore->w_mlines[y].font + x1;
420 # ifdef DW_CHARS
421 if (is_dw_font(rend->font))
422 return EXPENSIVE;
423 # endif
424 # ifdef UTF8
425 if (fore->w_encoding && fore->w_encoding != UTF8 && D_encoding == UTF8 && ContainsSpecialDeffont(fore->w_mlines + y, x1, x2, fore->w_encoding))
426 return EXPENSIVE;
427 # endif
428 #endif
429 #ifdef COLOR
430 c = fore->w_mlines[y].color + x1;
431 # ifdef COLORS256
432 cx = fore->w_mlines[y].colorx + x1;
433 # endif
434 #endif
436 cost = dx = x2 - x1 + 1;
437 while(dx-- > 0)
439 if (*p++ != rend->attr)
440 return EXPENSIVE;
441 #ifdef FONT
442 if (*f++ != rend->font)
443 return EXPENSIVE;
444 #endif
445 #ifdef COLOR
446 if (*c++ != rend->color)
447 return EXPENSIVE;
448 # ifdef COLORS256
449 if (*cx++ != rend->colorx)
450 return EXPENSIVE;
451 # endif
452 #endif
454 return cost;
457 static void
458 WinClearLine(y, xs, xe, bce)
459 int y, xs, xe, bce;
461 fore = (struct win *)flayer->l_data;
462 debug3("WinClearLine %d %d-%d\n", y, xs, xe);
463 LClearLine(flayer, y, xs, xe, bce, &fore->w_mlines[y]);
466 static int
467 WinResize(wi, he)
468 int wi, he;
470 fore = (struct win *)flayer->l_data;
471 ChangeWindowSize(fore, wi, he, fore->w_histheight);
472 return 0;
475 static void
476 WinRestore()
478 struct canvas *cv;
479 fore = (struct win *)flayer->l_data;
480 debug1("WinRestore: win %p\n", fore);
481 for (cv = flayer->l_cvlist; cv; cv = cv->c_next)
483 display = cv->c_display;
484 if (cv != D_forecv)
485 continue;
486 /* ChangeScrollRegion(fore->w_top, fore->w_bot); */
487 KeypadMode(fore->w_keypad);
488 CursorkeysMode(fore->w_cursorkeys);
489 SetFlow(fore->w_flow & FLOW_NOW);
490 InsertMode(fore->w_insert);
491 ReverseVideo(fore->w_revvid);
492 CursorVisibility(fore->w_curinv ? -1 : fore->w_curvvis);
493 MouseMode(fore->w_mouse);
497 /*****************************************************************/
501 * DoStartLog constructs a path for the "want to be logfile" in buf and
502 * attempts logfopen.
504 * returns 0 on success.
507 DoStartLog(w, buf, bufsize)
508 struct win *w;
509 char *buf;
510 int bufsize;
512 int n;
513 if (!w || !buf)
514 return -1;
516 strncpy(buf, MakeWinMsg(screenlogfile, w, '%'), bufsize - 1);
517 buf[bufsize - 1] = 0;
519 debug2("DoStartLog: win %d, file %s\n", w->w_number, buf);
521 if (w->w_log != NULL)
522 logfclose(w->w_log);
524 if ((w->w_log = logfopen(buf, islogfile(buf) ? NULL : secfopen(buf, "a"))) == NULL)
525 return -2;
526 if (!logflushev.queued)
528 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
529 if (n)
531 SetTimeout(&logflushev, n * 1000);
532 evenq(&logflushev);
535 return 0;
539 * Umask & wlock are set for the user of the display,
540 * The display d (if specified) switches to that window.
543 MakeWindow(newwin)
544 struct NewWindow *newwin;
546 register struct win **pp, *p;
547 register int n, i;
548 int f = -1;
549 struct NewWindow nwin;
550 int type, startat;
551 char *TtyName;
552 #ifdef MULTIUSER
553 extern struct acluser *users;
554 #endif
556 debug1("NewWindow: StartAt %d\n", newwin->StartAt);
557 debug1("NewWindow: aka %s\n", newwin->aka?newwin->aka:"NULL");
558 debug1("NewWindow: dir %s\n", newwin->dir?newwin->dir:"NULL");
559 debug1("NewWindow: term %s\n", newwin->term?newwin->term:"NULL");
561 nwin_compose(&nwin_default, newwin, &nwin);
562 debug1("NWin: aka %s\n", nwin.aka ? nwin.aka : "NULL");
563 debug1("NWin: wlock %d\n", nwin.wlock);
564 debug1("NWin: Lflag %d\n", nwin.Lflag);
566 startat = nwin.StartAt < maxwin ? nwin.StartAt : 0;
567 pp = wtab + startat;
571 if (*pp == 0)
572 break;
573 if (++pp == wtab + maxwin)
574 pp = wtab;
576 while (pp != wtab + startat);
577 if (*pp)
579 Msg(0, "No more windows.");
580 return -1;
583 #if defined(USRLIMIT) && defined(UTMPOK)
585 * Count current number of users, if logging windows in.
587 if (nwin.lflag && CountUsers() >= USRLIMIT)
589 Msg(0, "User limit reached. Window will not be logged in.");
590 nwin.lflag = 0;
592 #endif
593 n = pp - wtab;
594 debug1("Makewin creating %d\n", n);
596 if ((f = OpenDevice(nwin.args, nwin.lflag, &type, &TtyName)) < 0)
597 return -1;
598 if (type == W_TYPE_GROUP)
599 f = -1;
601 if ((p = (struct win *)calloc(1, sizeof(struct win))) == 0)
603 close(f);
604 Msg(0, strnomem);
605 return -1;
608 #ifdef UTMPOK
609 if (type != W_TYPE_PTY)
610 nwin.lflag = 0;
611 #endif
613 p->w_type = type;
615 /* save the command line so that zombies can be resurrected */
616 for (i = 0; nwin.args[i] && i < MAXARGS - 1; i++)
617 p->w_cmdargs[i] = SaveStr(nwin.args[i]);
618 p->w_cmdargs[i] = 0;
619 if (nwin.dir)
620 p->w_dir = SaveStr(nwin.dir);
621 if (nwin.term)
622 p->w_term = SaveStr(nwin.term);
624 p->w_number = n;
625 p->w_group = 0;
626 if (fore && fore->w_type == W_TYPE_GROUP)
627 p->w_group = fore;
628 else if (fore && fore->w_group)
629 p->w_group = fore->w_group;
630 #ifdef MULTIUSER
632 * This is dangerous: without a display we use creators umask
633 * This is intended to be usefull for detached startup.
634 * But is still better than default bits with a NULL user.
636 if (NewWindowAcl(p, display ? D_user : users))
638 free((char *)p);
639 close(f);
640 Msg(0, strnomem);
641 return -1;
643 #endif
644 p->w_layer.l_next = 0;
645 p->w_layer.l_bottom = &p->w_layer;
646 p->w_layer.l_layfn = &WinLf;
647 p->w_layer.l_data = (char *)p;
648 p->w_savelayer = &p->w_layer;
649 p->w_pdisplay = 0;
650 p->w_lastdisp = 0;
652 #ifdef MULTIUSER
653 if (display && !AclCheckPermWin(D_user, ACL_WRITE, p))
654 p->w_wlockuser = D_user;
655 p->w_wlock = nwin.wlock;
656 #endif
657 p->w_ptyfd = f;
658 p->w_aflag = nwin.aflag;
659 p->w_flow = nwin.flowflag | ((nwin.flowflag & FLOW_AUTOFLAG) ? (FLOW_AUTO|FLOW_NOW) : FLOW_AUTO);
660 if (!nwin.aka)
661 nwin.aka = Filename(nwin.args[0]);
662 strncpy(p->w_akabuf, nwin.aka, sizeof(p->w_akabuf) - 1);
663 if ((nwin.aka = rindex(p->w_akabuf, '|')) != NULL)
665 p->w_autoaka = 0;
666 *nwin.aka++ = 0;
667 p->w_title = nwin.aka;
668 p->w_akachange = nwin.aka + strlen(nwin.aka);
670 else
671 p->w_title = p->w_akachange = p->w_akabuf;
672 if (nwin.hstatus)
673 p->w_hstatus = SaveStr(nwin.hstatus);
674 p->w_monitor = nwin.monitor;
675 #ifdef MULTIUSER
676 if (p->w_monitor == MON_ON)
678 /* always tell all users */
679 for (i = 0; i < maxusercount; i++)
680 ACLBYTE(p->w_mon_notify, i) |= ACLBIT(i);
682 #endif
684 * defsilence by Lloyd Zusman (zusman_lloyd@jpmorgan.com)
686 p->w_silence = nwin.silence;
687 p->w_silencewait = SilenceWait;
688 #ifdef MULTIUSER
689 if (p->w_silence == SILENCE_ON)
691 /* always tell all users */
692 for (i = 0; i < maxusercount; i++)
693 ACLBYTE(p->w_lio_notify, i) |= ACLBIT(i);
695 #endif
696 #ifdef COPY_PASTE
697 p->w_slowpaste = nwin.slow;
698 #else
699 nwin.histheight = 0;
700 #endif
702 p->w_norefresh = 0;
703 strncpy(p->w_tty, TtyName, MAXSTR - 1);
705 #if 0
706 /* XXX Fixme display resize */
707 if (ChangeWindowSize(p, display ? D_defwidth : 80,
708 display ? D_defheight : 24,
709 nwin.histheight))
711 FreeWindow(p);
712 return -1;
714 #else
715 if (ChangeWindowSize(p, display ? D_forecv->c_xe - D_forecv->c_xs + 1: 80,
716 display ? D_forecv->c_ye - D_forecv->c_ys + 1 : 24,
717 nwin.histheight))
719 FreeWindow(p);
720 return -1;
722 #endif
724 p->w_encoding = nwin.encoding;
725 ResetWindow(p); /* sets w_wrap, w_c1, w_gr, w_bce */
727 #ifdef FONT
728 if (nwin.charset)
729 SetCharsets(p, nwin.charset);
730 #endif
732 if (VerboseCreate && type != W_TYPE_GROUP)
734 struct display *d = display; /* WriteString zaps display */
736 WriteString(p, ":screen (", 9);
737 WriteString(p, p->w_title, strlen(p->w_title));
738 WriteString(p, "):", 2);
739 for (f = 0; p->w_cmdargs[f]; f++)
741 WriteString(p, " ", 1);
742 WriteString(p, p->w_cmdargs[f], strlen(p->w_cmdargs[f]));
744 WriteString(p, "\r\n", 2);
745 display = d;
748 p->w_deadpid = 0;
749 p->w_pid = 0;
750 #ifdef PSEUDOS
751 p->w_pwin = 0;
752 #endif
754 #ifdef BUILTIN_TELNET
755 if (type == W_TYPE_TELNET)
757 if (TelConnect(p))
759 FreeWindow(p);
760 return -1;
763 else
764 #endif
765 if (type == W_TYPE_PTY)
767 p->w_pid = ForkWindow(p, nwin.args, TtyName);
768 if (p->w_pid < 0)
770 FreeWindow(p);
771 return -1;
776 * Place the new window at the head of the most-recently-used list.
778 if (display && D_fore)
779 D_other = D_fore;
780 *pp = p;
781 p->w_next = windows;
782 windows = p;
784 if (type == W_TYPE_GROUP)
786 SetForeWindow(p);
787 Activate(p->w_norefresh);
788 WindowChanged((struct win*)0, 'w');
789 WindowChanged((struct win*)0, 'W');
790 WindowChanged((struct win*)0, 0);
791 return n;
794 p->w_lflag = nwin.lflag;
795 #ifdef UTMPOK
796 p->w_slot = (slot_t)-1;
797 # ifdef LOGOUTOK
798 debug1("MakeWindow will %slog in.\n", nwin.lflag?"":"not ");
799 if (nwin.lflag & 1)
800 # else /* LOGOUTOK */
801 debug1("MakeWindow will log in, LOGOUTOK undefined in config.h%s.\n",
802 nwin.lflag?"":" (although lflag=0)");
803 # endif /* LOGOUTOK */
805 p->w_slot = (slot_t)0;
806 if (display || (p->w_lflag & 2))
807 SetUtmp(p);
809 # ifdef CAREFULUTMP
810 CarefulUtmp(); /* If all 've been zombies, we've had no slot */
811 # endif
812 #endif /* UTMPOK */
814 if (nwin.Lflag)
816 char buf[1024];
817 DoStartLog(p, buf, sizeof(buf));
820 p->w_readev.fd = p->w_writeev.fd = p->w_ptyfd;
821 p->w_readev.type = EV_READ;
822 p->w_writeev.type = EV_WRITE;
823 p->w_readev.data = p->w_writeev.data = (char *)p;
824 p->w_readev.handler = win_readev_fn;
825 p->w_writeev.handler = win_writeev_fn;
826 p->w_writeev.condpos = &p->w_inlen;
827 evenq(&p->w_readev);
828 evenq(&p->w_writeev);
829 #ifdef COPY_PASTE
830 p->w_paster.pa_slowev.type = EV_TIMEOUT;
831 p->w_paster.pa_slowev.data = (char *)&p->w_paster;
832 p->w_paster.pa_slowev.handler = paste_slowev_fn;
833 #endif
834 p->w_silenceev.type = EV_TIMEOUT;
835 p->w_silenceev.data = (char *)p;
836 p->w_silenceev.handler = win_silenceev_fn;
837 if (p->w_silence > 0)
839 debug("New window has silence enabled.\n");
840 SetTimeout(&p->w_silenceev, p->w_silencewait * 1000);
841 evenq(&p->w_silenceev);
844 SetForeWindow(p);
845 Activate(p->w_norefresh);
846 WindowChanged((struct win*)0, 'w');
847 WindowChanged((struct win*)0, 'W');
848 WindowChanged((struct win*)0, 0);
849 return n;
853 * Resurrect a window from Zombie state.
854 * The command vector is therefore stored in the window structure.
855 * Note: The terminaltype defaults to screenterm again, the current
856 * working directory is lost.
859 RemakeWindow(p)
860 struct win *p;
862 char *TtyName;
863 int lflag, f;
865 lflag = nwin_default.lflag;
866 if ((f = OpenDevice(p->w_cmdargs, lflag, &p->w_type, &TtyName)) < 0)
867 return -1;
869 strncpy(p->w_tty, *TtyName ? TtyName : p->w_title, MAXSTR - 1);
870 p->w_ptyfd = f;
871 p->w_readev.fd = f;
872 p->w_writeev.fd = f;
873 evenq(&p->w_readev);
874 evenq(&p->w_writeev);
876 if (VerboseCreate)
878 struct display *d = display; /* WriteString zaps display */
880 WriteString(p, ":screen (", 9);
881 WriteString(p, p->w_title, strlen(p->w_title));
882 WriteString(p, "):", 2);
883 for (f = 0; p->w_cmdargs[f]; f++)
885 WriteString(p, " ", 1);
886 WriteString(p, p->w_cmdargs[f], strlen(p->w_cmdargs[f]));
888 WriteString(p, "\r\n", 2);
889 display = d;
892 p->w_deadpid = 0;
893 p->w_pid = 0;
894 #ifdef BUILTIN_TELNET
895 if (p->w_type == W_TYPE_TELNET)
897 if (TelConnect(p))
898 return -1;
900 else
901 #endif
902 if (p->w_type == W_TYPE_PTY)
904 p->w_pid = ForkWindow(p, p->w_cmdargs, TtyName);
905 if (p->w_pid < 0)
906 return -1;
909 #ifdef UTMPOK
910 if (p->w_slot == (slot_t)0 && (display || (p->w_lflag & 2)))
911 SetUtmp(p);
912 # ifdef CAREFULUTMP
913 CarefulUtmp(); /* If all 've been zombies, we've had no slot */
914 # endif
915 #endif
916 WindowChanged(p, 'f');
917 return p->w_number;
920 void
921 CloseDevice(wp)
922 struct win *wp;
924 if (wp->w_ptyfd < 0)
925 return;
926 if (wp->w_type == W_TYPE_PTY)
928 /* pty 4 SALE */
929 (void)chmod(wp->w_tty, 0666);
930 (void)chown(wp->w_tty, 0, 0);
932 close(wp->w_ptyfd);
933 wp->w_ptyfd = -1;
934 wp->w_tty[0] = 0;
935 evdeq(&wp->w_readev);
936 evdeq(&wp->w_writeev);
937 #ifdef BUILTIN_TELNET
938 evdeq(&wp->w_telconnev);
939 #endif
940 wp->w_readev.fd = wp->w_writeev.fd = -1;
943 void
944 FreeWindow(wp)
945 struct win *wp;
947 struct display *d;
948 int i;
949 struct canvas *cv, *ncv;
950 struct layer *l;
952 debug1("FreeWindow %d\n", wp ? wp->w_number: -1);
954 #ifdef SCRIPT
955 trigger_sevent(&wp->w_sev.onclose, wp);
956 #endif
958 #ifdef PSEUDOS
959 if (wp->w_pwin)
960 FreePseudowin(wp);
961 #endif
962 #ifdef UTMPOK
963 RemoveUtmp(wp);
964 #endif
965 CloseDevice(wp);
967 if (wp == console_window)
969 TtyGrabConsole(-1, -1, "free");
970 console_window = 0;
972 if (wp->w_log != NULL)
973 logfclose(wp->w_log);
974 ChangeWindowSize(wp, 0, 0, 0);
976 if (wp->w_type == W_TYPE_GROUP)
978 struct win *win;
979 for (win = windows; win; win = win->w_next)
980 if (win->w_group == wp)
981 win->w_group = wp->w_group;
984 if (wp->w_hstatus)
985 free(wp->w_hstatus);
986 for (i = 0; wp->w_cmdargs[i]; i++)
987 free(wp->w_cmdargs[i]);
988 if (wp->w_dir)
989 free(wp->w_dir);
990 if (wp->w_term)
991 free(wp->w_term);
992 for (d = displays; d; d = d->d_next)
994 if (d->d_other == wp)
995 d->d_other = d->d_fore && d->d_fore->w_next != wp ? d->d_fore->w_next : wp->w_next;
996 if (d->d_fore == wp)
997 d->d_fore = NULL;
998 for (cv = d->d_cvlist; cv; cv = cv->c_next)
1000 for (l = cv->c_layer; l; l = l->l_next)
1001 if (l->l_layfn == &WinLf)
1002 break;
1003 if (!l)
1004 continue;
1005 if ((struct win *)l->l_data != wp)
1006 continue;
1007 if (cv->c_layer == wp->w_savelayer)
1008 wp->w_savelayer = 0;
1009 KillLayerChain(cv->c_layer);
1012 if (wp->w_savelayer)
1013 KillLayerChain(wp->w_savelayer);
1014 for (cv = wp->w_layer.l_cvlist; cv; cv = ncv)
1016 ncv = cv->c_lnext;
1017 cv->c_layer = &cv->c_blank;
1018 cv->c_blank.l_cvlist = cv;
1019 cv->c_lnext = 0;
1020 cv->c_xoff = cv->c_xs;
1021 cv->c_yoff = cv->c_ys;
1022 RethinkViewportOffsets(cv);
1024 wp->w_layer.l_cvlist = 0;
1025 if (flayer == &wp->w_layer)
1026 flayer = 0;
1028 #ifdef MULTIUSER
1029 FreeWindowAcl(wp);
1030 #endif /* MULTIUSER */
1031 evdeq(&wp->w_readev); /* just in case */
1032 evdeq(&wp->w_writeev); /* just in case */
1033 evdeq(&wp->w_silenceev);
1034 #ifdef COPY_PASTE
1035 FreePaster(&wp->w_paster);
1036 #endif
1037 free((char *)wp);
1040 static int
1041 OpenDevice(args, lflag, typep, namep)
1042 char **args;
1043 int lflag;
1044 int *typep;
1045 char **namep;
1047 char *arg = args[0];
1048 struct stat st;
1049 int f;
1051 if (!arg)
1052 return -1;
1053 if (strcmp(arg, "//group") == 0)
1055 *typep = W_TYPE_GROUP;
1056 *namep = "telnet";
1057 return 0;
1059 #ifdef BUILTIN_TELNET
1060 if (strcmp(arg, "//telnet") == 0)
1062 f = TelOpen(args + 1);
1063 lflag = 0;
1064 *typep = W_TYPE_TELNET;
1065 *namep = "telnet";
1067 else
1068 #endif
1069 if (strncmp(arg, "//", 2) == 0)
1071 Msg(0, "Invalid argument '%s'", arg);
1072 return -1;
1074 else if ((stat(arg, &st)) == 0 && S_ISCHR(st.st_mode))
1076 if (access(arg, R_OK | W_OK) == -1)
1078 Msg(errno, "Cannot access line '%s' for R/W", arg);
1079 return -1;
1081 debug("OpenDevice: OpenTTY\n");
1082 if ((f = OpenTTY(arg, args[1])) < 0)
1083 return -1;
1084 lflag = 0;
1085 *typep = W_TYPE_PLAIN;
1086 *namep = arg;
1088 else
1090 *typep = W_TYPE_PTY;
1091 f = OpenPTY(namep);
1092 if (f == -1)
1094 Msg(0, "No more PTYs.");
1095 return -1;
1097 #ifdef TIOCPKT
1099 int flag = 1;
1100 if (ioctl(f, TIOCPKT, (char *)&flag))
1102 Msg(errno, "TIOCPKT ioctl");
1103 close(f);
1104 return -1;
1107 #endif /* TIOCPKT */
1109 debug1("fcntl(%d, F_SETFL, FNBLOCK)\n", f);
1110 (void) fcntl(f, F_SETFL, FNBLOCK);
1111 #ifdef linux
1113 * Tenebreux (zeus@ns.acadiacom.net) has Linux 1.3.70 where select
1114 * gets confused in the following condition:
1115 * Open a pty-master side, request a flush on it, then set packet
1116 * mode and call select(). Select will return a possible read, where
1117 * the one byte response to the flush can be found. Select will
1118 * thereafter return a possible read, which yields I/O error.
1120 * If we request another flush *after* switching into packet mode,
1121 * this I/O error does not occur. We receive a single response byte
1122 * although we send two flush requests now.
1124 * Maybe we should not flush at all.
1126 * 10.5.96 jw.
1128 if (*typep == W_TYPE_PTY || *typep == W_TYPE_PLAIN)
1129 tcflush(f, TCIOFLUSH);
1130 #endif
1132 if (*typep != W_TYPE_PTY)
1133 return f;
1135 #ifndef PTYROFS
1136 #ifdef PTYGROUP
1137 if (chown(*namep, real_uid, PTYGROUP) && !eff_uid)
1138 #else
1139 if (chown(*namep, real_uid, real_gid) && !eff_uid)
1140 #endif
1142 Msg(errno, "chown tty");
1143 close(f);
1144 return -1;
1146 #ifdef UTMPOK
1147 if (chmod(*namep, lflag ? TtyMode : (TtyMode & ~022)) && !eff_uid)
1148 #else
1149 if (chmod(*namep, TtyMode) && !eff_uid)
1150 #endif
1152 Msg(errno, "chmod tty");
1153 close(f);
1154 return -1;
1156 #endif
1157 return f;
1161 * Fields w_width, w_height, aflag, number (and w_tty)
1162 * are read from struct win *win. No fields written.
1163 * If pwin is nonzero, filedescriptors are distributed
1164 * between win->w_tty and open(ttyn)
1167 static int
1168 ForkWindow(win, args, ttyn)
1169 struct win *win;
1170 char **args, *ttyn;
1172 int pid;
1173 char tebuf[25];
1174 char ebuf[20];
1175 char shellbuf[7 + MAXPATHLEN];
1176 char *proc;
1177 #ifndef TIOCSWINSZ
1178 char libuf[20], cobuf[20];
1179 #endif
1180 int newfd;
1181 int w = win->w_width;
1182 int h = win->w_height;
1183 #ifdef PSEUDOS
1184 int i, pat, wfdused;
1185 struct pseudowin *pwin = win->w_pwin;
1186 #endif
1187 int slave = -1;
1189 #ifdef O_NOCTTY
1190 if (pty_preopen)
1192 debug("pre-opening slave...\n");
1193 if ((slave = open(ttyn, O_RDWR|O_NOCTTY)) == -1)
1195 Msg(errno, "ttyn");
1196 return -1;
1199 #endif
1200 debug("forking...\n");
1201 proc = *args;
1202 if (proc == 0)
1204 args = ShellArgs;
1205 proc = *args;
1207 fflush(stdout);
1208 fflush(stderr);
1209 switch (pid = fork())
1211 case -1:
1212 Msg(errno, "fork");
1213 break;
1214 case 0:
1215 signal(SIGHUP, SIG_DFL);
1216 signal(SIGINT, SIG_DFL);
1217 signal(SIGQUIT, SIG_DFL);
1218 signal(SIGTERM, SIG_DFL);
1219 #ifdef BSDJOBS
1220 signal(SIGTTIN, SIG_DFL);
1221 signal(SIGTTOU, SIG_DFL);
1222 #endif
1223 #ifdef SIGPIPE
1224 signal(SIGPIPE, SIG_DFL);
1225 #endif
1226 #ifdef SIGXFSZ
1227 signal(SIGXFSZ, SIG_DFL);
1228 #endif
1230 displays = 0; /* beware of Panic() */
1231 if (setgid(real_gid) || setuid(real_uid))
1232 Panic(errno, "Setuid/gid");
1233 eff_uid = real_uid;
1234 eff_gid = real_gid;
1235 #ifdef PSEUDOS
1236 if (!pwin) /* ignore directory if pseudo */
1237 #endif
1238 if (win->w_dir && *win->w_dir && chdir(win->w_dir))
1239 Panic(errno, "Cannot chdir to %s", win->w_dir);
1241 if (display)
1243 brktty(D_userfd);
1244 freetty();
1246 else
1247 brktty(-1);
1248 #ifdef DEBUG
1249 if (dfp && dfp != stderr)
1250 fclose(dfp);
1251 #endif
1252 if (slave != -1)
1254 close(0);
1255 dup(slave);
1256 close(slave);
1257 closeallfiles(win->w_ptyfd);
1258 slave = dup(0);
1260 else
1261 closeallfiles(win->w_ptyfd);
1262 #ifdef DEBUG
1263 if (dfp) /* do not produce child debug, when debug is "off" */
1265 char buf[256];
1267 sprintf(buf, "%s/screen.child", DEBUGDIR);
1268 if ((dfp = fopen(buf, "a")) == 0)
1269 dfp = stderr;
1270 else
1271 (void) chmod(buf, 0666);
1273 debug1("=== ForkWindow: pid %d\n", (int)getpid());
1274 #endif
1275 /* Close the three /dev/null descriptors */
1276 close(0);
1277 close(1);
1278 close(2);
1279 newfd = -1;
1281 * distribute filedescriptors between the ttys
1283 #ifdef PSEUDOS
1284 pat = pwin ? pwin->p_fdpat :
1285 ((F_PFRONT<<(F_PSHIFT*2)) | (F_PFRONT<<F_PSHIFT) | F_PFRONT);
1286 debug1("Using window pattern 0x%x\n", pat);
1287 wfdused = 0;
1288 for(i = 0; i < 3; i++)
1290 if (pat & F_PFRONT << F_PSHIFT * i)
1292 if (newfd < 0)
1294 # ifdef O_NOCTTY
1295 if (separate_sids)
1296 newfd = open(ttyn, O_RDWR);
1297 else
1298 newfd = open(ttyn, O_RDWR|O_NOCTTY);
1299 # else
1300 newfd = open(ttyn, O_RDWR);
1301 # endif
1302 if (newfd < 0)
1303 Panic(errno, "Cannot open %s", ttyn);
1305 else
1306 dup(newfd);
1308 else
1310 dup(win->w_ptyfd);
1311 wfdused = 1;
1314 if (wfdused)
1317 * the pseudo window process should not be surprised with a
1318 * nonblocking filedescriptor. Poor Backend!
1320 debug1("Clearing NBLOCK on window-fd(%d)\n", win->w_ptyfd);
1321 if (fcntl(win->w_ptyfd, F_SETFL, 0))
1322 Msg(errno, "Warning: clear NBLOCK fcntl failed");
1324 #else /* PSEUDOS */
1325 # ifdef O_NOCTTY
1326 if (separate_sids)
1327 newfd = open(ttyn, O_RDWR);
1328 else
1329 newfd = open(ttyn, O_RDWR|O_NOCTTY);
1330 # else
1331 newfd = open(ttyn, O_RDWR);
1332 # endif
1333 if (newfd != 0)
1334 Panic(errno, "Cannot open %s", ttyn);
1335 dup(0);
1336 dup(0);
1337 #endif /* PSEUDOS */
1338 close(win->w_ptyfd);
1339 if (slave != -1)
1340 close(slave);
1341 if (newfd >= 0)
1343 struct mode fakemode, *modep;
1344 InitPTY(newfd);
1345 if (fgtty(newfd))
1346 Msg(errno, "fgtty");
1347 if (display)
1349 debug("ForkWindow: using display tty mode for new child.\n");
1350 modep = &D_OldMode;
1352 else
1354 debug("No display - creating tty setting\n");
1355 modep = &fakemode;
1356 InitTTY(modep, 0);
1357 #ifdef DEBUG
1358 DebugTTY(modep);
1359 #endif
1361 /* We only want echo if the users input goes to the pseudo
1362 * and the pseudo's stdout is not send to the window.
1364 #ifdef PSEUDOS
1365 if (pwin && (!(pat & F_UWP) || (pat & F_PBACK << F_PSHIFT)))
1367 debug1("clearing echo on pseudywin fd (pat %x)\n", pat);
1368 # if defined(POSIX) || defined(TERMIO)
1369 modep->tio.c_lflag &= ~ECHO;
1370 modep->tio.c_iflag &= ~ICRNL;
1371 # else
1372 modep->m_ttyb.sg_flags &= ~ECHO;
1373 # endif
1375 #endif
1376 SetTTY(newfd, modep);
1377 #ifdef TIOCSWINSZ
1378 glwz.ws_col = w;
1379 glwz.ws_row = h;
1380 (void) ioctl(newfd, TIOCSWINSZ, (char *)&glwz);
1381 #endif
1382 /* Always turn off nonblocking mode */
1383 (void)fcntl(newfd, F_SETFL, 0);
1385 #ifndef TIOCSWINSZ
1386 sprintf(libuf, "LINES=%d", h);
1387 sprintf(cobuf, "COLUMNS=%d", w);
1388 NewEnv[5] = libuf;
1389 NewEnv[6] = cobuf;
1390 #endif
1391 #ifdef MAPKEYS
1392 NewEnv[2] = MakeTermcap(display == 0 || win->w_aflag);
1393 #else
1394 if (win->w_aflag)
1395 NewEnv[2] = MakeTermcap(1);
1396 else
1397 NewEnv[2] = Termcap;
1398 #endif
1399 strcpy(shellbuf, "SHELL=");
1400 strncpy(shellbuf + 6, ShellProg + (*ShellProg == '-'), sizeof(shellbuf) - 7);
1401 shellbuf[sizeof(shellbuf) - 1] = 0;
1402 NewEnv[4] = shellbuf;
1403 debug1("ForkWindow: NewEnv[4] = '%s'\n", shellbuf);
1404 if (win->w_term && *win->w_term && strcmp(screenterm, win->w_term) &&
1405 (strlen(win->w_term) < 20))
1407 char *s1, *s2, tl;
1409 sprintf(tebuf, "TERM=%s", win->w_term);
1410 debug2("Makewindow %d with %s\n", win->w_number, tebuf);
1411 tl = strlen(win->w_term);
1412 NewEnv[1] = tebuf;
1413 if ((s1 = index(NewEnv[2], '|')))
1415 if ((s2 = index(++s1, '|')))
1417 if (strlen(NewEnv[2]) - (s2 - s1) + tl < 1024)
1419 bcopy(s2, s1 + tl, strlen(s2) + 1);
1420 bcopy(win->w_term, s1, tl);
1425 sprintf(ebuf, "WINDOW=%d", win->w_number);
1426 NewEnv[3] = ebuf;
1428 if (*proc == '-')
1429 proc++;
1430 if (!*proc)
1431 proc = DefaultShell;
1432 debug1("calling execvpe %s\n", proc);
1433 execvpe(proc, args, NewEnv);
1434 debug1("exec error: %d\n", errno);
1435 Panic(errno, "Cannot exec '%s'", proc);
1436 default:
1437 break;
1439 if (slave != -1)
1440 close(slave);
1441 return pid;
1444 void
1445 execvpe(prog, args, env)
1446 char *prog, **args, **env;
1448 register char *path = NULL, *p;
1449 char buf[1024];
1450 char *shargs[MAXARGS + 1];
1451 register int i, eaccess = 0;
1453 if (rindex(prog, '/'))
1454 path = "";
1455 if (!path && !(path = getenv("PATH")))
1456 path = DefaultPath;
1459 for (p = buf; *path && *path != ':'; path++)
1460 if (p - buf < (int)sizeof(buf) - 2)
1461 *p++ = *path;
1462 if (p > buf)
1463 *p++ = '/';
1464 if (p - buf + strlen(prog) >= sizeof(buf) - 1)
1465 continue;
1466 strcpy(p, prog);
1467 execve(buf, args, env);
1468 switch (errno)
1470 case ENOEXEC:
1471 shargs[0] = DefaultShell;
1472 shargs[1] = buf;
1473 for (i = 1; (shargs[i + 1] = args[i]) != NULL; ++i)
1475 execve(DefaultShell, shargs, env);
1476 return;
1477 case EACCES:
1478 eaccess = 1;
1479 break;
1480 case ENOMEM:
1481 case E2BIG:
1482 case ETXTBSY:
1483 return;
1485 } while (*path++);
1486 if (eaccess)
1487 errno = EACCES;
1490 #ifdef PSEUDOS
1493 winexec(av)
1494 char **av;
1496 char **pp;
1497 char *p, *s, *t;
1498 int i, r = 0, l = 0;
1499 struct win *w;
1500 extern struct display *display;
1501 extern struct win *windows;
1502 struct pseudowin *pwin;
1503 int type;
1505 if ((w = display ? fore : windows) == NULL)
1506 return -1;
1507 if (!*av || w->w_pwin)
1509 Msg(0, "Filter running: %s", w->w_pwin ? w->w_pwin->p_cmd : "(none)");
1510 return -1;
1512 if (w->w_ptyfd < 0)
1514 Msg(0, "You feel dead inside.");
1515 return -1;
1517 if (!(pwin = (struct pseudowin *)calloc(1, sizeof(struct pseudowin))))
1519 Msg(0, strnomem);
1520 return -1;
1523 /* allow ^a:!!./ttytest as a short form for ^a:exec !.. ./ttytest */
1524 for (s = *av; *s == ' '; s++)
1526 for (p = s; *p == ':' || *p == '.' || *p == '!'; p++)
1528 if (*p != '|')
1529 while (*p && p > s && p[-1] == '.')
1530 p--;
1531 if (*p == '|')
1533 l = F_UWP;
1534 p++;
1536 if (*p)
1537 av[0] = p;
1538 else
1539 av++;
1541 t = pwin->p_cmd;
1542 for (i = 0; i < 3; i++)
1544 *t = (s < p) ? *s++ : '.';
1545 switch (*t++)
1547 case '.':
1548 case '|':
1549 l |= F_PFRONT << (i * F_PSHIFT);
1550 break;
1551 case '!':
1552 l |= F_PBACK << (i * F_PSHIFT);
1553 break;
1554 case ':':
1555 l |= F_PBOTH << (i * F_PSHIFT);
1556 break;
1560 if (l & F_UWP)
1562 *t++ = '|';
1563 if ((l & F_PMASK) == F_PFRONT)
1565 *pwin->p_cmd = '!';
1566 l ^= F_PFRONT | F_PBACK;
1569 if (!(l & F_PBACK))
1570 l |= F_UWP;
1571 *t++ = ' ';
1572 pwin->p_fdpat = l;
1573 debug1("winexec: '%#x'\n", pwin->p_fdpat);
1575 l = MAXSTR - 4;
1576 for (pp = av; *pp; pp++)
1578 p = *pp;
1579 while (*p && l-- > 0)
1580 *t++ = *p++;
1581 if (l <= 0)
1582 break;
1583 *t++ = ' ';
1585 *--t = '\0';
1586 debug1("%s\n", pwin->p_cmd);
1588 if ((pwin->p_ptyfd = OpenDevice(av, 0, &type, &t)) < 0)
1590 free((char *)pwin);
1591 return -1;
1593 strncpy(pwin->p_tty, t, MAXSTR - 1);
1594 w->w_pwin = pwin;
1595 if (type != W_TYPE_PTY)
1597 FreePseudowin(w);
1598 Msg(0, "Cannot only use commands as pseudo win.");
1599 return -1;
1601 if (!(pwin->p_fdpat & F_PFRONT))
1602 evdeq(&w->w_readev);
1603 #ifdef TIOCPKT
1605 int flag = 0;
1607 if (ioctl(pwin->p_ptyfd, TIOCPKT, (char *)&flag))
1609 Msg(errno, "TIOCPKT pwin ioctl");
1610 FreePseudowin(w);
1611 return -1;
1613 if (w->w_type == W_TYPE_PTY && !(pwin->p_fdpat & F_PFRONT))
1615 if (ioctl(w->w_ptyfd, TIOCPKT, (char *)&flag))
1617 Msg(errno, "TIOCPKT win ioctl");
1618 FreePseudowin(w);
1619 return -1;
1623 #endif /* TIOCPKT */
1625 pwin->p_readev.fd = pwin->p_writeev.fd = pwin->p_ptyfd;
1626 pwin->p_readev.type = EV_READ;
1627 pwin->p_writeev.type = EV_WRITE;
1628 pwin->p_readev.data = pwin->p_writeev.data = (char *)w;
1629 pwin->p_readev.handler = pseu_readev_fn;
1630 pwin->p_writeev.handler = pseu_writeev_fn;
1631 pwin->p_writeev.condpos = &pwin->p_inlen;
1632 if (pwin->p_fdpat & (F_PFRONT << F_PSHIFT * 2 | F_PFRONT << F_PSHIFT))
1633 evenq(&pwin->p_readev);
1634 evenq(&pwin->p_writeev);
1635 r = pwin->p_pid = ForkWindow(w, av, t);
1636 if (r < 0)
1637 FreePseudowin(w);
1638 return r;
1641 void
1642 FreePseudowin(w)
1643 struct win *w;
1645 struct pseudowin *pwin = w->w_pwin;
1647 ASSERT(pwin);
1648 if (fcntl(w->w_ptyfd, F_SETFL, FNBLOCK))
1649 Msg(errno, "Warning: FreePseudowin: NBLOCK fcntl failed");
1650 #ifdef TIOCPKT
1651 if (w->w_type == W_TYPE_PTY && !(pwin->p_fdpat & F_PFRONT))
1653 int flag = 1;
1654 if (ioctl(w->w_ptyfd, TIOCPKT, (char *)&flag))
1655 Msg(errno, "Warning: FreePseudowin: TIOCPKT win ioctl");
1657 #endif
1658 /* should be able to use CloseDevice() here */
1659 (void)chmod(pwin->p_tty, 0666);
1660 (void)chown(pwin->p_tty, 0, 0);
1661 if (pwin->p_ptyfd >= 0)
1662 close(pwin->p_ptyfd);
1663 evdeq(&pwin->p_readev);
1664 evdeq(&pwin->p_writeev);
1665 if (w->w_readev.condneg == &pwin->p_inlen)
1666 w->w_readev.condpos = w->w_readev.condneg = 0;
1667 evenq(&w->w_readev);
1668 free((char *)pwin);
1669 w->w_pwin = NULL;
1672 #endif /* PSEUDOS */
1675 #ifdef MULTIUSER
1677 * returns 0, if the lock really has been released
1680 ReleaseAutoWritelock(dis, w)
1681 struct display *dis;
1682 struct win *w;
1684 debug2("ReleaseAutoWritelock: user %s, window %d\n",
1685 dis->d_user->u_name, w->w_number);
1687 /* release auto writelock when user has no other display here */
1688 if (w->w_wlock == WLOCK_AUTO && w->w_wlockuser == dis->d_user)
1690 struct display *d;
1692 for (d = displays; d; d = d->d_next)
1693 if (( d != dis) && (d->d_fore == w) && (d->d_user == dis->d_user))
1694 break;
1695 debug3("%s %s autolock on win %d\n",
1696 dis->d_user->u_name, d ? "keeps" : "releases", w->w_number);
1697 if (!d)
1699 w->w_wlockuser = NULL;
1700 return 0;
1703 return 1;
1707 * returns 0, if the lock really could be obtained
1710 ObtainAutoWritelock(d, w)
1711 struct display *d;
1712 struct win *w;
1714 if ((w->w_wlock == WLOCK_AUTO) &&
1715 !AclCheckPermWin(d->d_user, ACL_WRITE, w) &&
1716 !w->w_wlockuser)
1718 debug2("%s obtained auto writelock for exported window %d\n",
1719 d->d_user->u_name, w->w_number);
1720 w->w_wlockuser = d->d_user;
1721 return 0;
1723 return 1;
1726 #endif /* MULTIUSER */
1730 /********************************************************************/
1732 #ifdef COPY_PASTE
1733 static void
1734 paste_slowev_fn(ev, data)
1735 struct event *ev;
1736 char *data;
1738 struct paster *pa = (struct paster *)data;
1739 struct win *p;
1741 int l = 1;
1742 flayer = pa->pa_pastelayer;
1743 if (!flayer)
1744 pa->pa_pastelen = 0;
1745 if (!pa->pa_pastelen)
1746 return;
1747 p = Layer2Window(flayer);
1748 DoProcess(p, &pa->pa_pasteptr, &l, pa);
1749 pa->pa_pastelen -= 1 - l;
1750 if (pa->pa_pastelen > 0)
1752 SetTimeout(&pa->pa_slowev, p->w_slowpaste);
1753 evenq(&pa->pa_slowev);
1756 #endif
1759 static int
1760 muchpending(p, ev)
1761 struct win *p;
1762 struct event *ev;
1764 struct canvas *cv;
1765 for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext)
1767 display = cv->c_display;
1768 if (D_status == STATUS_ON_WIN && !D_status_bell)
1770 /* wait 'til status is gone */
1771 debug("BLOCKING because of status\n");
1772 ev->condpos = &const_one;
1773 ev->condneg = &D_status;
1774 return 1;
1776 debug2("muchpending %s %d: ", D_usertty, D_blocked);
1777 debug3("%d %d %d\n", D_obufp - D_obuf, D_obufmax, D_blocked_fuzz);
1778 if (D_blocked)
1779 continue;
1780 if (D_obufp - D_obuf > D_obufmax + D_blocked_fuzz)
1782 if (D_nonblock == 0)
1784 debug1("obuf is full, stopping output to display %s\n", D_usertty);
1785 D_blocked = 1;
1786 continue;
1788 debug("BLOCKING because of full obuf\n");
1789 ev->condpos = &D_obuffree;
1790 ev->condneg = &D_obuflenmax;
1791 if (D_nonblock > 0 && !D_blockedev.queued)
1793 debug1("created timeout of %g secs\n", D_nonblock/1000.);
1794 SetTimeout(&D_blockedev, D_nonblock);
1795 evenq(&D_blockedev);
1797 return 1;
1800 return 0;
1803 static void
1804 win_readev_fn(ev, data)
1805 struct event *ev;
1806 char *data;
1808 struct win *p = (struct win *)data;
1809 char buf[IOSIZE], *bp;
1810 int size, len;
1811 #ifdef PSEUDOS
1812 int wtop;
1813 #endif
1815 bp = buf;
1816 size = IOSIZE;
1818 #ifdef PSEUDOS
1819 wtop = p->w_pwin && W_WTOP(p);
1820 if (wtop)
1822 ASSERT(sizeof(p->w_pwin->p_inbuf) == IOSIZE);
1823 size = IOSIZE - p->w_pwin->p_inlen;
1824 if (size <= 0)
1826 ev->condpos = &const_IOSIZE;
1827 ev->condneg = &p->w_pwin->p_inlen;
1828 return;
1831 #endif
1832 if (p->w_layer.l_cvlist && muchpending(p, ev))
1833 return;
1834 #ifdef ZMODEM
1835 if (!p->w_zdisplay)
1836 #endif
1837 if (p->w_blocked)
1839 ev->condpos = &const_one;
1840 ev->condneg = &p->w_blocked;
1841 return;
1843 if (ev->condpos)
1844 ev->condpos = ev->condneg = 0;
1846 if ((len = p->w_outlen))
1848 p->w_outlen = 0;
1849 WriteString(p, p->w_outbuf, len);
1850 return;
1853 debug1("going to read from window fd %d\n", ev->fd);
1854 if ((len = read(ev->fd, buf, size)) < 0)
1856 if (errno == EINTR || errno == EAGAIN)
1857 return;
1858 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1859 if (errno == EWOULDBLOCK)
1860 return;
1861 #endif
1862 debug2("Window %d: read error (errno %d) - killing window\n", p->w_number, errno);
1863 #ifdef BSDWAIT
1864 WindowDied(p, (union wait)0, 0);
1865 #else
1866 WindowDied(p, 0, 0);
1867 #endif
1868 return;
1870 if (len == 0)
1872 debug1("Window %d: EOF - killing window\n", p->w_number);
1873 #ifdef BSDWAIT
1874 WindowDied(p, (union wait)0, 0);
1875 #else
1876 WindowDied(p, 0, 0);
1877 #endif
1878 return;
1880 debug1(" -> %d bytes\n", len);
1881 #ifdef TIOCPKT
1882 if (p->w_type == W_TYPE_PTY)
1884 if (buf[0])
1886 debug1("PAKET %x\n", buf[0]);
1887 if (buf[0] & TIOCPKT_NOSTOP)
1888 WNewAutoFlow(p, 0);
1889 if (buf[0] & TIOCPKT_DOSTOP)
1890 WNewAutoFlow(p, 1);
1892 bp++;
1893 len--;
1895 #endif
1896 #ifdef BUILTIN_TELNET
1897 if (p->w_type == W_TYPE_TELNET)
1898 len = TelIn(p, bp, len, buf + sizeof(buf) - (bp + len));
1899 #endif
1900 if (len == 0)
1901 return;
1902 #ifdef ZMODEM
1903 if (zmodem_mode && zmodem_parse(p, bp, len))
1904 return;
1905 #endif
1906 #ifdef PSEUDOS
1907 if (wtop)
1909 debug("sending input to pwin\n");
1910 bcopy(bp, p->w_pwin->p_inbuf + p->w_pwin->p_inlen, len);
1911 p->w_pwin->p_inlen += len;
1913 #endif
1914 WriteString(p, bp, len);
1915 return;
1919 static void
1920 win_writeev_fn(ev, data)
1921 struct event *ev;
1922 char *data;
1924 struct win *p = (struct win *)data;
1925 int len;
1926 if (p->w_inlen)
1928 debug2("writing %d bytes to win %d\n", p->w_inlen, p->w_number);
1929 if ((len = write(ev->fd, p->w_inbuf, p->w_inlen)) <= 0)
1930 len = p->w_inlen; /* dead window */
1931 if ((p->w_inlen -= len))
1932 bcopy(p->w_inbuf + len, p->w_inbuf, p->w_inlen);
1934 #ifdef COPY_PASTE
1935 if (p->w_paster.pa_pastelen && !p->w_slowpaste)
1937 struct paster *pa = &p->w_paster;
1938 flayer = pa->pa_pastelayer;
1939 if (flayer)
1940 DoProcess(p, &pa->pa_pasteptr, &pa->pa_pastelen, pa);
1942 #endif
1943 return;
1948 #ifdef PSEUDOS
1950 static void
1951 pseu_readev_fn(ev, data)
1952 struct event *ev;
1953 char *data;
1955 struct win *p = (struct win *)data;
1956 char buf[IOSIZE];
1957 int size, ptow, len;
1959 size = IOSIZE;
1961 ptow = W_PTOW(p);
1962 if (ptow)
1964 ASSERT(sizeof(p->w_inbuf) == IOSIZE);
1965 size = IOSIZE - p->w_inlen;
1966 if (size <= 0)
1968 ev->condpos = &const_IOSIZE;
1969 ev->condneg = &p->w_inlen;
1970 return;
1973 if (p->w_layer.l_cvlist && muchpending(p, ev))
1974 return;
1975 if (p->w_blocked)
1977 ev->condpos = &const_one;
1978 ev->condneg = &p->w_blocked;
1979 return;
1981 if (ev->condpos)
1982 ev->condpos = ev->condneg = 0;
1984 if ((len = p->w_outlen))
1986 p->w_outlen = 0;
1987 WriteString(p, p->w_outbuf, len);
1988 return;
1991 if ((len = read(ev->fd, buf, size)) <= 0)
1993 if (errno == EINTR || errno == EAGAIN)
1994 return;
1995 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1996 if (errno == EWOULDBLOCK)
1997 return;
1998 #endif
1999 debug2("Window %d: pseudowin read error (errno %d) -- removing pseudowin\n", p->w_number, len ? errno : 0);
2000 FreePseudowin(p);
2001 return;
2003 /* no packet mode on pseudos! */
2004 if (ptow)
2006 bcopy(buf, p->w_inbuf + p->w_inlen, len);
2007 p->w_inlen += len;
2009 WriteString(p, buf, len);
2010 return;
2013 static void
2014 pseu_writeev_fn(ev, data)
2015 struct event *ev;
2016 char *data;
2018 struct win *p = (struct win *)data;
2019 struct pseudowin *pw = p->w_pwin;
2020 int len;
2022 ASSERT(pw);
2023 if (pw->p_inlen == 0)
2024 return;
2025 if ((len = write(ev->fd, pw->p_inbuf, pw->p_inlen)) <= 0)
2026 len = pw->p_inlen; /* dead pseudo */
2027 if ((p->w_pwin->p_inlen -= len))
2028 bcopy(p->w_pwin->p_inbuf + len, p->w_pwin->p_inbuf, p->w_pwin->p_inlen);
2032 #endif /* PSEUDOS */
2034 static void
2035 win_silenceev_fn(ev, data)
2036 struct event *ev;
2037 char *data;
2039 struct win *p = (struct win *)data;
2040 struct canvas *cv;
2041 debug1("FOUND silence win %d\n", p->w_number);
2042 for (display = displays; display; display = display->d_next)
2044 for (cv = D_cvlist; cv; cv = cv->c_next)
2045 if (cv->c_layer->l_bottom == &p->w_layer)
2046 break;
2047 if (cv)
2048 continue; /* user already sees window */
2049 #ifdef MULTIUSER
2050 if (!(ACLBYTE(p->w_lio_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
2051 continue;
2052 #endif
2053 Msg(0, "Window %d: silence for %d seconds", p->w_number, p->w_silencewait);
2057 #ifdef ZMODEM
2059 static int
2060 zmodem_parse(p, bp, len)
2061 struct win *p;
2062 char *bp;
2063 int len;
2065 int i;
2066 char *b2 = bp;
2067 for (i = 0; i < len; i++, b2++)
2069 if (p->w_zauto == 0)
2071 for (; i < len; i++, b2++)
2072 if (*b2 == 030)
2073 break;
2074 if (i == len)
2075 break;
2076 if (i > 1 && b2[-1] == '*' && b2[-2] == '*')
2077 p->w_zauto = 3;
2078 continue;
2080 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'))
2082 if (++p->w_zauto < 6)
2083 continue;
2084 if (p->w_zauto == 6)
2085 p->w_zauto = 0;
2086 if (!p->w_zdisplay)
2088 if (i > 6)
2089 WriteString(p, bp, i + 1 - 6);
2090 WriteString(p, "\r\n", 2);
2091 zmodem_found(p, *b2 == '1', b2 + 1, len - i - 1);
2092 return 1;
2094 else if (p->w_zauto == 7 || *b2 == '8')
2096 int se = p->w_zdisplay->d_blocked == 2 ? 'O' : '\212';
2097 for (; i < len; i++, b2++)
2098 if (*b2 == se)
2099 break;
2100 if (i < len)
2102 zmodem_abort(p, 0);
2103 D_blocked = 0;
2104 D_readev.condpos = D_readev.condneg = 0;
2105 while (len-- > 0)
2106 AddChar(*bp++);
2107 Flush();
2108 Activate(D_fore ? D_fore->w_norefresh : 0);
2109 return 1;
2111 p->w_zauto = 6;
2114 else
2115 p->w_zauto = *b2 == '*' ? (p->w_zauto == 2 ? 2 : 1) : 0;
2117 if (p->w_zauto == 0 && bp[len - 1] == '*')
2118 p->w_zauto = len > 1 && bp[len - 2] == '*' ? 2 : 1;
2119 if (p->w_zdisplay)
2121 display = p->w_zdisplay;
2122 while (len-- > 0)
2123 AddChar(*bp++);
2124 return 1;
2126 return 0;
2129 static void
2130 zmodem_fin(buf, len, data)
2131 char *buf;
2132 int len;
2133 char *data;
2135 char *s;
2136 int n;
2138 if (len)
2139 RcLine(buf, strlen(buf) + 1);
2140 else
2142 s = "\030\030\030\030\030\030\030\030\030\030";
2143 n = strlen(s);
2144 LayProcess(&s, &n);
2148 static void
2149 zmodem_found(p, send, bp, len)
2150 struct win *p;
2151 int send;
2152 char *bp;
2153 int len;
2155 char *s;
2156 int i, n;
2157 extern int zmodem_mode;
2159 /* check for abort sequence */
2160 n = 0;
2161 for (i = 0; i < len ; i++)
2162 if (bp[i] != 030)
2163 n = 0;
2164 else if (++n > 4)
2165 return;
2166 if (zmodem_mode == 3 || (zmodem_mode == 1 && p->w_type != W_TYPE_PLAIN))
2168 struct display *d, *olddisplay;
2170 olddisplay = display;
2171 d = p->w_lastdisp;
2172 if (!d || d->d_fore != p)
2173 for (d = displays; d; d = d->d_next)
2174 if (d->d_fore == p)
2175 break;
2176 if (!d && p->w_layer.l_cvlist)
2177 d = p->w_layer.l_cvlist->c_display;
2178 if (!d)
2179 d = displays;
2180 if (!d)
2181 return;
2182 display = d;
2183 RemoveStatus();
2184 p->w_zdisplay = display;
2185 D_blocked = 2 + send;
2186 flayer = &p->w_layer;
2187 ZmodemPage();
2188 display = d;
2189 evdeq(&D_blockedev);
2190 D_readev.condpos = &const_IOSIZE;
2191 D_readev.condneg = &p->w_inlen;
2192 ClearAll();
2193 GotoPos(0, 0);
2194 SetRendition(&mchar_blank);
2195 AddStr("Zmodem active\r\n\r\n");
2196 AddStr(send ? "**\030B01" : "**\030B00");
2197 while (len-- > 0)
2198 AddChar(*bp++);
2199 display = olddisplay;
2200 return;
2202 flayer = &p->w_layer;
2203 Input(":", 100, INP_COOKED, zmodem_fin, NULL, 0);
2204 s = send ? zmodem_sendcmd : zmodem_recvcmd;
2205 n = strlen(s);
2206 LayProcess(&s, &n);
2209 void
2210 zmodem_abort(p, d)
2211 struct win *p;
2212 struct display *d;
2214 struct display *olddisplay = display;
2215 struct layer *oldflayer = flayer;
2216 if (p)
2218 if (p->w_savelayer && p->w_savelayer->l_next)
2220 if (oldflayer == p->w_savelayer)
2221 oldflayer = flayer->l_next;
2222 flayer = p->w_savelayer;
2223 ExitOverlayPage();
2225 p->w_zdisplay = 0;
2226 p->w_zauto = 0;
2227 LRefreshAll(&p->w_layer, 0);
2229 if (d)
2231 display = d;
2232 D_blocked = 0;
2233 D_readev.condpos = D_readev.condneg = 0;
2234 Activate(D_fore ? D_fore->w_norefresh : 0);
2236 display = olddisplay;
2237 flayer = oldflayer;
2240 #endif