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#ifdef HAVE_BRAILLE
6 * Modified by:
7 *      Authors:  Hadi Bargi Rangin  bargi@dots.physics.orst.edu
8 *                Bill Barry         barryb@dots.physics.orst.edu
9 *                Randy Lundquist    randyl@dots.physics.orst.edu
10 *
11 * Modifications Copyright (c) 1995 by
12 * Science Access Project, Oregon State University.
13#endif
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2, or (at your option)
18 * any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program (see the file COPYING); if not, write to the
27 * Free Software Foundation, Inc.,
28 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
29 *
30 ****************************************************************
31 */
32
33#include <sys/types.h>
34#include <ctype.h>
35
36#include <fcntl.h>
37
38#ifdef sgi
39# include <sys/sysmacros.h>
40#endif
41
42#include <sys/stat.h>
43#ifndef sun
44# include <sys/ioctl.h>
45#endif
46
47#ifndef SIGINT
48# include <signal.h>
49#endif
50
51#include "config.h"
52
53#ifdef SVR4
54# include <sys/stropts.h>
55#endif
56
57#if defined(SYSV) && !defined(ISC)
58# include <sys/utsname.h>
59#endif
60
61#if defined(sequent) || defined(SVR4)
62# include <sys/resource.h>
63#endif /* sequent || SVR4 */
64
65#ifdef ISC
66# include <sys/tty.h>
67# include <sys/sioctl.h>
68# include <sys/pty.h>
69#endif /* ISC */
70
71#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
72# include <compat.h>
73#endif
74#if defined(USE_LOCALE) || defined(ENCODINGS)
75# include <locale.h>
76#endif
77#if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
78# include <langinfo.h>
79#endif
80
81#include "screen.h"
82#ifdef HAVE_BRAILLE
83# include "braille.h"
84#endif
85
86#include "patchlevel.h"
87
88/*
89 *  At the moment we only need the real password if the
90 *  builtin lock is used. Therefore disable SHADOWPW if
91 *  we do not really need it (kind of security thing).
92 */
93#ifndef LOCK
94# undef SHADOWPW
95#endif
96
97#include <pwd.h>
98#ifdef SHADOWPW
99# include <shadow.h>
100#endif /* SHADOWPW */
101
102#include "logfile.h"	/* islogfile, logfflush */
103
104#ifdef __APPLE__
105#include <TargetConditionals.h>
106#if !TARGET_OS_EMBEDDED
107#include <vproc.h>
108#include <vproc_priv.h>
109#endif
110#endif
111
112#ifdef DEBUG
113FILE *dfp;
114#endif
115
116
117extern char Term[], screenterm[], **environ, Termcap[];
118int force_vt = 1;
119int VBellWait, MsgWait, MsgMinWait, SilenceWait;
120
121extern struct acluser *users;
122extern struct display *displays, *display;
123
124
125extern int visual_bell;
126#ifdef COPY_PASTE
127extern unsigned char mark_key_tab[];
128#endif
129extern char version[];
130extern char DefaultShell[];
131#ifdef ZMODEM
132extern char *zmodem_sendcmd;
133extern char *zmodem_recvcmd;
134#endif
135
136
137char *ShellProg;
138char *ShellArgs[2];
139
140extern struct NewWindow nwin_undef, nwin_default, nwin_options;
141struct backtick;
142
143static struct passwd *getpwbyname __P((char *, struct passwd *));
144static void  SigChldHandler __P((void));
145static sigret_t SigChld __P(SIGPROTOARG);
146static sigret_t SigInt __P(SIGPROTOARG);
147static sigret_t CoreDump __P(SIGPROTOARG);
148static sigret_t FinitHandler __P(SIGPROTOARG);
149static void  DoWait __P((void));
150static void  serv_read_fn __P((struct event *, char *));
151static void  serv_select_fn __P((struct event *, char *));
152static void  logflush_fn __P((struct event *, char *));
153static void  backtick_filter __P((struct backtick *));
154static void  backtick_fn __P((struct event *, char *));
155static char *runbacktick __P((struct backtick *, int *, time_t));
156static int   IsSymbol __P((char *, char *));
157static char *ParseChar __P((char *, char *));
158static int   ParseEscape __P((char *));
159static char *pad_expand __P((char *, char *, int, int));
160#ifdef DEBUG
161static void  fds __P((void));
162#endif
163
164int nversion;	/* numerical version, used for secondary DA */
165
166/* the attacher */
167struct passwd *ppp;
168char *attach_tty;
169char *attach_term;
170char *LoginName;
171struct mode attach_Mode;
172
173char SockPath[MAXPATHLEN + 2 * MAXSTR];
174char *SockName;			/* SockName is pointer in SockPath */
175char *SockMatch = NULL;		/* session id command line argument */
176int ServerSocket = -1;
177struct event serv_read;
178struct event serv_select;
179struct event logflushev;
180
181char **NewEnv = NULL;
182
183char *RcFileName = NULL;
184char *home;
185
186char *screenlogfile;			/* filename layout */
187int log_flush = 10;           		/* flush interval in seconds */
188int logtstamp_on = 0;			/* tstamp disabled */
189char *logtstamp_string;			/* stamp layout */
190int logtstamp_after = 120;		/* first tstamp after 120s */
191char *hardcopydir = NULL;
192char *BellString;
193char *VisualBellString;
194char *ActivityString;
195#ifdef COPY_PASTE
196char *BufferFile;
197#endif
198#ifdef POW_DETACH
199char *PowDetachString;
200#endif
201char *hstatusstring;
202char *captionstring;
203char *timestring;
204char *wliststr;
205char *wlisttit;
206int auto_detach = 1;
207int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
208int cmdflag;
209int adaptflag;
210
211#ifdef MULTIUSER
212char *multi;
213char *multi_home;
214int multi_uid;
215int own_uid;
216int multiattach;
217int tty_mode;
218int tty_oldmode = -1;
219#endif
220
221char HostName[MAXSTR];
222int MasterPid;
223int real_uid, real_gid, eff_uid, eff_gid;
224int default_startup;
225int ZombieKey_destroy, ZombieKey_resurrect;
226char *preselect = NULL;		/* only used in Attach() */
227
228#ifdef UTF8
229char *screenencodings;
230#endif
231
232#ifdef NETHACK
233int nethackflag = 0;
234#endif
235int maxwin = MAXWIN;
236
237
238struct layer *flayer;
239struct win *fore;
240struct win *windows;
241struct win *console_window;
242
243
244
245/*
246 * Do this last
247 */
248#include "extern.h"
249
250char strnomem[] = "Out of memory.";
251
252
253static int InterruptPlease;
254static int GotSigChld;
255
256static int
257lf_secreopen(name, wantfd, l)
258char *name;
259int wantfd;
260struct logfile *l;
261{
262  int got_fd;
263
264  close(wantfd);
265  if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
266      lf_move_fd(got_fd, wantfd) < 0)
267    {
268      logfclose(l);
269      debug1("lf_secreopen: failed for %s\n", name);
270      return -1;
271    }
272  l->st->st_ino = l->st->st_dev = 0;
273  debug2("lf_secreopen: %d = %s\n", wantfd, name);
274  return 0;
275}
276
277/********************************************************************/
278/********************************************************************/
279/********************************************************************/
280
281
282static struct passwd *
283getpwbyname(name, ppp)
284char *name;
285struct passwd *ppp;
286{
287  int n;
288#ifdef SHADOWPW
289  struct spwd *sss = NULL;
290  static char *spw = NULL;
291#endif
292
293  if (!ppp && !(ppp = getpwnam(name)))
294    return NULL;
295
296  /* Do password sanity check..., allow ##user for SUN_C2 security */
297#ifdef SHADOWPW
298pw_try_again:
299#endif
300  n = 0;
301  if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
302      strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
303    n = 13;
304  for (; n < 13; n++)
305    {
306      char c = ppp->pw_passwd[n];
307      if (!(c == '.' || c == '/' || c == '$' ||
308	    (c >= '0' && c <= '9') ||
309	    (c >= 'a' && c <= 'z') ||
310	    (c >= 'A' && c <= 'Z')))
311	break;
312    }
313
314#ifdef SHADOWPW
315  /* try to determine real password */
316  if (n < 13 && sss == 0)
317    {
318      sss = getspnam(ppp->pw_name);
319      if (sss)
320	{
321	  if (spw)
322	    free(spw);
323	  ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
324	  endspent();	/* this should delete all buffers ... */
325	  goto pw_try_again;
326	}
327      endspent();	/* this should delete all buffers ... */
328    }
329#endif
330  if (n < 13)
331    ppp->pw_passwd = 0;
332#ifdef linux
333  if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
334    ppp->pw_passwd[13] = 0;	/* beware of linux's long passwords */
335#endif
336
337  return ppp;
338}
339
340
341int
342main(ac, av)
343int ac;
344char **av;
345{
346  register int n;
347  char *ap;
348  char *av0;
349  char socknamebuf[2 * MAXSTR];
350  int mflag = 0;
351  char *myname = (ac == 0) ? "screen" : av[0];
352  char *SockDir;
353  struct stat st;
354#ifdef _MODE_T			/* (jw) */
355  mode_t oumask;
356#else
357  int oumask;
358#endif
359#if defined(SYSV) && !defined(ISC)
360  struct utsname utsnam;
361#endif
362  struct NewWindow nwin;
363  int detached = 0;		/* start up detached */
364#ifdef MULTIUSER
365  char *sockp;
366#endif
367
368#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
369  setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
370#endif
371#if defined(sun) && defined(SVR4)
372  {
373    /* Solaris' login blocks SIGHUP! This is _very bad_ */
374    sigset_t sset;
375    sigemptyset(&sset);
376    sigprocmask(SIG_SETMASK, &sset, 0);
377  }
378#endif
379
380  /*
381   *  First, close all unused descriptors
382   *  (otherwise, we might have problems with the select() call)
383   */
384  closeallfiles(0);
385#ifdef DEBUG
386  opendebug(1, 0);
387#endif
388  sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
389	  PATCHLEVEL, STATE, ORIGIN, DATE);
390  nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
391  debug2("-- screen debug started %s (%s)\n", *av, version);
392#ifdef POSIX
393  debug("POSIX\n");
394#endif
395#ifdef TERMIO
396  debug("TERMIO\n");
397#endif
398#ifdef SYSV
399  debug("SYSV\n");
400#endif
401#ifdef SYSVSIGS
402  debug("SYSVSIGS\n");
403#endif
404#ifdef NAMEDPIPE
405  debug("NAMEDPIPE\n");
406#endif
407#if defined(SIGWINCH) && defined(TIOCGWINSZ)
408  debug("Window size changing enabled\n");
409#endif
410#ifdef HAVE_SETREUID
411  debug("SETREUID\n");
412#endif
413#ifdef HAVE_SETEUID
414  debug("SETEUID\n");
415#endif
416#ifdef hpux
417  debug("hpux\n");
418#endif
419#ifdef USEBCOPY
420  debug("USEBCOPY\n");
421#endif
422#ifdef UTMPOK
423  debug("UTMPOK\n");
424#endif
425#ifdef LOADAV
426  debug("LOADAV\n");
427#endif
428#ifdef NETHACK
429  debug("NETHACK\n");
430#endif
431#ifdef TERMINFO
432  debug("TERMINFO\n");
433#endif
434#ifdef SHADOWPW
435  debug("SHADOWPW\n");
436#endif
437#ifdef NAME_MAX
438  debug1("NAME_MAX = %d\n", NAME_MAX);
439#endif
440
441  BellString = SaveStr("Bell in window %n");
442  VisualBellString = SaveStr("   Wuff,  Wuff!!  ");
443  ActivityString = SaveStr("Activity in window %n");
444  screenlogfile = SaveStr("screenlog.%n");
445  logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
446  hstatusstring = SaveStr("%h");
447  captionstring = SaveStr("%3n %t");
448  timestring = SaveStr("%c:%s %M %d %H%? %l%?");
449  wlisttit = SaveStr("Num Name%=Flags");
450  wliststr = SaveStr("%3n %t%=%f");
451#ifdef COPY_PASTE
452  BufferFile = SaveStr(DEFAULT_BUFFERFILE);
453#endif
454  ShellProg = NULL;
455#ifdef POW_DETACH
456  PowDetachString = 0;
457#endif
458  default_startup = (ac > 1) ? 0 : 1;
459  adaptflag = 0;
460  VBellWait = VBELLWAIT * 1000;
461  MsgWait = MSGWAIT * 1000;
462  MsgMinWait = MSGMINWAIT * 1000;
463  SilenceWait = SILENCEWAIT;
464#ifdef HAVE_BRAILLE
465  InitBraille();
466#endif
467#ifdef ZMODEM
468  zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
469  zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
470#endif
471
472#ifdef COPY_PASTE
473  CompileKeys((char *)0, 0, mark_key_tab);
474#endif
475#ifdef UTF8
476  InitBuiltinTabs();
477  screenencodings = SaveStr(SCREENENCODINGS);
478#endif
479  nwin = nwin_undef;
480  nwin_options = nwin_undef;
481  strcpy(screenterm, "screen");
482
483  logreopen_register(lf_secreopen);
484
485  av0 = *av;
486  /* if this is a login screen, assume -RR */
487  if (*av0 == '-')
488    {
489      rflag = 4;
490#ifdef MULTI
491      xflag = 1;
492#else
493      dflag = 1;
494#endif
495      ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
496    }
497  while (ac > 0)
498    {
499      ap = *++av;
500      if (--ac > 0 && *ap == '-')
501	{
502	  if (ap[1] == '-' && ap[2] == 0)
503	    {
504	      av++;
505	      ac--;
506	      break;
507	    }
508	  if (ap[1] == '-' && !strcmp(ap, "--version"))
509	    Panic(0, "Screen version %s", version);
510	  if (ap[1] == '-' && !strcmp(ap, "--help"))
511	    exit_with_usage(myname, NULL, NULL);
512	  while (ap && *ap && *++ap)
513	    {
514	      switch (*ap)
515		{
516		case 'a':
517		  nwin_options.aflag = 1;
518		  break;
519		case 'A':
520		  adaptflag = 1;
521		  break;
522		case 'p': /* preselect */
523		  if (*++ap)
524		    preselect = ap;
525		  else
526		    {
527		      if (!--ac)
528			exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
529		      preselect = *++av;
530		    }
531		  ap = NULL;
532		  break;
533#ifdef HAVE_BRAILLE
534		case 'B':
535		  bd.bd_start_braille = 1;
536		  break;
537#endif
538		case 'c':
539		  if (*++ap)
540		    RcFileName = ap;
541		  else
542		    {
543		      if (--ac == 0)
544			exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
545		      RcFileName = *++av;
546		    }
547		  ap = NULL;
548		  break;
549		case 'e':
550		  if (!*++ap)
551		    {
552		      if (--ac == 0)
553			exit_with_usage(myname, "Specify command characters with -e", NULL);
554		      ap = *++av;
555		    }
556		  if (ParseEscape(ap))
557		    Panic(0, "Two characters are required with -e option, not '%s'.", ap);
558		  ap = NULL;
559		  break;
560		case 'f':
561		  ap++;
562		  switch (*ap++)
563		    {
564		    case 'n':
565		    case '0':
566		      nwin_options.flowflag = FLOW_NOW * 0;
567		      break;
568		    case '\0':
569		      ap--;
570		      /* FALLTHROUGH */
571		    case 'y':
572		    case '1':
573		      nwin_options.flowflag = FLOW_NOW * 1;
574		      break;
575		    case 'a':
576		      nwin_options.flowflag = FLOW_AUTOFLAG;
577		      break;
578		    default:
579		      exit_with_usage(myname, "Unknown flow option -%s", --ap);
580		    }
581		  break;
582		case 'h':
583		  if (--ac == 0)
584		    exit_with_usage(myname, NULL, NULL);
585		  nwin_options.histheight = atoi(*++av);
586		  if (nwin_options.histheight < 0)
587		    exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
588		  break;
589		case 'i':
590		  iflag = 1;
591		  break;
592		case 't': /* title, the former AkA == -k */
593		  if (--ac == 0)
594		    exit_with_usage(myname, "Specify a new window-name with -t", NULL);
595		  nwin_options.aka = *++av;
596		  break;
597		case 'l':
598		  ap++;
599		  switch (*ap++)
600		    {
601		    case 'n':
602		    case '0':
603		      nwin_options.lflag = 0;
604		      break;
605		    case '\0':
606		      ap--;
607		      /* FALLTHROUGH */
608		    case 'y':
609		    case '1':
610		      nwin_options.lflag = 1;
611		      break;
612		    case 'a':
613		      nwin_options.lflag = 3;
614		      break;
615		    case 's':	/* -ls */
616		    case 'i':	/* -list */
617		      lsflag = 1;
618		      if (ac > 1 && !SockMatch)
619			{
620			  SockMatch = *++av;
621			  ac--;
622			}
623		      ap = NULL;
624		      break;
625		    default:
626		      exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
627		    }
628		  break;
629		case 'w':
630		  lsflag = 1;
631		  wipeflag = 1;
632		  if (ac > 1 && !SockMatch)
633		    {
634		      SockMatch = *++av;
635		      ac--;
636		    }
637		  break;
638		case 'L':
639		  nwin_options.Lflag = 1;
640		  break;
641		case 'm':
642		  mflag = 1;
643		  break;
644		case 'O':		/* to be (or not to be?) deleted. jw. */
645		  force_vt = 0;
646		  break;
647		case 'T':
648		  if (--ac == 0)
649		    exit_with_usage(myname, "Specify terminal-type with -T", NULL);
650		  if (strlen(*++av) < 20)
651		    strcpy(screenterm, *av);
652		  else
653		    Panic(0, "-T: terminal name too long. (max. 20 char)");
654		  nwin_options.term = screenterm;
655		  break;
656		case 'q':
657		  quietflag = 1;
658		  break;
659		case 'r':
660		case 'R':
661#ifdef MULTI
662		case 'x':
663#endif
664		  if (ac > 1 && *av[1] != '-' && !SockMatch)
665		    {
666		      SockMatch = *++av;
667		      ac--;
668		      debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
669		    }
670#ifdef MULTI
671		  if (*ap == 'x')
672		    xflag = 1;
673#endif
674		  if (rflag)
675		    rflag = 2;
676		  rflag += (*ap == 'R') ? 2 : 1;
677		  break;
678#ifdef REMOTE_DETACH
679		case 'd':
680		  dflag = 1;
681		  /* FALLTHROUGH */
682		case 'D':
683		  if (!dflag)
684		    dflag = 2;
685		  if (ac == 2)
686		    {
687		      if (*av[1] != '-' && !SockMatch)
688			{
689			  SockMatch = *++av;
690			  ac--;
691			  debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
692			}
693		    }
694		  break;
695#endif
696		case 's':
697		  if (--ac == 0)
698		    exit_with_usage(myname, "Specify shell with -s", NULL);
699		  if (ShellProg)
700		    free(ShellProg);
701		  ShellProg = SaveStr(*++av);
702		  debug1("ShellProg: '%s'\n", ShellProg);
703		  break;
704		case 'S':
705		  if (!SockMatch)
706		    {
707		      if (--ac == 0)
708			exit_with_usage(myname, "Specify session-name with -S", NULL);
709		      SockMatch = *++av;
710		    }
711		  if (!*SockMatch)
712		    exit_with_usage(myname, "Empty session-name?", NULL);
713		  break;
714		case 'X':
715		  cmdflag = 1;
716		  break;
717		case 'v':
718		  Panic(0, "Screen version %s", version);
719		  /* NOTREACHED */
720#ifdef UTF8
721		case 'U':
722		  nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
723		  break;
724#endif
725		default:
726		  exit_with_usage(myname, "Unknown option %s", --ap);
727		}
728	    }
729	}
730      else
731	break;
732    }
733
734  real_uid = getuid();
735  real_gid = getgid();
736  eff_uid = geteuid();
737  eff_gid = getegid();
738  if (eff_uid != real_uid)
739    {
740      /* if running with s-bit, we must install a special signal
741       * handler routine that resets the s-bit, so that we get a
742       * core file anyway.
743       */
744#ifdef SIGBUS /* OOPS, linux has no bus errors! */
745      signal(SIGBUS, CoreDump);
746#endif /* SIGBUS */
747      signal(SIGSEGV, CoreDump);
748    }
749
750#ifdef USE_LOCALE
751  setlocale(LC_ALL, "");
752#endif
753#ifdef ENCODINGS
754  if (nwin_options.encoding == -1)
755    {
756      /* ask locale if we should start in UTF-8 mode */
757# ifdef HAVE_NL_LANGINFO
758#  ifndef USE_LOCALE
759      setlocale(LC_CTYPE, "");
760#  endif
761      nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
762      debug1("locale says encoding = %d\n", nwin_options.encoding);
763# else
764#  ifdef UTF8
765      char *s;
766      if (((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
767          (s = getenv("LANG"))) && InStr(s, "UTF-8"))
768        nwin_options.encoding = UTF8;
769#  endif
770      debug1("environment says encoding=%d\n", nwin_options.encoding);
771#endif
772    }
773#endif
774  if (SockMatch && strlen(SockMatch) >= MAXSTR)
775    Panic(0, "Ridiculously long socketname - try again.");
776  if (cmdflag && !rflag && !dflag && !xflag)
777    xflag = 1;
778  if (!cmdflag && dflag && mflag && !(rflag || xflag))
779    detached = 1;
780  nwin = nwin_options;
781#ifdef ENCODINGS
782  nwin.encoding = nwin_undef.encoding;	/* let screenrc overwrite it */
783#endif
784  if (ac)
785    nwin.args = av;
786
787  /* make the write() calls return -1 on all errors */
788#ifdef SIGXFSZ
789  /*
790   * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
791   * It appears that in System V Release 4, UNIX, if you are writing
792   * an output file and you exceed the currently set file size limit,
793   * you _don't_ just get the call to `write' returning with a
794   * failure code.  Rather, you get a signal called `SIGXFSZ' which,
795   * if neither handled nor ignored, will cause your program to crash
796   * with a core dump.
797   */
798  signal(SIGXFSZ, SIG_IGN);
799#endif /* SIGXFSZ */
800
801#ifdef SIGPIPE
802  signal(SIGPIPE, SIG_IGN);
803#endif
804
805  if (!ShellProg)
806    {
807      register char *sh;
808
809      sh = getenv("SHELL");
810      ShellProg = SaveStr(sh ? sh : DefaultShell);
811    }
812  ShellArgs[0] = ShellProg;
813  home = getenv("HOME");
814
815#ifdef NETHACK
816  if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
817    {
818      char nethackrc[MAXPATHLEN];
819
820      if (home && (strlen(home) < (MAXPATHLEN - 20)))
821        {
822	  sprintf(nethackrc,"%s/.nethackrc", home);
823	  nethackflag = !access(nethackrc, F_OK);
824	}
825    }
826#endif
827
828#ifdef MULTIUSER
829  own_uid = multi_uid = real_uid;
830  if (SockMatch && (sockp = index(SockMatch, '/')))
831    {
832      if (eff_uid)
833        Panic(0, "Must run suid root for multiuser support.");
834      *sockp = 0;
835      multi = SockMatch;
836      SockMatch = sockp + 1;
837      if (*multi)
838	{
839	  struct passwd *mppp;
840	  if ((mppp = getpwnam(multi)) == (struct passwd *)0)
841	    Panic(0, "Cannot identify account '%s'.", multi);
842	  multi_uid = mppp->pw_uid;
843	  multi_home = SaveStr(mppp->pw_dir);
844          if (strlen(multi_home) > MAXPATHLEN - 10)
845	    Panic(0, "home directory path too long");
846# ifdef MULTI
847          /* always fake multi attach mode */
848	  if (rflag || lsflag)
849	    xflag = 1;
850# endif /* MULTI */
851	  detached = 0;
852	  multiattach = 1;
853	}
854    }
855  if (SockMatch && *SockMatch == 0)
856    SockMatch = 0;
857#endif /* MULTIUSER */
858
859  if ((LoginName = getlogin()) && LoginName[0] != '\0')
860    {
861      if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
862	if ((int)ppp->pw_uid != real_uid)
863	  ppp = (struct passwd *) 0;
864    }
865  if (ppp == 0)
866    {
867      if ((ppp = getpwuid(real_uid)) == 0)
868        {
869	  Panic(0, "getpwuid() can't identify your account!");
870	  exit(1);
871        }
872      LoginName = ppp->pw_name;
873    }
874  LoginName = SaveStr(LoginName);
875
876  ppp = getpwbyname(LoginName, ppp);
877
878#if !defined(SOCKDIR) && defined(MULTIUSER)
879  if (multi && !multiattach)
880    {
881      if (home && strcmp(home, ppp->pw_dir))
882        Panic(0, "$HOME must match passwd entry for multiuser screens.");
883    }
884#endif
885
886  if (home == 0 || *home == '\0')
887    home = ppp->pw_dir;
888  if (strlen(LoginName) > 20)
889    Panic(0, "LoginName too long - sorry.");
890#ifdef MULTIUSER
891  if (multi && strlen(multi) > 20)
892    Panic(0, "Screen owner name too long - sorry.");
893#endif
894  if (strlen(home) > MAXPATHLEN - 25)
895    Panic(0, "$HOME too long - sorry.");
896
897  attach_tty = "";
898  if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag))
899    {
900      /* ttyname implies isatty */
901      if (!(attach_tty = ttyname(0)))
902        Panic(0, "Must be connected to a terminal.");
903      if (strlen(attach_tty) >= MAXPATHLEN)
904	Panic(0, "TtyName too long - sorry.");
905      if (stat(attach_tty, &st))
906	Panic(errno, "Cannot access '%s'", attach_tty);
907#ifdef MULTIUSER
908      tty_mode = (int)st.st_mode & 0777;
909#endif
910      if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
911	Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
912      close(n);
913      debug1("attach_tty is %s\n", attach_tty);
914      if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
915	Panic(0, "Please set a terminal type.");
916      if (strlen(attach_term) > sizeof(D_termname) - 1)
917	Panic(0, "$TERM too long - sorry.");
918      GetTTY(0, &attach_Mode);
919#ifdef DEBUGGGGGGGGGGGGGGG
920      DebugTTY(&attach_Mode);
921#endif /* DEBUG */
922    }
923
924#ifdef _MODE_T
925  oumask = umask(0);		/* well, unsigned never fails? jw. */
926#else
927  if ((oumask = (int)umask(0)) == -1)
928    Panic(errno, "Cannot change umask to zero");
929#endif
930  SockDir = getenv("SCREENDIR");
931  if (SockDir)
932    {
933      if (strlen(SockDir) >= MAXPATHLEN - 1)
934	Panic(0, "Ridiculously long $SCREENDIR - try again.");
935#ifdef MULTIUSER
936      if (multi)
937	Panic(0, "No $SCREENDIR with multi screens, please.");
938#endif
939    }
940#ifdef __APPLE__
941    else if (!multi && real_uid == eff_uid) {
942      static char DarwinSockDir[PATH_MAX];
943      if (confstr(_CS_DARWIN_USER_TEMP_DIR, DarwinSockDir, sizeof(DarwinSockDir))) {
944	strlcat(DarwinSockDir, ".screen", sizeof(DarwinSockDir));
945	SockDir = DarwinSockDir;
946      }
947    }
948#endif	/* __APPLE__ */
949
950#ifdef MULTIUSER
951  if (multiattach)
952    {
953# ifndef SOCKDIR
954      sprintf(SockPath, "%s/.screen", multi_home);
955      SockDir = SockPath;
956# else
957      SockDir = SOCKDIR;
958      sprintf(SockPath, "%s/S-%s", SockDir, multi);
959# endif
960    }
961  else
962#endif
963    {
964#ifndef SOCKDIR
965      if (SockDir == 0)
966	{
967	  sprintf(SockPath, "%s/.screen", home);
968	  SockDir = SockPath;
969	}
970#endif
971      if (SockDir)
972	{
973	  if (access(SockDir, F_OK))
974	    {
975	      debug1("SockDir '%s' missing ...\n", SockDir);
976	      if (UserContext() > 0)
977		{
978		  if (mkdir(SockDir, 0700))
979		    UserReturn(0);
980		  UserReturn(1);
981		}
982	      if (UserStatus() <= 0)
983		Panic(0, "Cannot make directory '%s'.", SockDir);
984	    }
985	  if (SockDir != SockPath)
986	    strcpy(SockPath, SockDir);
987	}
988#ifdef SOCKDIR
989      else
990	{
991	  SockDir = SOCKDIR;
992	  if (lstat(SockDir, &st))
993	    {
994	      n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
995	          (eff_gid != real_gid) ? 0775 :
996#ifdef S_ISVTX
997		  0777|S_ISVTX;
998#else
999		  0777;
1000#endif
1001	      if (mkdir(SockDir, n) == -1)
1002		Panic(errno, "Cannot make directory '%s'", SockDir);
1003	    }
1004	  else
1005	    {
1006	      if (!S_ISDIR(st.st_mode))
1007		Panic(0, "'%s' must be a directory.", SockDir);
1008              if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1009		Panic(0, "Directory '%s' must be owned by root.", SockDir);
1010	      n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1011	          (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1012		  0777;
1013	      if (((int)st.st_mode & 0777) != n)
1014		Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1015	    }
1016	  sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1017	  if (access(SockPath, F_OK))
1018	    {
1019	      if (mkdir(SockPath, 0700) == -1)
1020		Panic(errno, "Cannot make directory '%s'", SockPath);
1021	      (void) chown(SockPath, real_uid, real_gid);
1022	    }
1023	}
1024#endif
1025    }
1026
1027  if (stat(SockPath, &st) == -1)
1028    Panic(errno, "Cannot access %s", SockPath);
1029  else
1030  if (!S_ISDIR(st.st_mode))
1031    Panic(0, "%s is not a directory.", SockPath);
1032#ifdef MULTIUSER
1033  if (multi)
1034    {
1035      if ((int)st.st_uid != multi_uid)
1036	Panic(0, "%s is not the owner of %s.", multi, SockPath);
1037    }
1038  else
1039#endif
1040    {
1041      if ((int)st.st_uid != real_uid)
1042	Panic(0, "You are not the owner of %s.", SockPath);
1043    }
1044  if ((st.st_mode & 0777) != 0700)
1045    Panic(0, "Directory %s must have mode 700.", SockPath);
1046  if (SockMatch && index(SockMatch, '/'))
1047    Panic(0, "Bad session name '%s'", SockMatch);
1048  SockName = SockPath + strlen(SockPath) + 1;
1049  *SockName = 0;
1050  (void) umask(oumask);
1051  debug2("SockPath: %s  SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1052
1053#if defined(SYSV) && !defined(ISC)
1054  if (uname(&utsnam) == -1)
1055    Panic(errno, "uname");
1056  strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1057  HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1058#else
1059  (void) gethostname(HostName, MAXSTR);
1060  HostName[MAXSTR - 1] = '\0';
1061#endif
1062  if ((ap = index(HostName, '.')) != NULL)
1063    *ap = '\0';
1064
1065  if (lsflag)
1066    {
1067      int i, fo, oth;
1068
1069#ifdef MULTIUSER
1070      if (multi)
1071	real_uid = multi_uid;
1072#endif
1073      setgid(real_gid);
1074      setuid(real_uid);
1075      eff_uid = real_uid;
1076      eff_gid = real_gid;
1077      i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1078      if (quietflag)
1079        exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1080      if (fo == 0)
1081        Panic(0, "No Sockets found in %s.\n", SockPath);
1082      Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1083      /* NOTREACHED */
1084    }
1085  signal(SIG_BYE, AttacherFinit);	/* prevent races */
1086  if (cmdflag)
1087    {
1088      char *sty = 0;
1089
1090      /* attach_tty is not mandatory */
1091      if ((attach_tty = ttyname(0)) == 0)
1092        attach_tty = "";
1093      if (strlen(attach_tty) >= MAXPATHLEN)
1094	Panic(0, "TtyName too long - sorry.");
1095      if (!*av)
1096	Panic(0, "Please specify a command.");
1097      setgid(real_gid);
1098      setuid(real_uid);
1099      eff_uid = real_uid;
1100      eff_gid = real_gid;
1101      if (!mflag && !SockMatch)
1102	{
1103	  sty = getenv("STY");
1104	  if (sty && *sty == 0)
1105	    sty = 0;
1106	}
1107      SendCmdMessage(sty, SockMatch, av);
1108      exit(0);
1109    }
1110  else if (rflag || xflag)
1111    {
1112      debug("screen -r: - is there anybody out there?\n");
1113      if (Attach(MSG_ATTACH))
1114	{
1115	  Attacher();
1116	  /* NOTREACHED */
1117	}
1118#ifdef MULTIUSER
1119      if (multiattach)
1120	Panic(0, "Can't create sessions of other users.");
1121#endif
1122      debug("screen -r: backend not responding -- still crying\n");
1123    }
1124  else if (dflag && !mflag)
1125    {
1126      (void) Attach(MSG_DETACH);
1127      Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1128      eexit(0);
1129      /* NOTREACHED */
1130    }
1131  if (!SockMatch && !mflag)
1132    {
1133      register char *sty;
1134
1135      if ((sty = getenv("STY")) != 0 && *sty != '\0')
1136	{
1137	  setgid(real_gid);
1138	  setuid(real_uid);
1139	  eff_uid = real_uid;
1140	  eff_gid = real_gid;
1141	  nwin_options.args = av;
1142	  SendCreateMsg(sty, &nwin);
1143	  exit(0);
1144	  /* NOTREACHED */
1145	}
1146    }
1147  nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1148
1149  if (!detached || dflag != 2)
1150    MasterPid = fork();
1151  else
1152    MasterPid = 0;
1153
1154  switch (MasterPid)
1155    {
1156    case -1:
1157      Panic(errno, "fork");
1158      /* NOTREACHED */
1159    case 0:
1160      break;
1161    default:
1162      if (detached)
1163        exit(0);
1164      if (SockMatch)
1165	sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1166      else
1167	sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1168      for (ap = socknamebuf; *ap; ap++)
1169	if (*ap == '/')
1170	  *ap = '-';
1171#ifdef NAME_MAX
1172      if (strlen(socknamebuf) > NAME_MAX)
1173        socknamebuf[NAME_MAX] = 0;
1174#endif
1175      sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1176      setgid(real_gid);
1177      setuid(real_uid);
1178      eff_uid = real_uid;
1179      eff_gid = real_gid;
1180      Attacher();
1181      /* NOTREACHED */
1182    }
1183
1184  if (DefaultEsc == -1)
1185    DefaultEsc = Ctrl('a');
1186  if (DefaultMetaEsc == -1)
1187    DefaultMetaEsc = 'a';
1188
1189  ap = av0 + strlen(av0) - 1;
1190  while (ap >= av0)
1191    {
1192      if (!strncmp("screen", ap, 6))
1193	{
1194	  strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1195	  break;
1196	}
1197      ap--;
1198    }
1199  if (ap < av0)
1200    *av0 = 'S';
1201
1202#ifdef DEBUG
1203  {
1204    char buf[256];
1205
1206    if (dfp && dfp != stderr)
1207      fclose(dfp);
1208    sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1209    if ((dfp = fopen(buf, "w")) == NULL)
1210      dfp = stderr;
1211    else
1212      (void) chmod(buf, 0666);
1213  }
1214#endif
1215  if (!detached)
1216    {
1217      /* reopen tty. must do this, because fd 0 may be RDONLY */
1218      if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1219	Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1220    }
1221  else
1222    n = -1;
1223  freopen("/dev/null", "r", stdin);
1224  freopen("/dev/null", "w", stdout);
1225
1226#ifdef DEBUG
1227  if (dfp != stderr)
1228#endif
1229  freopen("/dev/null", "w", stderr);
1230  debug("-- screen.back debug started\n");
1231
1232#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
1233	if (_vprocmgr_detach_from_console(0) != NULL)
1234		errx(1, "can't detach from console");
1235#endif
1236
1237  /*
1238   * This guarantees that the session owner is listed, even when we
1239   * start detached. From now on we should not refer to 'LoginName'
1240   * any more, use users->u_name instead.
1241   */
1242  if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1243    Panic(0, "Could not create user info");
1244  if (!detached)
1245    {
1246      if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1247	Panic(0, "Could not alloc display");
1248#ifdef ENCODINGS
1249      D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1250      debug1("D_encoding = %d\n", D_encoding);
1251#endif
1252    }
1253
1254  if (SockMatch)
1255    {
1256      /* user started us with -S option */
1257      sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1258    }
1259  else
1260    {
1261      sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1262	      HostName);
1263    }
1264  for (ap = socknamebuf; *ap; ap++)
1265    if (*ap == '/')
1266      *ap = '-';
1267#ifdef NAME_MAX
1268  if (strlen(socknamebuf) > NAME_MAX)
1269    {
1270      debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1271      socknamebuf[NAME_MAX] = 0;
1272    }
1273#endif
1274  sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1275
1276  ServerSocket = MakeServerSocket();
1277  InitKeytab();
1278#ifdef ETCSCREENRC
1279# ifdef ALLOW_SYSSCREENRC
1280  if ((ap = getenv("SYSSCREENRC")))
1281    StartRc(ap);
1282  else
1283# endif
1284    StartRc(ETCSCREENRC);
1285#endif
1286  StartRc(RcFileName);
1287# ifdef UTMPOK
1288#  ifndef UTNOKEEP
1289  InitUtmp();
1290#  endif /* UTNOKEEP */
1291# endif /* UTMPOK */
1292  if (display)
1293    {
1294      if (InitTermcap(0, 0))
1295	{
1296	  debug("Could not init termcap - exiting\n");
1297	  fcntl(D_userfd, F_SETFL, 0);	/* Flush sets FNBLOCK */
1298	  freetty();
1299	  if (D_userpid)
1300	    Kill(D_userpid, SIG_BYE);
1301	  eexit(1);
1302	}
1303      MakeDefaultCanvas();
1304      InitTerm(0);
1305#ifdef UTMPOK
1306      RemoveLoginSlot();
1307#endif
1308    }
1309  else
1310    MakeTermcap(1);
1311#ifdef LOADAV
1312  InitLoadav();
1313#endif /* LOADAV */
1314  MakeNewEnv();
1315  signal(SIGHUP, SigHup);
1316  signal(SIGINT, FinitHandler);
1317  signal(SIGQUIT, FinitHandler);
1318  signal(SIGTERM, FinitHandler);
1319#ifdef BSDJOBS
1320  signal(SIGTTIN, SIG_IGN);
1321  signal(SIGTTOU, SIG_IGN);
1322#endif
1323
1324  if (display)
1325    {
1326      brktty(D_userfd);
1327      SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1328      /* Note: SetMode must be called _before_ FinishRc. */
1329      SetTTY(D_userfd, &D_NewMode);
1330      if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1331	Msg(errno, "Warning: NBLOCK fcntl failed");
1332    }
1333  else
1334    brktty(-1);		/* just try */
1335  signal(SIGCHLD, SigChld);
1336#ifdef ETCSCREENRC
1337# ifdef ALLOW_SYSSCREENRC
1338  if ((ap = getenv("SYSSCREENRC")))
1339    FinishRc(ap);
1340  else
1341# endif
1342    FinishRc(ETCSCREENRC);
1343#endif
1344  FinishRc(RcFileName);
1345
1346  debug2("UID %d  EUID %d\n", (int)getuid(), (int)geteuid());
1347  if (windows == NULL)
1348    {
1349      debug("We open one default window, as screenrc did not specify one.\n");
1350      if (MakeWindow(&nwin) == -1)
1351	{
1352	  Msg(0, "Sorry, could not find a PTY.");
1353	  sleep(5);
1354	  Finit(0);
1355	  /* NOTREACHED */
1356	}
1357    }
1358
1359#ifdef HAVE_BRAILLE
1360  StartBraille();
1361#endif
1362
1363  if (display && default_startup)
1364    display_copyright();
1365  signal(SIGINT, SigInt);
1366  if (rflag && (rflag & 1) == 0)
1367    {
1368      Msg(0, "New screen...");
1369      rflag = 0;
1370    }
1371
1372  serv_read.type = EV_READ;
1373  serv_read.fd = ServerSocket;
1374  serv_read.handler = serv_read_fn;
1375  evenq(&serv_read);
1376
1377  serv_select.pri = -10;
1378  serv_select.type = EV_ALWAYS;
1379  serv_select.handler = serv_select_fn;
1380  evenq(&serv_select);
1381
1382  logflushev.type = EV_TIMEOUT;
1383  logflushev.handler = logflush_fn;
1384
1385  sched();
1386  /* NOTREACHED */
1387  return 0;
1388}
1389
1390void
1391WindowDied(p)
1392struct win *p;
1393{
1394  if (ZombieKey_destroy)
1395    {
1396      char buf[100], *s;
1397      time_t now;
1398
1399      (void) time(&now);
1400      s = ctime(&now);
1401      if (s && *s)
1402        s[strlen(s) - 1] = '\0';
1403      debug3("window %d (%s) going into zombie state fd %d",
1404	     p->w_number, p->w_title, p->w_ptyfd);
1405#ifdef UTMPOK
1406      if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1407	{
1408	  RemoveUtmp(p);
1409	  p->w_slot = 0;	/* "detached" */
1410	}
1411#endif
1412      CloseDevice(p);
1413
1414      p->w_pid = 0;
1415      ResetWindow(p);
1416      /* p->w_y = p->w_bot; */
1417      p->w_y = MFindUsedLine(p, p->w_bot, 1);
1418      sprintf(buf, "\n\r=== Window terminated (%s) ===", s ? s : "?");
1419      WriteString(p, buf, strlen(buf));
1420      WindowChanged(p, 'f');
1421    }
1422  else
1423    KillWindow(p);
1424#ifdef UTMPOK
1425  CarefulUtmp();
1426#endif
1427}
1428
1429static void
1430SigChldHandler()
1431{
1432  struct stat st;
1433#ifdef DEBUG
1434  fds();
1435#endif
1436  while (GotSigChld)
1437    {
1438      GotSigChld = 0;
1439      DoWait();
1440#ifdef SYSVSIGS
1441      signal(SIGCHLD, SigChld);
1442#endif
1443    }
1444  if (stat(SockPath, &st) == -1)
1445    {
1446      debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1447      if (!RecoverSocket())
1448	{
1449	  debug("SCREEN cannot recover from corrupt Socket, bye\n");
1450	  Finit(1);
1451	}
1452      else
1453	debug1("'%s' reconstructed\n", SockPath);
1454    }
1455  else
1456    debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1457}
1458
1459static sigret_t
1460SigChld SIGDEFARG
1461{
1462  debug("SigChld()\n");
1463  GotSigChld = 1;
1464  SIGRETURN;
1465}
1466
1467sigret_t
1468SigHup SIGDEFARG
1469{
1470  /* Hangup all displays */
1471  while ((display = displays) != 0)
1472    Hangup();
1473  SIGRETURN;
1474}
1475
1476/*
1477 * the backend's Interrupt handler
1478 * we cannot insert the intrc directly, as we never know
1479 * if fore is valid.
1480 */
1481static sigret_t
1482SigInt SIGDEFARG
1483{
1484#if HAZARDOUS
1485  char ibuf;
1486
1487  debug("SigInt()\n");
1488  if (fore && displays)
1489    {
1490# if defined(TERMIO) || defined(POSIX)
1491      ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1492# else
1493      ibuf = displays->d_OldMode.m_tchars.t_intrc;
1494# endif
1495      fore->w_inlen = 0;
1496      write(fore->w_ptyfd, &ibuf, 1);
1497    }
1498#else
1499  signal(SIGINT, SigInt);
1500  debug("SigInt() careful\n");
1501  InterruptPlease = 1;
1502#endif
1503  SIGRETURN;
1504}
1505
1506static sigret_t
1507CoreDump SIGDEFARG
1508{
1509  struct display *disp;
1510  char buf[80];
1511
1512#if defined(SYSVSIGS) && defined(SIGHASARG)
1513  signal(sigsig, SIG_IGN);
1514#endif
1515  setgid(getgid());
1516  setuid(getuid());
1517  unlink("core");
1518#ifdef SIGHASARG
1519  sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1520#else
1521  sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1522#endif
1523#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1524              ""
1525#else /* SHADOWPW  && !DEBUG */
1526              " (core dumped)"
1527#endif /* SHADOWPW  && !DEBUG */
1528              );
1529  for (disp = displays; disp; disp = disp->d_next)
1530    {
1531      fcntl(disp->d_userfd, F_SETFL, 0);
1532      SetTTY(disp->d_userfd, &D_OldMode);
1533      write(disp->d_userfd, buf, strlen(buf));
1534      Kill(disp->d_userpid, SIG_BYE);
1535    }
1536#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1537  Kill(getpid(), SIGKILL);
1538  eexit(11);
1539#else /* SHADOWPW && !DEBUG */
1540  abort();
1541#endif /* SHADOWPW  && !DEBUG */
1542  SIGRETURN;
1543}
1544
1545static void
1546DoWait()
1547{
1548  register int pid;
1549  struct win *p, *next;
1550#ifdef BSDWAIT
1551  union wait wstat;
1552#else
1553  int wstat;
1554#endif
1555
1556#ifdef BSDJOBS
1557# ifndef BSDWAIT
1558  while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1559# else
1560# ifdef USE_WAIT2
1561  /*
1562   * From: rouilj@sni-usa.com (John Rouillard)
1563   * note that WUNTRACED is not documented to work, but it is defined in
1564   * /usr/include/sys/wait.h, so it may work
1565   */
1566  while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1567#  else /* USE_WAIT2 */
1568  while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1569#  endif /* USE_WAIT2 */
1570# endif
1571#else	/* BSDJOBS */
1572  while ((pid = wait(&wstat)) < 0)
1573    if (errno != EINTR)
1574      break;
1575  if (pid > 0)
1576#endif	/* BSDJOBS */
1577    {
1578      for (p = windows; p; p = next)
1579	{
1580	  next = p->w_next;
1581	  if (pid == p->w_pid)
1582	    {
1583#ifdef BSDJOBS
1584	      if (WIFSTOPPED(wstat))
1585		{
1586		  debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, p->w_pid, WSTOPSIG(wstat));
1587#ifdef SIGTTIN
1588		  if (WSTOPSIG(wstat) == SIGTTIN)
1589		    {
1590		      Msg(0, "Suspended (tty input)");
1591		      continue;
1592		    }
1593#endif
1594#ifdef SIGTTOU
1595		  if (WSTOPSIG(wstat) == SIGTTOU)
1596		    {
1597		      Msg(0, "Suspended (tty output)");
1598		      continue;
1599		    }
1600#endif
1601		  /* Try to restart process */
1602		  Msg(0, "Child has been stopped, restarting.");
1603		  if (killpg(p->w_pid, SIGCONT))
1604		    kill(p->w_pid, SIGCONT);
1605		}
1606	      else
1607#endif
1608		{
1609		  WindowDied(p);
1610		}
1611	      break;
1612	    }
1613#ifdef PSEUDOS
1614	  if (p->w_pwin && pid == p->w_pwin->p_pid)
1615	    {
1616	      debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1617	      FreePseudowin(p);
1618	      break;
1619	    }
1620#endif
1621	}
1622      if (p == 0)
1623	{
1624	  debug1("pid %d not found - hope that's ok\n", pid);
1625	}
1626    }
1627}
1628
1629
1630static sigret_t
1631FinitHandler SIGDEFARG
1632{
1633#ifdef SIGHASARG
1634  debug1("FinitHandler called, sig %d.\n", sigsig);
1635#else
1636  debug("FinitHandler called.\n");
1637#endif
1638  Finit(1);
1639  SIGRETURN;
1640}
1641
1642void
1643Finit(i)
1644int i;
1645{
1646  struct win *p, *next;
1647
1648  signal(SIGCHLD, SIG_DFL);
1649  signal(SIGHUP, SIG_IGN);
1650  debug1("Finit(%d);\n", i);
1651  for (p = windows; p; p = next)
1652    {
1653      next = p->w_next;
1654      FreeWindow(p);
1655    }
1656  if (ServerSocket != -1)
1657    {
1658      debug1("we unlink(%s)\n", SockPath);
1659#ifdef USE_SETEUID
1660      xseteuid(real_uid);
1661      xsetegid(real_gid);
1662#endif
1663      (void) unlink(SockPath);
1664#ifdef USE_SETEUID
1665      xseteuid(eff_uid);
1666      xsetegid(eff_gid);
1667#endif
1668    }
1669  for (display = displays; display; display = display->d_next)
1670    {
1671      if (D_status)
1672	RemoveStatus();
1673      FinitTerm();
1674#ifdef UTMPOK
1675      RestoreLoginSlot();
1676#endif
1677      AddStr("[screen is terminating]\r\n");
1678      Flush();
1679      SetTTY(D_userfd, &D_OldMode);
1680      fcntl(D_userfd, F_SETFL, 0);
1681      freetty();
1682      Kill(D_userpid, SIG_BYE);
1683    }
1684  /*
1685   * we _cannot_ call eexit(i) here,
1686   * instead of playing with the Socket above. Sigh.
1687   */
1688  exit(i);
1689}
1690
1691void
1692eexit(e)
1693int e;
1694{
1695  debug("eexit\n");
1696  if (ServerSocket != -1)
1697    {
1698      debug1("we unlink(%s)\n", SockPath);
1699      setgid(real_gid);
1700      setuid(real_uid);
1701      (void) unlink(SockPath);
1702    }
1703  exit(e);
1704}
1705
1706void
1707Hangup()
1708{
1709  if (display == 0)
1710    return;
1711  debug1("Hangup %x\n", display);
1712  if (D_userfd >= 0)
1713    {
1714      close(D_userfd);
1715      D_userfd = -1;
1716    }
1717  if (auto_detach || displays->d_next)
1718    Detach(D_HANGUP);
1719  else
1720    Finit(0);
1721}
1722
1723/*
1724 * Detach now has the following modes:
1725 *D_DETACH	 SIG_BYE	detach backend and exit attacher
1726 *D_HANGUP	 SIG_BYE	detach backend and exit attacher
1727 *D_STOP	 SIG_STOP	stop attacher (and detach backend)
1728 *D_REMOTE	 SIG_BYE	remote detach -- reattach to new attacher
1729 *D_POWER 	 SIG_POWER_BYE 	power detach -- attacher kills his parent
1730 *D_REMOTE_POWER SIG_POWER_BYE	remote power detach -- both
1731 *D_LOCK	 SIG_LOCK	lock the attacher
1732 * (jw)
1733 * we always remove our utmp slots. (even when "lock" or "stop")
1734 * Note: Take extra care here, we may be called by interrupt!
1735 */
1736void
1737Detach(mode)
1738int mode;
1739{
1740  int sign = 0, pid;
1741  struct canvas *cv;
1742  struct win *p;
1743
1744  if (display == 0)
1745    return;
1746
1747  signal(SIGHUP, SIG_IGN);
1748  debug1("Detach(%d)\n", mode);
1749  if (D_status)
1750    RemoveStatus();
1751  FinitTerm();
1752  if (!display)
1753    return;
1754  switch (mode)
1755    {
1756    case D_HANGUP:
1757      sign = SIG_BYE;
1758      break;
1759    case D_DETACH:
1760      AddStr("[detached]\r\n");
1761      sign = SIG_BYE;
1762      break;
1763#ifdef BSDJOBS
1764    case D_STOP:
1765      sign = SIG_STOP;
1766      break;
1767#endif
1768#ifdef REMOTE_DETACH
1769    case D_REMOTE:
1770      AddStr("[remote detached]\r\n");
1771      sign = SIG_BYE;
1772      break;
1773#endif
1774#ifdef POW_DETACH
1775    case D_POWER:
1776      AddStr("[power detached]\r\n");
1777      if (PowDetachString)
1778	{
1779	  AddStr(PowDetachString);
1780	  AddStr("\r\n");
1781	}
1782      sign = SIG_POWER_BYE;
1783      break;
1784#ifdef REMOTE_DETACH
1785    case D_REMOTE_POWER:
1786      AddStr("[remote power detached]\r\n");
1787      if (PowDetachString)
1788	{
1789	  AddStr(PowDetachString);
1790	  AddStr("\r\n");
1791	}
1792      sign = SIG_POWER_BYE;
1793      break;
1794#endif
1795#endif
1796    case D_LOCK:
1797      ClearAll();
1798      sign = SIG_LOCK;
1799      /* tell attacher to lock terminal with a lockprg. */
1800      break;
1801    }
1802#ifdef UTMPOK
1803  if (displays->d_next == 0)
1804    {
1805      for (p = windows; p; p = p->w_next)
1806        {
1807	  if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1808	    {
1809	      RemoveUtmp(p);
1810	      /*
1811	       * Set the slot to 0 to get the window
1812	       * logged in again.
1813	       */
1814	      p->w_slot = (slot_t) 0;
1815	    }
1816	}
1817    }
1818  if (mode != D_HANGUP)
1819    RestoreLoginSlot();
1820#endif
1821  if (displays->d_next == 0 && console_window)
1822    {
1823      if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1824	{
1825	  debug("could not release console - killing window\n");
1826	  KillWindow(console_window);
1827	  display = displays;		/* restore display */
1828	}
1829    }
1830  if (D_fore)
1831    {
1832#ifdef MULTIUSER
1833      ReleaseAutoWritelock(display, D_fore);
1834#endif
1835      D_user->u_detachwin = D_fore->w_number;
1836      D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1837    }
1838  for (cv = D_cvlist; cv; cv = cv->c_next)
1839    {
1840      p = Layer2Window(cv->c_layer);
1841      SetCanvasWindow(cv, 0);
1842      if (p)
1843        WindowChanged(p, 'u');
1844    }
1845
1846  pid = D_userpid;
1847  debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1848  FreeDisplay();
1849  if (displays == 0)
1850    /* Flag detached-ness */
1851    (void) chsock();
1852  /*
1853   * tell father what to do. We do that after we
1854   * freed the tty, thus getty feels more comfortable on hpux
1855   * if it was a power detach.
1856   */
1857  Kill(pid, sign);
1858  debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1859  debug("Detach returns, we are successfully detached.\n");
1860  signal(SIGHUP, SigHup);
1861}
1862
1863static int
1864IsSymbol(e, s)
1865char *e, *s;
1866{
1867  register int l;
1868
1869  l = strlen(s);
1870  return strncmp(e, s, l) == 0 && e[l] == '=';
1871}
1872
1873void
1874MakeNewEnv()
1875{
1876  register char **op, **np;
1877  static char stybuf[MAXSTR];
1878
1879  for (op = environ; *op; ++op)
1880    ;
1881  if (NewEnv)
1882    free((char *)NewEnv);
1883  NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
1884  if (!NewEnv)
1885    Panic(0, strnomem);
1886  sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
1887  *np++ = stybuf;	                /* NewEnv[0] */
1888  *np++ = Term;	                /* NewEnv[1] */
1889  np++;		/* room for SHELL */
1890#ifdef TIOCSWINSZ
1891  np += 2;	/* room for TERMCAP and WINDOW */
1892#else
1893  np += 4;	/* room for TERMCAP WINDOW LINES COLUMNS */
1894#endif
1895
1896  for (op = environ; *op; ++op)
1897    {
1898      if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
1899	  && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
1900	  && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
1901	  && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
1902	 )
1903	*np++ = *op;
1904    }
1905  *np = 0;
1906}
1907
1908void
1909/*VARARGS2*/
1910#if defined(USEVARARGS) && defined(__STDC__)
1911Msg(int err, char *fmt, VA_DOTS)
1912#else
1913Msg(err, fmt, VA_DOTS)
1914int err;
1915char *fmt;
1916VA_DECL
1917#endif
1918{
1919  VA_LIST(ap)
1920  char buf[MAXPATHLEN*2];
1921  char *p = buf;
1922
1923  VA_START(ap, fmt);
1924  fmt = DoNLS(fmt);
1925  (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
1926  VA_END(ap);
1927  if (err)
1928    {
1929      p += strlen(p);
1930      *p++ = ':';
1931      *p++ = ' ';
1932      strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
1933      buf[sizeof(buf) - 1] = 0;
1934    }
1935  debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
1936
1937  if (display && displays)
1938    MakeStatus(buf);
1939  else if (displays)
1940    {
1941      for (display = displays; display; display = display->d_next)
1942	MakeStatus(buf);
1943    }
1944  else if (display)
1945    {
1946      /* no displays but a display - must have forked.
1947       * send message to backend!
1948       */
1949      char *tty = D_usertty;
1950      struct display *olddisplay = display;
1951      display = 0;	/* only send once */
1952      SendErrorMsg(tty, buf);
1953      display = olddisplay;
1954    }
1955  else
1956    printf("%s\r\n", buf);
1957}
1958
1959/*
1960 * Call FinitTerm for all displays, write a message to each and call eexit();
1961 */
1962void
1963/*VARARGS2*/
1964#if defined(USEVARARGS) && defined(__STDC__)
1965Panic(int err, char *fmt, VA_DOTS)
1966#else
1967Panic(err, fmt, VA_DOTS)
1968int err;
1969char *fmt;
1970VA_DECL
1971#endif
1972{
1973  VA_LIST(ap)
1974  char buf[MAXPATHLEN*2];
1975  char *p = buf;
1976
1977  VA_START(ap, fmt);
1978  fmt = DoNLS(fmt);
1979  (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
1980  VA_END(ap);
1981  if (err)
1982    {
1983      p += strlen(p);
1984      *p++ = ':';
1985      *p++ = ' ';
1986      strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
1987      buf[sizeof(buf) - 1] = 0;
1988    }
1989  debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
1990  if (displays == 0 && display == 0)
1991    printf("%s\r\n", buf);
1992  else if (displays == 0)
1993    {
1994      /* no displays but a display - must have forked.
1995       * send message to backend!
1996       */
1997      char *tty = D_usertty;
1998      display = 0;
1999      SendErrorMsg(tty, buf);
2000      sleep(2);
2001      _exit(1);
2002    }
2003  else
2004    for (display = displays; display; display = display->d_next)
2005      {
2006        if (D_status)
2007	  RemoveStatus();
2008        FinitTerm();
2009        Flush();
2010#ifdef UTMPOK
2011        RestoreLoginSlot();
2012#endif
2013        SetTTY(D_userfd, &D_OldMode);
2014        fcntl(D_userfd, F_SETFL, 0);
2015        write(D_userfd, buf, strlen(buf));
2016        write(D_userfd, "\n", 1);
2017        freetty();
2018	if (D_userpid)
2019	  Kill(D_userpid, SIG_BYE);
2020      }
2021#ifdef MULTIUSER
2022  if (tty_oldmode >= 0)
2023    {
2024# ifdef USE_SETEUID
2025      if (setuid(own_uid))
2026        xseteuid(own_uid);	/* may be a loop. sigh. */
2027# else
2028      setuid(own_uid);
2029# endif
2030      debug1("Panic: changing back modes from %s\n", attach_tty);
2031      chmod(attach_tty, tty_oldmode);
2032    }
2033#endif
2034  eexit(1);
2035}
2036
2037
2038/*
2039 * '^' is allowed as an escape mechanism for control characters. jw.
2040 *
2041 * Added time insertion using ideas/code from /\ndy Jones
2042 *   (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2043 *
2044 */
2045
2046#ifndef USE_LOCALE
2047static const char days[]   = "SunMonTueWedThuFriSat";
2048static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2049#endif
2050
2051static char winmsg_buf[MAXSTR];
2052#define MAX_WINMSG_REND 16	/* rendition changes */
2053static int winmsg_rend[MAX_WINMSG_REND];
2054static int winmsg_rendpos[MAX_WINMSG_REND];
2055static int winmsg_numrend;
2056
2057static char *
2058pad_expand(buf, p, numpad, padlen)
2059char *buf;
2060char *p;
2061int numpad;
2062int padlen;
2063{
2064  char *pn, *pn2;
2065  int i, r;
2066
2067  padlen = padlen - (p - buf);	/* space for rent */
2068  if (padlen < 0)
2069    padlen = 0;
2070  pn2 = pn = p + padlen;
2071  r = winmsg_numrend;
2072  while (p >= buf)
2073    {
2074      if (r && p - buf == winmsg_rendpos[r - 1])
2075	{
2076	  winmsg_rendpos[--r] = pn - buf;
2077	  continue;
2078	}
2079      *pn-- = *p;
2080      if (*p-- == 127)
2081	{
2082	  pn[1] = ' ';
2083	  i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2084	  padlen -= i;
2085	  while (i-- > 0)
2086	    *pn-- = ' ';
2087	  numpad--;
2088	}
2089    }
2090  return pn2;
2091}
2092
2093struct backtick {
2094  struct backtick *next;
2095  int num;
2096  int tick;
2097  int lifespan;
2098  time_t bestbefore;
2099  char result[MAXSTR];
2100  char **cmdv;
2101  struct event ev;
2102  char *buf;
2103  int bufi;
2104};
2105
2106struct backtick *backticks;
2107
2108static void
2109backtick_filter(bt)
2110struct backtick *bt;
2111{
2112  char *p, *q;
2113  int c;
2114
2115  for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2116    {
2117      if (c == '\t')
2118	c = ' ';
2119      if (c >= ' ' || c == '\005')
2120	*q++ = c;
2121    }
2122  *q = 0;
2123}
2124
2125static void
2126backtick_fn(ev, data)
2127struct event *ev;
2128char *data;
2129{
2130  struct backtick *bt;
2131  int i, j, k, l;
2132
2133  bt = (struct backtick *)data;
2134  debug1("backtick_fn for #%d\n", bt->num);
2135  i = bt->bufi;
2136  l = read(ev->fd, bt->buf + i, MAXSTR - i);
2137  if (l <= 0)
2138    {
2139      debug1("EOF on backtick #%d\n", bt->num);
2140      evdeq(ev);
2141      close(ev->fd);
2142      ev->fd = -1;
2143      return;
2144    }
2145  debug1("read %d bytes\n", l);
2146  i += l;
2147  for (j = 0; j < l; j++)
2148    if (bt->buf[i - j - 1] == '\n')
2149      break;
2150  if (j < l)
2151    {
2152      for (k = i - j - 2; k >= 0; k--)
2153	if (bt->buf[k] == '\n')
2154	  break;
2155      k++;
2156      bcopy(bt->buf + k, bt->result, i - j - k);
2157      bt->result[i - j - k - 1] = 0;
2158      backtick_filter(bt);
2159      WindowChanged(0, '`');
2160    }
2161  if (j == l && i == MAXSTR)
2162    {
2163      j = MAXSTR/2;
2164      l = j + 1;
2165    }
2166  if (j < l)
2167    {
2168      if (j)
2169        bcopy(bt->buf + i - j, bt->buf, j);
2170      i = j;
2171    }
2172  bt->bufi = i;
2173}
2174
2175void
2176setbacktick(num, lifespan, tick, cmdv)
2177int num;
2178int lifespan;
2179int tick;
2180char **cmdv;
2181{
2182  struct backtick **btp, *bt;
2183  char **v;
2184
2185  debug1("setbacktick called for backtick #%d\n", num);
2186  for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2187    if (bt->num == num)
2188      break;
2189  if (!bt && !cmdv)
2190    return;
2191  if (bt)
2192    {
2193      for (v = bt->cmdv; *v; v++)
2194	free(*v);
2195      free(bt->cmdv);
2196      if (bt->buf)
2197	free(bt->buf);
2198      if (bt->ev.fd >= 0)
2199	close(bt->ev.fd);
2200      evdeq(&bt->ev);
2201    }
2202  if (bt && !cmdv)
2203    {
2204      *btp = bt->next;
2205      free(bt);
2206      return;
2207    }
2208  if (!bt)
2209    {
2210      bt = (struct backtick *)malloc(sizeof *bt);
2211      if (!bt)
2212	{
2213	  Msg(0, strnomem);
2214          return;
2215	}
2216      bzero(bt, sizeof(*bt));
2217      bt->next = 0;
2218      *btp = bt;
2219    }
2220  bt->num = num;
2221  bt->tick = tick;
2222  bt->lifespan = lifespan;
2223  bt->bestbefore = 0;
2224  bt->result[0] = 0;
2225  bt->buf = 0;
2226  bt->bufi = 0;
2227  bt->cmdv = cmdv;
2228  bt->ev.fd = -1;
2229  if (bt->tick == 0 && bt->lifespan == 0)
2230    {
2231      debug("setbacktick: continuous mode\n");
2232      bt->buf = (char *)malloc(MAXSTR);
2233      if (bt->buf == 0)
2234	{
2235	  Msg(0, strnomem);
2236	  setbacktick(num, 0, 0, (char **)0);
2237          return;
2238	}
2239      bt->ev.type = EV_READ;
2240      bt->ev.fd = readpipe(bt->cmdv);
2241      bt->ev.handler = backtick_fn;
2242      bt->ev.data = (char *)bt;
2243      if (bt->ev.fd >= 0)
2244	evenq(&bt->ev);
2245    }
2246}
2247
2248static char *
2249runbacktick(bt, tickp, now)
2250struct backtick *bt;
2251int *tickp;
2252time_t now;
2253{
2254  int f, i, l, j;
2255  time_t now2;
2256
2257  debug1("runbacktick called for backtick #%d\n", bt->num);
2258  if (bt->tick && (!*tickp || bt->tick < *tickp))
2259    *tickp = bt->tick;
2260  if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2261    {
2262      debug1("returning old result (%d)\n", bt->lifespan);
2263      return bt->result;
2264    }
2265  f = readpipe(bt->cmdv);
2266  if (f == -1)
2267    return bt->result;
2268  i = 0;
2269  while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2270    {
2271      debug1("runbacktick: read %d bytes\n", l);
2272      i += l;
2273      for (j = 1; j < l; j++)
2274	if (bt->result[i - j - 1] == '\n')
2275	  break;
2276      if (j == l && i == sizeof(bt->result))
2277	{
2278	  j = sizeof(bt->result) / 2;
2279	  l = j + 1;
2280	}
2281      if (j < l)
2282	{
2283	  bcopy(bt->result + i - j, bt->result, j);
2284	  i = j;
2285	}
2286    }
2287  close(f);
2288  bt->result[sizeof(bt->result) - 1] = '\n';
2289  if (i && bt->result[i - 1] == '\n')
2290    i--;
2291  debug1("runbacktick: finished, %d bytes\n", i);
2292  bt->result[i] = 0;
2293  backtick_filter(bt);
2294  (void)time(&now2);
2295  bt->bestbefore = now2 + bt->lifespan;
2296  return bt->result;
2297}
2298
2299char *
2300MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2301char *str;
2302struct win *win;
2303int esc;
2304int padlen;
2305struct event *ev;
2306int rec;
2307{
2308  static int tick;
2309  char *s = str;
2310  register char *p = winmsg_buf;
2311  register int ctrl;
2312  struct timeval now;
2313  struct tm *tm;
2314  int l, i, r;
2315  int num;
2316  int zeroflg;
2317  int longflg;
2318  int minusflg;
2319  int plusflg;
2320  int qmflag = 0, omflag = 0, qmnumrend = 0;
2321  char *qmpos = 0;
2322  int numpad = 0;
2323  int lastpad = 0;
2324  int truncpos = -1;
2325  int truncper = 0;
2326  int trunclong = 0;
2327  struct backtick *bt;
2328
2329  if (winmsg_numrend >= 0)
2330    winmsg_numrend = 0;
2331  else
2332    winmsg_numrend = -winmsg_numrend;
2333
2334  tick = 0;
2335  tm = 0;
2336  ctrl = 0;
2337  gettimeofday(&now, NULL);
2338  for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2339    {
2340      *p = *s;
2341      if (ctrl)
2342	{
2343	  ctrl = 0;
2344	  if (*s != '^' && *s >= 64)
2345	    *p &= 0x1f;
2346	  continue;
2347	}
2348      if (*s != esc)
2349	{
2350	  if (esc == '%')
2351	    {
2352	      switch (*s)
2353		{
2354#if 0
2355		case '~':
2356		  *p = BELL;
2357		  break;
2358#endif
2359		case '^':
2360		  ctrl = 1;
2361		  *p-- = '^';
2362		  break;
2363		default:
2364		  break;
2365		}
2366	    }
2367	  continue;
2368	}
2369      if (*++s == esc)	/* double escape ? */
2370	continue;
2371      if ((plusflg = *s == '+') != 0)
2372	s++;
2373      if ((minusflg = *s == '-') != 0)
2374	s++;
2375      if ((zeroflg = *s == '0') != 0)
2376	s++;
2377      num = 0;
2378      while(*s >= '0' && *s <= '9')
2379	num = num * 10 + (*s++ - '0');
2380      if ((longflg = *s == 'L') != 0)
2381	s++;
2382      switch (*s)
2383	{
2384        case '?':
2385	  p--;
2386	  if (qmpos)
2387	    {
2388	      if ((!qmflag && !omflag) || omflag == 1)
2389		{
2390	          p = qmpos;
2391	          if (qmnumrend < winmsg_numrend)
2392		    winmsg_numrend = qmnumrend;
2393		}
2394	      qmpos = 0;
2395	      break;
2396	    }
2397	  qmpos = p;
2398	  qmnumrend = winmsg_numrend;
2399	  qmflag = omflag = 0;
2400	  break;
2401        case ':':
2402	  p--;
2403	  if (!qmpos)
2404	    break;
2405	  if (qmflag && omflag != 1)
2406	    {
2407	      omflag = 1;
2408	      qmpos = p;
2409	      qmnumrend = winmsg_numrend;
2410	    }
2411	  else
2412	    {
2413	      p = qmpos;
2414	      if (qmnumrend < winmsg_numrend)
2415		winmsg_numrend = qmnumrend;
2416	      omflag = -1;
2417	    }
2418	  break;
2419	case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2420	case 'a': case 'A': case 's': case 'c': case 'C':
2421	  if (l < 4)
2422	    break;
2423	  if (tm == 0)
2424            {
2425	      time_t nowsec = now.tv_sec;
2426	      tm = localtime(&nowsec);
2427	    }
2428	  qmflag = 1;
2429	  if (!tick || tick > 3600)
2430	    tick = 3600;
2431	  switch (*s)
2432	    {
2433	    case 'd':
2434	      sprintf(p, "%02d", tm->tm_mday % 100);
2435	      break;
2436	    case 'D':
2437#ifdef USE_LOCALE
2438	      strftime(p, l, (longflg ? "%A" : "%a"), tm);
2439#else
2440	      sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2441#endif
2442	      break;
2443	    case 'm':
2444	      sprintf(p, "%02d", tm->tm_mon + 1);
2445	      break;
2446	    case 'M':
2447#ifdef USE_LOCALE
2448	      strftime(p, l, (longflg ? "%B" : "%b"), tm);
2449#else
2450	      sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2451#endif
2452	      break;
2453	    case 'y':
2454	      sprintf(p, "%02d", tm->tm_year % 100);
2455	      break;
2456	    case 'Y':
2457	      sprintf(p, "%04d", tm->tm_year + 1900);
2458	      break;
2459	    case 'a':
2460	      sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2461	      break;
2462	    case 'A':
2463	      sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2464	      break;
2465	    case 's':
2466	      sprintf(p, "%02d", tm->tm_sec);
2467	      tick = 1;
2468	      break;
2469	    case 'c':
2470	      sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2471	      if (!tick || tick > 60)
2472		tick = 60;
2473	      break;
2474	    case 'C':
2475	      sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2476	      if (!tick || tick > 60)
2477		tick = 60;
2478	      break;
2479	    default:
2480	      break;
2481	    }
2482	  p += strlen(p) - 1;
2483	  break;
2484	case 'l':
2485#ifdef LOADAV
2486	  *p = 0;
2487	  if (l > 20)
2488	    AddLoadav(p);
2489	  if (*p)
2490	    {
2491	      qmflag = 1;
2492	      p += strlen(p) - 1;
2493	    }
2494	  else
2495	    *p = '?';
2496	  if (!tick || tick > 60)
2497	    tick = 60;
2498#else
2499	  *p = '?';
2500#endif
2501	  p += strlen(p) - 1;
2502	  break;
2503	case '`':
2504	case 'h':
2505	  if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2506	    {
2507	      p--;
2508	      break;
2509	    }
2510	  if (*s == '`')
2511	    {
2512	      for (bt = backticks; bt; bt = bt->next)
2513		if (bt->num == num)
2514		  break;
2515	      if (bt == 0)
2516		{
2517		  p--;
2518		  break;
2519		}
2520	    }
2521	    {
2522	      char savebuf[sizeof(winmsg_buf)];
2523	      int oldtick = tick;
2524	      int oldnumrend = winmsg_numrend;
2525
2526	      *p = 0;
2527	      strcpy(savebuf, winmsg_buf);
2528	      winmsg_numrend = -winmsg_numrend;
2529	      MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2530	      debug2("oldtick=%d tick=%d\n", oldtick, tick);
2531	      if (!tick || oldtick < tick)
2532		tick = oldtick;
2533	      if ((int)strlen(winmsg_buf) < l)
2534		strcat(savebuf, winmsg_buf);
2535	      strcpy(winmsg_buf, savebuf);
2536	      while (oldnumrend < winmsg_numrend)
2537		winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2538	      if (*p)
2539		qmflag = 1;
2540	      p += strlen(p) - 1;
2541	    }
2542	  break;
2543	case 'w':
2544	case 'W':
2545	  {
2546	    struct win *oldfore = 0;
2547	    char *ss;
2548
2549	    if (display)
2550	      {
2551		oldfore = D_fore;
2552		D_fore = win;
2553	      }
2554	    ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
2555	    if (minusflg)
2556	       *ss = 0;
2557	    if (display)
2558	      D_fore = oldfore;
2559	  }
2560	  if (*p)
2561	    qmflag = 1;
2562	  p += strlen(p) - 1;
2563	  break;
2564	case 'u':
2565	  *p = 0;
2566	  if (win)
2567	    AddOtherUsers(p, l - 1, win);
2568	  if (*p)
2569	    qmflag = 1;
2570	  p += strlen(p) - 1;
2571	  break;
2572	case 'f':
2573	  *p = 0;
2574	  if (win)
2575	    AddWindowFlags(p, l - 1, win);
2576	  if (*p)
2577	    qmflag = 1;
2578	  p += strlen(p) - 1;
2579	  break;
2580	case 't':
2581	  *p = 0;
2582	  if (win && (int)strlen(win->w_title) < l)
2583	    {
2584	      strcpy(p, win->w_title);
2585	      if (*p)
2586		qmflag = 1;
2587	    }
2588	  p += strlen(p) - 1;
2589	  break;
2590	case '{':
2591          {
2592	    char rbuf[128];
2593	    s++;
2594	    for (i = 0; i < 127; i++)
2595	      if (s[i] && s[i] != '}')
2596		rbuf[i] = s[i];
2597	      else
2598		break;
2599	    if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2600	      {
2601		r = -1;
2602		rbuf[i] = 0;
2603		debug1("MakeWinMsg attrcolor %s\n", rbuf);
2604	        if (i != 1 || rbuf[0] != '-')
2605		  r = ParseAttrColor(rbuf, (char *)0, 0);
2606	        if (r != -1 || (i == 1 && rbuf[0] == '-'))
2607		  {
2608		    winmsg_rend[winmsg_numrend] = r;
2609		    winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2610		    winmsg_numrend++;
2611		  }
2612	      }
2613	    s += i;
2614	    p--;
2615          }
2616	  break;
2617	case 'H':
2618	  *p = 0;
2619	  if ((int)strlen(HostName) < l)
2620	    {
2621	      strcpy(p, HostName);
2622	      if (*p)
2623		qmflag = 1;
2624	    }
2625	  p += strlen(p) - 1;
2626	  break;
2627	case 'F':
2628	  p--;
2629	  /* small hack */
2630	  if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2631	    qmflag = 1;
2632	  break;
2633	case '>':
2634	  truncpos = p - winmsg_buf;
2635	  truncper = num > 100 ? 100 : num;
2636	  trunclong = longflg;
2637	  p--;
2638	  break;
2639	case '=':
2640	case '<':
2641	  *p = ' ';
2642	  if (num || zeroflg || plusflg || longflg || (*s != '='))
2643	    {
2644	      /* expand all pads */
2645	      if (minusflg)
2646		{
2647		  num = (plusflg ? lastpad : padlen) - num;
2648		  if (!plusflg && padlen == 0)
2649		    num = p - winmsg_buf;
2650		  plusflg = 0;
2651		}
2652	      else if (!zeroflg)
2653		{
2654		  if (*s != '=' && num == 0 && !plusflg)
2655		    num = 100;
2656		  if (num > 100)
2657		    num = 100;
2658		  if (padlen == 0)
2659		    num = p - winmsg_buf;
2660		  else
2661		    num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2662		}
2663	      if (num < 0)
2664		num = 0;
2665	      if (plusflg)
2666		num += lastpad;
2667	      if (num > MAXSTR - 1)
2668		num = MAXSTR - 1;
2669	      if (numpad)
2670	        p = pad_expand(winmsg_buf, p, numpad, num);
2671	      numpad = 0;
2672	      if (p - winmsg_buf > num && !longflg)
2673		{
2674		  int left, trunc;
2675
2676		  if (truncpos == -1)
2677		    {
2678		      truncpos = lastpad;
2679		      truncper = 0;
2680		    }
2681		  trunc = lastpad + truncper * (num - lastpad) / 100;
2682		  if (trunc > num)
2683		    trunc = num;
2684		  if (trunc < lastpad)
2685		    trunc = lastpad;
2686		  left = truncpos - trunc;
2687		  if (left > p - winmsg_buf - num)
2688		    left = p - winmsg_buf - num;
2689		  debug1("lastpad = %d, ", lastpad);
2690		  debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2691		  if (left > 0)
2692		    {
2693		      if (left + lastpad > p - winmsg_buf)
2694			left = p - winmsg_buf - lastpad;
2695		      if (p - winmsg_buf - lastpad - left > 0)
2696		        bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad,  p - winmsg_buf - lastpad - left);
2697		      p -= left;
2698		      r = winmsg_numrend;
2699		      while (r && winmsg_rendpos[r - 1] > lastpad)
2700			{
2701			  r--;
2702			  winmsg_rendpos[r] -= left;
2703			  if (winmsg_rendpos[r] < lastpad)
2704			    winmsg_rendpos[r] = lastpad;
2705			}
2706		      if (trunclong)
2707			{
2708			  if (p - winmsg_buf > lastpad)
2709			    winmsg_buf[lastpad] = '.';
2710			  if (p - winmsg_buf > lastpad + 1)
2711			    winmsg_buf[lastpad + 1] = '.';
2712			  if (p - winmsg_buf > lastpad + 2)
2713			    winmsg_buf[lastpad + 2] = '.';
2714			}
2715		    }
2716		  if (p - winmsg_buf > num)
2717		    {
2718		      p = winmsg_buf + num;
2719		      if (trunclong)
2720			{
2721			  if (num - 1 >= lastpad)
2722			    p[-1] = '.';
2723			  if (num - 2 >= lastpad)
2724			    p[-2] = '.';
2725			  if (num - 3 >= lastpad)
2726			    p[-3] = '.';
2727			}
2728		      r = winmsg_numrend;
2729		      while (r && winmsg_rendpos[r - 1] > num)
2730			winmsg_rendpos[--r] = num;
2731		    }
2732		  truncpos = -1;
2733		  trunclong = 0;
2734		  if (lastpad > p - winmsg_buf)
2735		    lastpad = p - winmsg_buf;
2736		  debug1("lastpad now %d\n", lastpad);
2737		}
2738	      if (*s == '=')
2739		{
2740		  while (p - winmsg_buf < num)
2741		    *p++ = ' ';
2742		  lastpad = p - winmsg_buf;
2743		  truncpos = -1;
2744		  trunclong = 0;
2745		  debug1("lastpad2 now %d\n", lastpad);
2746		}
2747	      p--;
2748	    }
2749	  else if (padlen)
2750	    {
2751	      *p = 127;		/* internal pad representation */
2752	      numpad++;
2753	    }
2754	  break;
2755	case 'n':
2756	  s++;
2757	  /* FALLTHROUGH */
2758	default:
2759	  s--;
2760	  if (l > 10 + num)
2761	    {
2762	      if (num == 0)
2763		num = 1;
2764	      if (!win)
2765	        sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2766	      else
2767	        sprintf(p, "%*d", num, win->w_number);
2768	      qmflag = 1;
2769	      p += strlen(p) - 1;
2770	    }
2771	  break;
2772	}
2773    }
2774  if (qmpos && !qmflag)
2775    p = qmpos + 1;
2776  *p = '\0';
2777  if (numpad)
2778    {
2779      if (padlen > MAXSTR - 1)
2780	padlen = MAXSTR - 1;
2781      p = pad_expand(winmsg_buf, p, numpad, padlen);
2782    }
2783  if (ev)
2784    {
2785      evdeq(ev);		/* just in case */
2786      ev->timeout.tv_sec = 0;
2787      ev->timeout.tv_usec = 0;
2788    }
2789  if (ev && tick)
2790    {
2791      now.tv_usec = 100000;
2792      if (tick == 1)
2793	now.tv_sec++;
2794      else
2795	now.tv_sec += tick - (now.tv_sec % tick);
2796      ev->timeout = now;
2797      debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2798    }
2799  return winmsg_buf;
2800}
2801
2802char *
2803MakeWinMsg(s, win, esc)
2804char *s;
2805struct win *win;
2806int esc;
2807{
2808  return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2809}
2810
2811int
2812PutWinMsg(s, start, max)
2813char *s;
2814int start, max;
2815{
2816  int i, p, l, r, n;
2817  struct mchar rend;
2818  struct mchar rendstack[MAX_WINMSG_REND];
2819  int rendstackn = 0;
2820
2821  if (s != winmsg_buf)
2822    return 0;
2823  rend = D_rend;
2824  p = 0;
2825  l = strlen(s);
2826  debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
2827  for (i = 0; i < winmsg_numrend && max > 0; i++)
2828    {
2829      if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
2830	break;
2831      if (p < winmsg_rendpos[i])
2832	{
2833	  n = winmsg_rendpos[i] - p;
2834	  if (n > max)
2835	    n = max;
2836	  max -= n;
2837	  p += n;
2838	  while(n-- > 0)
2839	    {
2840	      if (start-- > 0)
2841		s++;
2842	      else
2843	        PUTCHARLP(*s++);
2844	    }
2845	}
2846      r = winmsg_rend[i];
2847      if (r == -1)
2848	{
2849	  if (rendstackn > 0)
2850	    rend = rendstack[--rendstackn];
2851	}
2852      else
2853	{
2854	  rendstack[rendstackn++] = rend;
2855	  ApplyAttrColor(r, &rend);
2856	}
2857      SetRendition(&rend);
2858    }
2859  if (p < l)
2860    {
2861      n = l - p;
2862      if (n > max)
2863	n = max;
2864      while(n-- > 0)
2865	{
2866	  if (start-- > 0)
2867	    s++;
2868	  else
2869	    PUTCHARLP(*s++);
2870	}
2871    }
2872  return 1;
2873}
2874
2875
2876#ifdef DEBUG
2877static void
2878fds1(i, j)
2879int i, j;
2880{
2881  while (i < j)
2882    {
2883      debug1("%d ", i);
2884      i++;
2885    }
2886  if ((j = open("/dev/null", 0)) >= 0)
2887    {
2888      fds1(i + 1, j);
2889      close(j);
2890    }
2891  else
2892    {
2893      while (dup(++i) < 0 && errno != EBADF)
2894        debug1("%d ", i);
2895      debug1(" [%d]\n", i);
2896    }
2897}
2898
2899static void
2900fds()
2901{
2902  debug("fds: ");
2903  fds1(-1, -1);
2904}
2905#endif
2906
2907static void
2908serv_read_fn(ev, data)
2909struct event *ev;
2910char *data;
2911{
2912  debug("Knock - knock!\n");
2913  ReceiveMsg();
2914}
2915
2916static void
2917serv_select_fn(ev, data)
2918struct event *ev;
2919char *data;
2920{
2921  struct win *p;
2922
2923  debug("serv_select_fn called\n");
2924  /* XXX: messages?? */
2925  if (GotSigChld)
2926    {
2927      SigChldHandler();
2928    }
2929  if (InterruptPlease)
2930    {
2931      debug("Backend received interrupt\n");
2932      /* This approach is rather questionable in a multi-display
2933       * environment */
2934      if (fore && displays)
2935	{
2936#if defined(TERMIO) || defined(POSIX)
2937	  char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
2938#else
2939	  char ibuf = displays->d_OldMode.m_tchars.t_intrc;
2940#endif
2941#ifdef PSEUDOS
2942	  write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
2943		&ibuf, 1);
2944	  debug1("Backend wrote interrupt to %d", fore->w_number);
2945	  debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
2946#else
2947	  write(fore->w_ptyfd, &ibuf, 1);
2948	  debug1("Backend wrote interrupt to %d\n", fore->w_number);
2949#endif
2950	}
2951      InterruptPlease = 0;
2952    }
2953
2954  for (p = windows; p; p = p->w_next)
2955    {
2956      if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
2957	{
2958	  struct canvas *cv;
2959	  int visual = p->w_bell == BELL_VISUAL || visual_bell;
2960	  p->w_bell = BELL_ON;
2961	  for (display = displays; display; display = display->d_next)
2962	    {
2963	      for (cv = D_cvlist; cv; cv = cv->c_next)
2964		if (cv->c_layer->l_bottom == &p->w_layer)
2965		  break;
2966	      if (cv == 0)
2967		{
2968		  p->w_bell = BELL_DONE;
2969		  Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
2970		}
2971	      else if (visual && !D_VB && (!D_status || !D_status_bell))
2972		{
2973		  Msg(0, "%s", VisualBellString);
2974		  if (D_status)
2975		    {
2976		      D_status_bell = 1;
2977		      debug1("using vbell timeout %d\n", VBellWait);
2978		      SetTimeout(&D_statusev, VBellWait );
2979		    }
2980		}
2981	    }
2982	  /* don't annoy the user with two messages */
2983	  if (p->w_monitor == MON_FOUND)
2984	    p->w_monitor = MON_DONE;
2985          WindowChanged(p, 'f');
2986	}
2987      if (p->w_monitor == MON_FOUND)
2988	{
2989	  struct canvas *cv;
2990	  p->w_monitor = MON_ON;
2991	  for (display = displays; display; display = display->d_next)
2992	    {
2993	      for (cv = D_cvlist; cv; cv = cv->c_next)
2994		if (cv->c_layer->l_bottom == &p->w_layer)
2995		  break;
2996	      if (cv)
2997		continue;	/* user already sees window */
2998#ifdef MULTIUSER
2999	      if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3000		continue;	/* user doesn't care */
3001#endif
3002	      Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3003	      p->w_monitor = MON_DONE;
3004	    }
3005          WindowChanged(p, 'f');
3006	}
3007    }
3008
3009  for (display = displays; display; display = display->d_next)
3010    {
3011      struct canvas *cv;
3012      if (D_status == STATUS_ON_WIN)
3013	continue;
3014      /* XXX: should use display functions! */
3015      for (cv = D_cvlist; cv; cv = cv->c_next)
3016	{
3017	  int lx, ly;
3018
3019	  /* normalize window, see resize.c */
3020	  lx = cv->c_layer->l_x;
3021	  ly = cv->c_layer->l_y;
3022	  if (lx == cv->c_layer->l_width)
3023	    lx--;
3024	  if (ly + cv->c_yoff < cv->c_ys)
3025	    {
3026	      int i, n = cv->c_ys - (ly + cv->c_yoff);
3027	      cv->c_yoff = cv->c_ys - ly;
3028	      RethinkViewportOffsets(cv);
3029	      if (n > cv->c_layer->l_height)
3030		n = cv->c_layer->l_height;
3031	      CV_CALL(cv,
3032		LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3033		LayRedisplayLine(-1, -1, -1, 1);
3034		for (i = 0; i < n; i++)
3035		  LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3036	        if (cv == cv->c_display->d_forecv)
3037	          LaySetCursor();
3038	      );
3039	    }
3040	  else if (ly + cv->c_yoff > cv->c_ye)
3041	    {
3042	      int i, n = ly + cv->c_yoff - cv->c_ye;
3043	      cv->c_yoff = cv->c_ye - ly;
3044	      RethinkViewportOffsets(cv);
3045	      if (n > cv->c_layer->l_height)
3046		n = cv->c_layer->l_height;
3047	      CV_CALL(cv,
3048	        LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3049		LayRedisplayLine(-1, -1, -1, 1);
3050		for (i = 0; i < n; i++)
3051		  LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3052	        if (cv == cv->c_display->d_forecv)
3053	          LaySetCursor();
3054	      );
3055	    }
3056	  if (lx + cv->c_xoff < cv->c_xs)
3057	    {
3058	      int i, n = cv->c_xs - (lx + cv->c_xoff);
3059	      if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3060		n = (cv->c_xe - cv->c_xs + 1) / 2;
3061	      if (cv->c_xoff + n > cv->c_xs)
3062		n = cv->c_xs - cv->c_xoff;
3063	      cv->c_xoff += n;
3064	      RethinkViewportOffsets(cv);
3065	      if (n > cv->c_layer->l_width)
3066		n = cv->c_layer->l_width;
3067	      CV_CALL(cv,
3068		LayRedisplayLine(-1, -1, -1, 1);
3069		for (i = 0; i < flayer->l_height; i++)
3070		  {
3071		    LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3072		    LayRedisplayLine(i, 0, n - 1, 1);
3073		  }
3074	        if (cv == cv->c_display->d_forecv)
3075	          LaySetCursor();
3076	      );
3077	    }
3078	  else if (lx + cv->c_xoff > cv->c_xe)
3079	    {
3080	      int i, n = lx + cv->c_xoff - cv->c_xe;
3081	      if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3082		n = (cv->c_xe - cv->c_xs + 1) / 2;
3083	      if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3084		n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3085	      cv->c_xoff -= n;
3086	      RethinkViewportOffsets(cv);
3087	      if (n > cv->c_layer->l_width)
3088		n = cv->c_layer->l_width;
3089	      CV_CALL(cv,
3090		LayRedisplayLine(-1, -1, -1, 1);
3091		for (i = 0; i < flayer->l_height; i++)
3092		  {
3093		    LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3094		    LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3095		  }
3096	        if (cv == cv->c_display->d_forecv)
3097	          LaySetCursor();
3098	      );
3099	    }
3100	}
3101    }
3102
3103  for (display = displays; display; display = display->d_next)
3104    {
3105      if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3106	continue;
3107      debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3108      CV_CALL(D_forecv, LayRestore();LaySetCursor());
3109    }
3110}
3111
3112static void
3113logflush_fn(ev, data)
3114struct event *ev;
3115char *data;
3116{
3117  struct win *p;
3118  char *buf;
3119  int n;
3120
3121  if (!islogfile(NULL))
3122    return;		/* no more logfiles */
3123  logfflush(NULL);
3124  n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3125  if (n)
3126    {
3127      SetTimeout(ev, n * 1000);
3128      evenq(ev);	/* re-enqueue ourself */
3129    }
3130  if (!logtstamp_on)
3131    return;
3132  /* write fancy time-stamp */
3133  for (p = windows; p; p = p->w_next)
3134    {
3135      if (!p->w_log)
3136	continue;
3137      p->w_logsilence += n;
3138      if (p->w_logsilence < logtstamp_after)
3139	continue;
3140      if (p->w_logsilence - n >= logtstamp_after)
3141	continue;
3142      buf = MakeWinMsg(logtstamp_string, p, '%');
3143      logfwrite(p->w_log, buf, strlen(buf));
3144    }
3145}
3146
3147/*
3148 * Interprets ^?, ^@ and other ^-control-char notation.
3149 * Interprets \ddd octal notation
3150 *
3151 * The result is placed in *cp, p is advanced behind the parsed expression and
3152 * returned.
3153 */
3154static char *
3155ParseChar(p, cp)
3156char *p, *cp;
3157{
3158  if (*p == 0)
3159    return 0;
3160  if (*p == '^' && p[1])
3161    {
3162      if (*++p == '?')
3163        *cp = '\177';
3164      else if (*p >= '@')
3165        *cp = Ctrl(*p);
3166      else
3167        return 0;
3168      ++p;
3169    }
3170  else if (*p == '\\' && *++p <= '7' && *p >= '0')
3171    {
3172      *cp = 0;
3173      do
3174        *cp = *cp * 8 + *p - '0';
3175      while (*++p <= '7' && *p >= '0');
3176    }
3177  else
3178    *cp = *p++;
3179  return p;
3180}
3181
3182static int
3183ParseEscape(p)
3184char *p;
3185{
3186  unsigned char buf[2];
3187
3188  if (*p == 0)
3189    SetEscape((struct acluser *)0, -1, -1);
3190  else
3191    {
3192      if ((p = ParseChar(p, (char *)buf)) == NULL ||
3193	  (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3194	return -1;
3195      SetEscape((struct acluser *)0, buf[0], buf[1]);
3196    }
3197  return 0;
3198}
3199
3200