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