TipAndDoc/console: screen.c

File screen.c, 69.1 KB (added by mitty, 15 years ago)

via 4.0.3-7ubuntu1/screen.c

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