Merge branch 'master' into lua-scripting
[screen-lua.git] / src / screen.c
blob7f10c806dc7333b494ac0d7b998e1ce2f4f1a8df
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
11 * Modifications Copyright (c) 1995 by
12 * Science Access Project, Oregon State University.
13 #endif
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.
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.
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
30 ****************************************************************
33 #include <sys/types.h>
34 #include <ctype.h>
36 #include <fcntl.h>
38 #ifdef sgi
39 # include <sys/sysmacros.h>
40 #endif
42 #include <sys/stat.h>
43 #ifndef sun
44 # include <sys/ioctl.h>
45 #endif
47 #ifndef SIGINT
48 # include <signal.h>
49 #endif
51 #include "config.h"
53 #ifdef HAVE_STROPTS_H
54 # include <sys/stropts.h>
55 #endif
57 #if defined(SYSV) && !defined(ISC)
58 # include <sys/utsname.h>
59 #endif
61 #if defined(sequent) || defined(SVR4)
62 # include <sys/resource.h>
63 #endif /* sequent || SVR4 */
65 #ifdef ISC
66 # include <sys/tty.h>
67 # include <sys/sioctl.h>
68 # include <sys/pty.h>
69 #endif /* ISC */
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
81 #include "screen.h"
82 #ifdef HAVE_BRAILLE
83 # include "braille.h"
84 #endif
86 #include "patchlevel.h"
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).
93 #ifndef LOCK
94 # undef SHADOWPW
95 #endif
97 #include <pwd.h>
98 #ifdef SHADOWPW
99 # include <shadow.h>
100 #endif /* SHADOWPW */
102 #include "logfile.h" /* islogfile, logfflush */
104 #ifdef DEBUG
105 FILE *dfp;
106 #endif
109 extern char Term[], screenterm[], **environ, Termcap[];
110 int force_vt = 1;
111 int VBellWait, MsgWait, MsgMinWait, SilenceWait;
113 extern struct acluser *users;
114 extern struct display *displays, *display;
117 extern int visual_bell;
118 #ifdef COPY_PASTE
119 extern unsigned char mark_key_tab[];
120 #endif
121 extern char version[];
122 extern char DefaultShell[];
123 #ifdef ZMODEM
124 extern char *zmodem_sendcmd;
125 extern char *zmodem_recvcmd;
126 #endif
127 extern struct layout *layout_last;
130 char *ShellProg;
131 char *ShellArgs[2];
133 extern struct NewWindow nwin_undef, nwin_default, nwin_options;
134 struct backtick;
136 static struct passwd *getpwbyname __P((char *, struct passwd *));
137 static void SigChldHandler __P((void));
138 static sigret_t SigChld __P(SIGPROTOARG);
139 static sigret_t SigInt __P(SIGPROTOARG);
140 static sigret_t CoreDump __P(SIGPROTOARG);
141 static sigret_t FinitHandler __P(SIGPROTOARG);
142 static void DoWait __P((void));
143 static void serv_read_fn __P((struct event *, char *));
144 static void serv_select_fn __P((struct event *, char *));
145 static void logflush_fn __P((struct event *, char *));
146 static void backtick_filter __P((struct backtick *));
147 static void backtick_fn __P((struct event *, char *));
148 static char *runbacktick __P((struct backtick *, int *, time_t));
149 static int IsSymbol __P((char *, char *));
150 static char *ParseChar __P((char *, char *));
151 static int ParseEscape __P((char *));
152 static char *pad_expand __P((char *, char *, int, int));
153 #ifdef DEBUG
154 static void fds __P((void));
155 #endif
157 int nversion; /* numerical version, used for secondary DA */
159 /* the attacher */
160 struct passwd *ppp;
161 char *attach_tty;
162 int attach_fd = -1;
163 char *attach_term;
164 char *LoginName;
165 struct mode attach_Mode;
167 char SockPath[MAXPATHLEN + 2 * MAXSTR];
168 char *SockName; /* SockName is pointer in SockPath */
169 char *SockMatch = NULL; /* session id command line argument */
170 int ServerSocket = -1;
171 struct event serv_read;
172 struct event serv_select;
173 struct event logflushev;
175 char **NewEnv = NULL;
177 char *RcFileName = NULL;
178 char *home;
180 char *screenlogfile; /* filename layout */
181 int log_flush = 10; /* flush interval in seconds */
182 int logtstamp_on = 0; /* tstamp disabled */
183 char *logtstamp_string; /* stamp layout */
184 int logtstamp_after = 120; /* first tstamp after 120s */
185 char *hardcopydir = NULL;
186 char *BellString;
187 char *VisualBellString;
188 char *ActivityString;
189 #ifdef COPY_PASTE
190 char *BufferFile;
191 #endif
192 #ifdef POW_DETACH
193 char *PowDetachString;
194 #endif
195 char *hstatusstring;
196 char *captionstring;
197 char *timestring;
198 char *wliststr;
199 char *wlisttit;
200 int auto_detach = 1;
201 int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
202 int cmdflag;
203 int adaptflag;
205 #ifdef MULTIUSER
206 char *multi;
207 char *multi_home;
208 int multi_uid;
209 int own_uid;
210 int multiattach;
211 int tty_mode;
212 int tty_oldmode = -1;
213 #endif
215 char HostName[MAXSTR];
216 int MasterPid, PanicPid;
217 int real_uid, real_gid, eff_uid, eff_gid;
218 int default_startup;
219 int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
220 char *preselect = NULL; /* only used in Attach() */
222 #ifdef UTF8
223 char *screenencodings;
224 #endif
226 #ifdef DW_CHARS
227 int cjkwidth;
228 #endif
230 #ifdef NETHACK
231 int nethackflag = 0;
232 #endif
233 int maxwin = MAXWIN;
236 struct layer *flayer;
237 struct win *fore;
238 struct win *windows;
239 struct win *console_window;
244 * Do this last
246 #include "extern.h"
248 char strnomem[] = "Out of memory.";
251 static int InterruptPlease;
252 static int GotSigChld;
254 static int
255 lf_secreopen(name, wantfd, l)
256 char *name;
257 int wantfd;
258 struct logfile *l;
260 int got_fd;
262 close(wantfd);
263 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
264 lf_move_fd(got_fd, wantfd) < 0)
266 logfclose(l);
267 debug1("lf_secreopen: failed for %s\n", name);
268 return -1;
270 l->st->st_ino = l->st->st_dev = 0;
271 debug2("lf_secreopen: %d = %s\n", wantfd, name);
272 return 0;
275 /********************************************************************/
276 /********************************************************************/
277 /********************************************************************/
280 static struct passwd *
281 getpwbyname(name, ppp)
282 char *name;
283 struct passwd *ppp;
285 int n;
286 #ifdef SHADOWPW
287 struct spwd *sss = NULL;
288 static char *spw = NULL;
289 #endif
291 if (!ppp && !(ppp = getpwnam(name)))
292 return NULL;
294 /* Do password sanity check..., allow ##user for SUN_C2 security */
295 #ifdef SHADOWPW
296 pw_try_again:
297 #endif
298 n = 0;
299 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
300 strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
301 n = 13;
302 for (; n < 13; n++)
304 char c = ppp->pw_passwd[n];
305 if (!(c == '.' || c == '/' || c == '$' ||
306 (c >= '0' && c <= '9') ||
307 (c >= 'a' && c <= 'z') ||
308 (c >= 'A' && c <= 'Z')))
309 break;
312 #ifdef SHADOWPW
313 /* try to determine real password */
314 if (n < 13 && sss == 0)
316 sss = getspnam(ppp->pw_name);
317 if (sss)
319 if (spw)
320 free(spw);
321 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
322 endspent(); /* this should delete all buffers ... */
323 goto pw_try_again;
325 endspent(); /* this should delete all buffers ... */
327 #endif
328 if (n < 13)
329 ppp->pw_passwd = 0;
330 #ifdef linux
331 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
332 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
333 #endif
335 return ppp;
338 static char *
339 locale_name(void)
341 static char *s;
343 if (!s)
345 s = getenv("LC_ALL");
346 if (s == NULL)
347 s = getenv("LC_CTYPE");
348 if (s == NULL)
349 s = getenv("LANG");
351 return s;
355 main(ac, av)
356 int ac;
357 char **av;
359 register int n;
360 char *ap;
361 char *av0;
362 char socknamebuf[2 * MAXSTR];
363 int mflag = 0;
364 char *myname = (ac == 0) ? "screen" : av[0];
365 char *SockDir;
366 struct stat st;
367 #ifdef _MODE_T /* (jw) */
368 mode_t oumask;
369 #else
370 int oumask;
371 #endif
372 #if defined(SYSV) && !defined(ISC)
373 struct utsname utsnam;
374 #endif
375 struct NewWindow nwin;
376 int detached = 0; /* start up detached */
377 #ifdef MULTIUSER
378 char *sockp;
379 #endif
380 char *script_file = 0;
381 char *sty = 0;
383 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
384 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
385 #endif
386 #if defined(sun) && defined(SVR4)
388 /* Solaris' login blocks SIGHUP! This is _very bad_ */
389 sigset_t sset;
390 sigemptyset(&sset);
391 sigprocmask(SIG_SETMASK, &sset, 0);
393 #endif
396 * First, close all unused descriptors
397 * (otherwise, we might have problems with the select() call)
399 closeallfiles(0);
400 #ifdef DEBUG
401 opendebug(1, 0);
402 #endif
403 sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
404 PATCHLEVEL, STATE, ORIGIN, DATE);
405 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
406 debug2("-- screen debug started %s (%s)\n", *av, version);
407 #ifdef POSIX
408 debug("POSIX\n");
409 #endif
410 #ifdef TERMIO
411 debug("TERMIO\n");
412 #endif
413 #ifdef SYSV
414 debug("SYSV\n");
415 #endif
416 #ifdef SYSVSIGS
417 debug("SYSVSIGS\n");
418 #endif
419 #ifdef NAMEDPIPE
420 debug("NAMEDPIPE\n");
421 #endif
422 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
423 debug("Window size changing enabled\n");
424 #endif
425 #ifdef HAVE_SETREUID
426 debug("SETREUID\n");
427 #endif
428 #ifdef HAVE_SETEUID
429 debug("SETEUID\n");
430 #endif
431 #ifdef hpux
432 debug("hpux\n");
433 #endif
434 #ifdef USEBCOPY
435 debug("USEBCOPY\n");
436 #endif
437 #ifdef UTMPOK
438 debug("UTMPOK\n");
439 #endif
440 #ifdef LOADAV
441 debug("LOADAV\n");
442 #endif
443 #ifdef NETHACK
444 debug("NETHACK\n");
445 #endif
446 #ifdef TERMINFO
447 debug("TERMINFO\n");
448 #endif
449 #ifdef SHADOWPW
450 debug("SHADOWPW\n");
451 #endif
452 #ifdef NAME_MAX
453 debug1("NAME_MAX = %d\n", NAME_MAX);
454 #endif
456 BellString = SaveStr("Bell in window %n");
457 VisualBellString = SaveStr(" Wuff, Wuff!! ");
458 ActivityString = SaveStr("Activity in window %n");
459 screenlogfile = SaveStr("screenlog.%n");
460 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
461 hstatusstring = SaveStr("%h");
462 captionstring = SaveStr("%3n %t");
463 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
464 wlisttit = SaveStr("Num Name%=Flags");
465 wliststr = SaveStr("%3n %t%=%f");
466 #ifdef COPY_PASTE
467 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
468 #endif
469 ShellProg = NULL;
470 #ifdef POW_DETACH
471 PowDetachString = 0;
472 #endif
473 default_startup = (ac > 1) ? 0 : 1;
474 adaptflag = 0;
475 VBellWait = VBELLWAIT * 1000;
476 MsgWait = MSGWAIT * 1000;
477 MsgMinWait = MSGMINWAIT * 1000;
478 SilenceWait = SILENCEWAIT;
479 #ifdef HAVE_BRAILLE
480 InitBraille();
481 #endif
482 #ifdef ZMODEM
483 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
484 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
485 #endif
487 #ifdef COPY_PASTE
488 CompileKeys((char *)0, 0, mark_key_tab);
489 #endif
490 #ifdef UTF8
491 InitBuiltinTabs();
492 screenencodings = SaveStr(SCREENENCODINGS);
493 #endif
494 #ifdef DW_CHARS
495 cjkwidth = 0;
496 #endif
497 nwin = nwin_undef;
498 nwin_options = nwin_undef;
499 strcpy(screenterm, "screen");
501 logreopen_register(lf_secreopen);
503 av0 = *av;
504 /* if this is a login screen, assume -RR */
505 if (*av0 == '-')
507 rflag = 4;
508 #ifdef MULTI
509 xflag = 1;
510 #else
511 dflag = 1;
512 #endif
513 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
515 while (ac > 0)
517 ap = *++av;
518 if (--ac > 0 && *ap == '-')
520 if (ap[1] == '-' && ap[2] == 0)
522 av++;
523 ac--;
524 break;
526 if (ap[1] == '-' && !strcmp(ap, "--version"))
527 Panic(0, "Screen version %s", version);
528 if (ap[1] == '-' && !strcmp(ap, "--help"))
529 exit_with_usage(myname, NULL, NULL);
530 while (ap && *ap && *++ap)
532 switch (*ap)
534 case 'a':
535 nwin_options.aflag = 1;
536 break;
537 case 'A':
538 adaptflag = 1;
539 break;
540 case 'p': /* preselect */
541 if (*++ap)
542 preselect = ap;
543 else
545 if (!--ac)
546 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
547 preselect = *++av;
549 ap = NULL;
550 break;
551 #ifdef HAVE_BRAILLE
552 case 'B':
553 bd.bd_start_braille = 1;
554 break;
555 #endif
556 case 'c':
557 if (*++ap)
558 RcFileName = ap;
559 else
561 if (--ac == 0)
562 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
563 RcFileName = *++av;
565 ap = NULL;
566 break;
567 case 'e':
568 if (!*++ap)
570 if (--ac == 0)
571 exit_with_usage(myname, "Specify command characters with -e", NULL);
572 ap = *++av;
574 if (ParseEscape(ap))
575 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
576 ap = NULL;
577 break;
578 case 'f':
579 ap++;
580 switch (*ap++)
582 case 'n':
583 case '0':
584 nwin_options.flowflag = FLOW_NOW * 0;
585 break;
586 case '\0':
587 ap--;
588 /* FALLTHROUGH */
589 case 'y':
590 case '1':
591 nwin_options.flowflag = FLOW_NOW * 1;
592 break;
593 case 'a':
594 nwin_options.flowflag = FLOW_AUTOFLAG;
595 break;
596 default:
597 exit_with_usage(myname, "Unknown flow option -%s", --ap);
599 break;
600 case 'h':
601 if (--ac == 0)
602 exit_with_usage(myname, NULL, NULL);
603 nwin_options.histheight = atoi(*++av);
604 if (nwin_options.histheight < 0)
605 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
606 break;
607 case 'i':
608 iflag = 1;
609 break;
610 case 't': /* title, the former AkA == -k */
611 if (--ac == 0)
612 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
613 nwin_options.aka = *++av;
614 break;
615 case 'l':
616 ap++;
617 switch (*ap++)
619 case 'n':
620 case '0':
621 nwin_options.lflag = 0;
622 break;
623 case '\0':
624 ap--;
625 /* FALLTHROUGH */
626 case 'y':
627 case '1':
628 nwin_options.lflag = 1;
629 break;
630 case 'a':
631 nwin_options.lflag = 3;
632 break;
633 case 's': /* -ls */
634 case 'i': /* -list */
635 lsflag = 1;
636 if (ac > 1 && !SockMatch)
638 SockMatch = *++av;
639 ac--;
641 ap = NULL;
642 break;
643 default:
644 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
646 break;
647 case 'w':
648 lsflag = 1;
649 wipeflag = 1;
650 if (ac > 1 && !SockMatch)
652 SockMatch = *++av;
653 ac--;
655 break;
656 case 'L':
657 nwin_options.Lflag = 1;
658 break;
659 case 'm':
660 mflag = 1;
661 break;
662 case 'O': /* to be (or not to be?) deleted. jw. */
663 force_vt = 0;
664 break;
665 case 'T':
666 if (--ac == 0)
667 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
668 if (strlen(*++av) < 20)
669 strcpy(screenterm, *av);
670 else
671 Panic(0, "-T: terminal name too long. (max. 20 char)");
672 nwin_options.term = screenterm;
673 break;
674 case 'q':
675 quietflag = 1;
676 break;
677 case 'r':
678 case 'R':
679 #ifdef MULTI
680 case 'x':
681 #endif
682 if (ac > 1 && *av[1] != '-' && !SockMatch)
684 SockMatch = *++av;
685 ac--;
686 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
688 #ifdef MULTI
689 if (*ap == 'x')
690 xflag = 1;
691 #endif
692 if (rflag)
693 rflag = 2;
694 rflag += (*ap == 'R') ? 2 : 1;
695 break;
696 #ifdef REMOTE_DETACH
697 case 'd':
698 dflag = 1;
699 /* FALLTHROUGH */
700 case 'D':
701 if (!dflag)
702 dflag = 2;
703 if (ac == 2)
705 if (*av[1] != '-' && !SockMatch)
707 SockMatch = *++av;
708 ac--;
709 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
712 break;
713 #endif
714 case 's':
715 if (--ac == 0)
716 exit_with_usage(myname, "Specify shell with -s", NULL);
717 if (ShellProg)
718 free(ShellProg);
719 ShellProg = SaveStr(*++av);
720 debug1("ShellProg: '%s'\n", ShellProg);
721 break;
722 case 'S':
723 if (!SockMatch)
725 if (--ac == 0)
726 exit_with_usage(myname, "Specify session-name with -S", NULL);
727 SockMatch = *++av;
729 if (!*SockMatch)
730 exit_with_usage(myname, "Empty session-name?", NULL);
731 break;
732 case 'X':
733 cmdflag = 1;
734 break;
735 case 'v':
736 Panic(0, "Screen version %s", version);
737 /* NOTREACHED */
738 #ifdef UTF8
739 case 'U':
740 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
741 break;
742 #endif
743 case 'u':
744 if (--ac == 0)
745 exit_with_usage(myname, "Specify lua script file with -u", NULL);
746 if (script_file)
747 free(script_file);
748 script_file = SaveStr(*++av);
749 break;
750 default:
751 exit_with_usage(myname, "Unknown option %s", --ap);
755 else
756 break;
759 real_uid = getuid();
760 real_gid = getgid();
761 eff_uid = geteuid();
762 eff_gid = getegid();
763 if (eff_uid != real_uid)
765 /* if running with s-bit, we must install a special signal
766 * handler routine that resets the s-bit, so that we get a
767 * core file anyway.
769 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
770 signal(SIGBUS, CoreDump);
771 #endif /* SIGBUS */
772 signal(SIGSEGV, CoreDump);
775 #ifdef USE_LOCALE
776 setlocale(LC_ALL, "");
777 #endif
778 #ifdef ENCODINGS
779 if (nwin_options.encoding == -1)
781 /* ask locale if we should start in UTF-8 mode */
782 # ifdef HAVE_NL_LANGINFO
783 # ifndef USE_LOCALE
784 setlocale(LC_CTYPE, "");
785 # endif
786 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
787 debug1("locale says encoding = %d\n", nwin_options.encoding);
788 # else
789 # ifdef UTF8
790 char *s;
791 if ((s = locale_name()) && InStr(s, "UTF-8"))
792 nwin_options.encoding = UTF8;
793 # endif
794 debug1("environment says encoding=%d\n", nwin_options.encoding);
795 #endif
797 # ifdef DW_CHARS
799 char *s;
800 if ((s = locale_name()))
802 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
804 cjkwidth = 1;
808 #endif
809 #endif
810 if (nwin_options.aka)
812 #ifdef ENCODINGS
813 if (nwin_options.encoding > 0)
815 size_t len = strlen(nwin_options.aka);
816 size_t newsz;
817 char *newbuf = malloc(3 * len);
818 if (!newbuf)
819 Panic(0, strnomem);
820 newsz = RecodeBuf(nwin_options.aka, len,
821 nwin_options.encoding, 0, newbuf);
822 newbuf[newsz] = '\0';
823 nwin_options.aka = newbuf;
825 else
826 #endif
828 /* If we just use the original value from av,
829 subsequent shelltitle invocations will attempt to free
830 space we don't own... */
831 nwin_options.aka = SaveStr(nwin_options.aka);
835 if (SockMatch && strlen(SockMatch) >= MAXSTR)
836 Panic(0, "Ridiculously long socketname - try again.");
837 if (cmdflag && !rflag && !dflag && !xflag)
838 xflag = 1;
839 if (!cmdflag && dflag && mflag && !(rflag || xflag))
840 detached = 1;
841 nwin = nwin_options;
842 #ifdef ENCODINGS
843 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
844 #endif
845 if (ac)
846 nwin.args = av;
848 /* make the write() calls return -1 on all errors */
849 #ifdef SIGXFSZ
851 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
852 * It appears that in System V Release 4, UNIX, if you are writing
853 * an output file and you exceed the currently set file size limit,
854 * you _don't_ just get the call to `write' returning with a
855 * failure code. Rather, you get a signal called `SIGXFSZ' which,
856 * if neither handled nor ignored, will cause your program to crash
857 * with a core dump.
859 signal(SIGXFSZ, SIG_IGN);
860 #endif /* SIGXFSZ */
862 #ifdef SIGPIPE
863 signal(SIGPIPE, SIG_IGN);
864 #endif
866 if (!ShellProg)
868 register char *sh;
870 sh = getenv("SHELL");
871 ShellProg = SaveStr(sh ? sh : DefaultShell);
873 ShellArgs[0] = ShellProg;
874 home = getenv("HOME");
875 if (!mflag && !SockMatch)
877 sty = getenv("STY");
878 if (sty && *sty == 0)
879 sty = 0;
882 #ifdef NETHACK
883 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
885 char nethackrc[MAXPATHLEN];
887 if (home && (strlen(home) < (MAXPATHLEN - 20)))
889 sprintf(nethackrc,"%s/.nethackrc", home);
890 nethackflag = !access(nethackrc, F_OK);
893 #endif
895 #ifdef MULTIUSER
896 own_uid = multi_uid = real_uid;
897 if (SockMatch && (sockp = index(SockMatch, '/')))
899 *sockp = 0;
900 multi = SockMatch;
901 SockMatch = sockp + 1;
902 if (*multi)
904 struct passwd *mppp;
905 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
906 Panic(0, "Cannot identify account '%s'.", multi);
907 multi_uid = mppp->pw_uid;
908 multi_home = SaveStr(mppp->pw_dir);
909 if (strlen(multi_home) > MAXPATHLEN - 10)
910 Panic(0, "home directory path too long");
911 # ifdef MULTI
912 /* always fake multi attach mode */
913 if (rflag || lsflag)
914 xflag = 1;
915 # endif /* MULTI */
916 detached = 0;
917 multiattach = 1;
919 /* Special case: effective user is multiuser. */
920 if (eff_uid && (multi_uid != eff_uid))
921 Panic(0, "Must run suid root for multiuser support.");
923 if (SockMatch && *SockMatch == 0)
924 SockMatch = 0;
925 #endif /* MULTIUSER */
927 if ((LoginName = getlogin()) && LoginName[0] != '\0')
929 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
930 if ((int)ppp->pw_uid != real_uid)
931 ppp = (struct passwd *) 0;
933 if (ppp == 0)
935 if ((ppp = getpwuid(real_uid)) == 0)
937 Panic(0, "getpwuid() can't identify your account!");
938 exit(1);
940 LoginName = ppp->pw_name;
942 LoginName = SaveStr(LoginName);
944 ppp = getpwbyname(LoginName, ppp);
946 #if !defined(SOCKDIR) && defined(MULTIUSER)
947 if (multi && !multiattach)
949 if (home && strcmp(home, ppp->pw_dir))
950 Panic(0, "$HOME must match passwd entry for multiuser screens.");
952 #endif
954 #define SET_GUID() do \
956 setgid(real_gid); \
957 setuid(real_uid); \
958 eff_uid = real_uid; \
959 eff_gid = real_gid; \
960 } while (0)
962 #define SET_TTYNAME(fatal) do \
964 if (!(attach_tty = ttyname(0))) \
966 if (fatal) \
967 Panic(0, "Must be connected to a terminal."); \
968 else \
969 attach_tty = ""; \
971 else if (stat(attach_tty, &st)) \
972 Panic(errno, "Cannot access '%s'", attach_tty); \
973 if (strlen(attach_tty) >= MAXPATHLEN) \
974 Panic(0, "TtyName too long - sorry."); \
975 } while (0)
977 if (home == 0 || *home == '\0')
978 home = ppp->pw_dir;
979 if (strlen(LoginName) > 20)
980 Panic(0, "LoginName too long - sorry.");
981 #ifdef MULTIUSER
982 if (multi && strlen(multi) > 20)
983 Panic(0, "Screen owner name too long - sorry.");
984 #endif
985 if (strlen(home) > MAXPATHLEN - 25)
986 Panic(0, "$HOME too long - sorry.");
988 attach_tty = "";
989 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty))
991 #ifndef NAMEDPIPE
992 int fl;
993 #endif
995 /* ttyname implies isatty */
996 SET_TTYNAME(1);
997 #ifdef MULTIUSER
998 tty_mode = (int)st.st_mode & 0777;
999 #endif
1001 #ifndef NAMEDPIPE
1002 fl = fcntl(0, F_GETFL, 0);
1003 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1004 attach_fd = 0;
1005 #endif
1006 if (attach_fd == -1)
1008 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1009 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1010 close(n);
1012 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1014 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1015 Panic(0, "Please set a terminal type.");
1016 if (strlen(attach_term) > sizeof(D_termname) - 1)
1017 Panic(0, "$TERM too long - sorry.");
1018 GetTTY(0, &attach_Mode);
1019 #ifdef DEBUGGGGGGGGGGGGGGG
1020 DebugTTY(&attach_Mode);
1021 #endif /* DEBUG */
1024 #ifdef _MODE_T
1025 oumask = umask(0); /* well, unsigned never fails? jw. */
1026 #else
1027 if ((oumask = (int)umask(0)) == -1)
1028 Panic(errno, "Cannot change umask to zero");
1029 #endif
1030 SockDir = getenv("SCREENDIR");
1031 if (SockDir)
1033 if (strlen(SockDir) >= MAXPATHLEN - 1)
1034 Panic(0, "Ridiculously long $SCREENDIR - try again.");
1035 #ifdef MULTIUSER
1036 if (multi)
1037 Panic(0, "No $SCREENDIR with multi screens, please.");
1038 #endif
1040 #ifdef MULTIUSER
1041 if (multiattach)
1043 # ifndef SOCKDIR
1044 sprintf(SockPath, "%s/.screen", multi_home);
1045 SockDir = SockPath;
1046 # else
1047 SockDir = SOCKDIR;
1048 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1049 # endif
1051 else
1052 #endif
1054 #ifndef SOCKDIR
1055 if (SockDir == 0)
1057 sprintf(SockPath, "%s/.screen", home);
1058 SockDir = SockPath;
1060 #endif
1061 if (SockDir)
1063 if (access(SockDir, F_OK))
1065 debug1("SockDir '%s' missing ...\n", SockDir);
1066 if (UserContext() > 0)
1068 if (mkdir(SockDir, 0700))
1069 UserReturn(0);
1070 UserReturn(1);
1072 if (UserStatus() <= 0)
1073 Panic(0, "Cannot make directory '%s'.", SockDir);
1075 if (SockDir != SockPath)
1076 strcpy(SockPath, SockDir);
1078 #ifdef SOCKDIR
1079 else
1081 SockDir = SOCKDIR;
1082 if (lstat(SockDir, &st))
1084 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1085 (eff_gid != real_gid) ? 0775 :
1086 #ifdef S_ISVTX
1087 0777|S_ISVTX;
1088 #else
1089 0777;
1090 #endif
1091 if (mkdir(SockDir, n) == -1)
1092 Panic(errno, "Cannot make directory '%s'", SockDir);
1094 else
1096 if (!S_ISDIR(st.st_mode))
1097 Panic(0, "'%s' must be a directory.", SockDir);
1098 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1099 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1100 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1101 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1102 0777;
1103 if (((int)st.st_mode & 0777) != n)
1104 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1106 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1107 if (access(SockPath, F_OK))
1109 if (mkdir(SockPath, 0700) == -1)
1110 Panic(errno, "Cannot make directory '%s'", SockPath);
1111 (void) chown(SockPath, real_uid, real_gid);
1114 #endif
1117 if (stat(SockPath, &st) == -1)
1118 Panic(errno, "Cannot access %s", SockPath);
1119 else
1120 if (!S_ISDIR(st.st_mode))
1121 Panic(0, "%s is not a directory.", SockPath);
1122 #ifdef MULTIUSER
1123 if (multi)
1125 if ((int)st.st_uid != multi_uid)
1126 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1128 else
1129 #endif
1131 if ((int)st.st_uid != real_uid)
1132 Panic(0, "You are not the owner of %s.", SockPath);
1134 if ((st.st_mode & 0777) != 0700)
1135 Panic(0, "Directory %s must have mode 700.", SockPath);
1136 if (SockMatch && index(SockMatch, '/'))
1137 Panic(0, "Bad session name '%s'", SockMatch);
1138 SockName = SockPath + strlen(SockPath) + 1;
1139 *SockName = 0;
1140 (void) umask(oumask);
1141 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1143 #if defined(SYSV) && !defined(ISC)
1144 if (uname(&utsnam) == -1)
1145 Panic(errno, "uname");
1146 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1147 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1148 #else
1149 (void) gethostname(HostName, MAXSTR);
1150 HostName[MAXSTR - 1] = '\0';
1151 #endif
1152 if ((ap = index(HostName, '.')) != NULL)
1153 *ap = '\0';
1155 if (lsflag)
1157 int i, fo, oth;
1159 #ifdef MULTIUSER
1160 if (multi)
1161 real_uid = multi_uid;
1162 #endif
1163 SET_GUID();
1164 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1165 if (quietflag)
1166 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1167 if (fo == 0)
1168 Panic(0, "No Sockets found in %s.\n", SockPath);
1169 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1170 /* NOTREACHED */
1172 signal(SIG_BYE, AttacherFinit); /* prevent races */
1173 if (cmdflag)
1175 /* attach_tty is not mandatory */
1176 SET_TTYNAME(0);
1177 if (!*av)
1178 Panic(0, "Please specify a command.");
1179 SET_GUID();
1180 SendCmdMessage(sty, SockMatch, av);
1181 exit(0);
1183 else if (rflag || xflag)
1185 debug("screen -r: - is there anybody out there?\n");
1186 if (Attach(MSG_ATTACH))
1188 Attacher();
1189 /* NOTREACHED */
1191 #ifdef MULTIUSER
1192 if (multiattach)
1193 Panic(0, "Can't create sessions of other users.");
1194 #endif
1195 debug("screen -r: backend not responding -- still crying\n");
1197 else if (dflag && !mflag)
1199 SET_TTYNAME(0);
1200 Attach(MSG_DETACH);
1201 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1202 eexit(0);
1203 /* NOTREACHED */
1205 if (!SockMatch && !mflag && sty)
1207 /* attach_tty is not mandatory */
1208 SET_TTYNAME(0);
1209 SET_GUID();
1210 nwin_options.args = av;
1211 SendCreateMsg(sty, &nwin);
1212 exit(0);
1213 /* NOTREACHED */
1215 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1217 if (!detached || dflag != 2)
1218 MasterPid = fork();
1219 else
1220 MasterPid = 0;
1222 switch (MasterPid)
1224 case -1:
1225 Panic(errno, "fork");
1226 /* NOTREACHED */
1227 case 0:
1228 break;
1229 default:
1230 if (detached)
1231 exit(0);
1232 if (SockMatch)
1233 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1234 else
1235 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1236 for (ap = socknamebuf; *ap; ap++)
1237 if (*ap == '/')
1238 *ap = '-';
1239 #ifdef NAME_MAX
1240 if (strlen(socknamebuf) > NAME_MAX)
1241 socknamebuf[NAME_MAX] = 0;
1242 #endif
1243 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1244 SET_GUID();
1245 Attacher();
1246 /* NOTREACHED */
1249 if (!detached)
1250 PanicPid = getppid();
1252 if (DefaultEsc == -1)
1253 DefaultEsc = Ctrl('a');
1254 if (DefaultMetaEsc == -1)
1255 DefaultMetaEsc = 'a';
1257 ap = av0 + strlen(av0) - 1;
1258 while (ap >= av0)
1260 if (!strncmp("screen", ap, 6))
1262 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1263 break;
1265 ap--;
1267 if (ap < av0)
1268 *av0 = 'S';
1270 #ifdef DEBUG
1272 char buf[256];
1274 if (dfp && dfp != stderr)
1275 fclose(dfp);
1276 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1277 if ((dfp = fopen(buf, "w")) == NULL)
1278 dfp = stderr;
1279 else
1280 (void) chmod(buf, 0666);
1282 #endif
1283 if (!detached)
1285 if (attach_fd == -1)
1287 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1288 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1290 else
1291 n = dup(attach_fd);
1293 else
1294 n = -1;
1295 freopen("/dev/null", "r", stdin);
1296 freopen("/dev/null", "w", stdout);
1298 #ifdef DEBUG
1299 if (dfp != stderr)
1300 #endif
1301 freopen("/dev/null", "w", stderr);
1302 debug("-- screen.back debug started\n");
1305 * This guarantees that the session owner is listed, even when we
1306 * start detached. From now on we should not refer to 'LoginName'
1307 * any more, use users->u_name instead.
1309 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1310 Panic(0, "Could not create user info");
1311 if (!detached)
1313 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1314 Panic(0, "Could not alloc display");
1315 PanicPid = 0;
1316 #ifdef ENCODINGS
1317 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1318 debug1("D_encoding = %d\n", D_encoding);
1319 #endif
1322 if (SockMatch)
1324 /* user started us with -S option */
1325 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1327 else
1329 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1330 HostName);
1332 for (ap = socknamebuf; *ap; ap++)
1333 if (*ap == '/')
1334 *ap = '-';
1335 #ifdef NAME_MAX
1336 if (strlen(socknamebuf) > NAME_MAX)
1338 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1339 socknamebuf[NAME_MAX] = 0;
1341 #endif
1342 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1344 ServerSocket = MakeServerSocket();
1345 InitKeytab();
1347 LoadScripts();
1348 ScriptInit();
1349 if (script_file)
1351 ScriptSource(script_file);
1352 free(script_file);
1353 script_file = 0;
1356 #ifdef ETCSCREENRC
1357 # ifdef ALLOW_SYSSCREENRC
1358 if ((ap = getenv("SYSSCREENRC")))
1359 (void)StartRc(ap, 0);
1360 else
1361 # endif
1362 (void)StartRc(ETCSCREENRC, 0);
1363 #endif
1364 (void)StartRc(RcFileName, 0);
1365 # ifdef UTMPOK
1366 # ifndef UTNOKEEP
1367 InitUtmp();
1368 # endif /* UTNOKEEP */
1369 # endif /* UTMPOK */
1370 if (display)
1372 if (InitTermcap(0, 0))
1374 debug("Could not init termcap - exiting\n");
1375 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1376 freetty();
1377 if (D_userpid)
1378 Kill(D_userpid, SIG_BYE);
1379 eexit(1);
1381 MakeDefaultCanvas();
1382 InitTerm(0);
1383 #ifdef UTMPOK
1384 RemoveLoginSlot();
1385 #endif
1387 else
1388 MakeTermcap(1);
1389 #ifdef LOADAV
1390 InitLoadav();
1391 #endif /* LOADAV */
1392 MakeNewEnv();
1393 signal(SIGHUP, SigHup);
1394 signal(SIGINT, FinitHandler);
1395 signal(SIGQUIT, FinitHandler);
1396 signal(SIGTERM, FinitHandler);
1397 #ifdef BSDJOBS
1398 signal(SIGTTIN, SIG_IGN);
1399 signal(SIGTTOU, SIG_IGN);
1400 #endif
1402 if (display)
1404 brktty(D_userfd);
1405 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1406 /* Note: SetMode must be called _before_ FinishRc. */
1407 SetTTY(D_userfd, &D_NewMode);
1408 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1409 Msg(errno, "Warning: NBLOCK fcntl failed");
1411 else
1412 brktty(-1); /* just try */
1413 signal(SIGCHLD, SigChld);
1414 #ifdef ETCSCREENRC
1415 # ifdef ALLOW_SYSSCREENRC
1416 if ((ap = getenv("SYSSCREENRC")))
1417 FinishRc(ap);
1418 else
1419 # endif
1420 FinishRc(ETCSCREENRC);
1421 #endif
1422 FinishRc(RcFileName);
1424 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1425 if (windows == NULL)
1427 debug("We open one default window, as screenrc did not specify one.\n");
1428 if (MakeWindow(&nwin) == -1)
1430 Msg(0, "Sorry, could not find a PTY.");
1431 sleep(5);
1432 Finit(0);
1433 /* NOTREACHED */
1437 #ifdef HAVE_BRAILLE
1438 StartBraille();
1439 #endif
1441 if (display && default_startup)
1442 display_copyright();
1443 signal(SIGINT, SigInt);
1444 if (rflag && (rflag & 1) == 0 && !quietflag)
1446 Msg(0, "New screen...");
1447 rflag = 0;
1450 serv_read.type = EV_READ;
1451 serv_read.fd = ServerSocket;
1452 serv_read.handler = serv_read_fn;
1453 evenq(&serv_read);
1455 serv_select.pri = -10;
1456 serv_select.type = EV_ALWAYS;
1457 serv_select.handler = serv_select_fn;
1458 evenq(&serv_select);
1460 logflushev.type = EV_TIMEOUT;
1461 logflushev.handler = logflush_fn;
1463 sched();
1464 /* NOTREACHED */
1465 return 0;
1468 void
1469 WindowDied(p, wstat, wstat_valid)
1470 struct win *p;
1471 int wstat;
1472 int wstat_valid;
1474 int killit = 0;
1476 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1477 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1478 killit = 1;
1480 if (ZombieKey_destroy && !killit)
1482 char buf[100], *s, reason[100];
1483 time_t now;
1485 if (wstat_valid) {
1486 if (WIFEXITED(wstat))
1487 if (WEXITSTATUS(wstat))
1488 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1489 else
1490 sprintf(reason, "terminated normally");
1491 else if (WIFSIGNALED(wstat))
1492 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1493 #ifdef WCOREDUMP
1494 WCOREDUMP(wstat) ? " (core file generated)" : "");
1495 #else
1496 "");
1497 #endif
1498 } else
1499 sprintf(reason, "detached from window");
1501 (void) time(&now);
1502 s = ctime(&now);
1503 if (s && *s)
1504 s[strlen(s) - 1] = '\0';
1505 debug3("window %d (%s) going into zombie state fd %d",
1506 p->w_number, p->w_title, p->w_ptyfd);
1507 #ifdef UTMPOK
1508 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1510 RemoveUtmp(p);
1511 p->w_slot = 0; /* "detached" */
1513 #endif
1514 CloseDevice(p);
1516 p->w_deadpid = p->w_pid;
1517 p->w_pid = 0;
1518 ResetWindow(p);
1519 /* p->w_y = p->w_bot; */
1520 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1521 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1522 WriteString(p, buf, strlen(buf));
1523 WindowChanged(p, 'f');
1525 else
1526 KillWindow(p);
1527 #ifdef UTMPOK
1528 CarefulUtmp();
1529 #endif
1532 static void
1533 SigChldHandler()
1535 struct stat st;
1536 #ifdef DEBUG
1537 fds();
1538 #endif
1539 while (GotSigChld)
1541 GotSigChld = 0;
1542 DoWait();
1543 #ifdef SYSVSIGS
1544 signal(SIGCHLD, SigChld);
1545 #endif
1547 if (stat(SockPath, &st) == -1)
1549 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1550 if (!RecoverSocket())
1552 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1553 Finit(1);
1555 else
1556 debug1("'%s' reconstructed\n", SockPath);
1558 else
1559 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1562 static sigret_t
1563 SigChld SIGDEFARG
1565 debug("SigChld()\n");
1566 GotSigChld = 1;
1567 SIGRETURN;
1570 sigret_t
1571 SigHup SIGDEFARG
1573 /* Hangup all displays */
1574 while ((display = displays) != 0)
1575 Hangup();
1576 SIGRETURN;
1580 * the backend's Interrupt handler
1581 * we cannot insert the intrc directly, as we never know
1582 * if fore is valid.
1584 static sigret_t
1585 SigInt SIGDEFARG
1587 #if HAZARDOUS
1588 char ibuf;
1590 debug("SigInt()\n");
1591 if (fore && displays)
1593 # if defined(TERMIO) || defined(POSIX)
1594 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1595 # else
1596 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1597 # endif
1598 fore->w_inlen = 0;
1599 write(fore->w_ptyfd, &ibuf, 1);
1601 #else
1602 signal(SIGINT, SigInt);
1603 debug("SigInt() careful\n");
1604 InterruptPlease = 1;
1605 #endif
1606 SIGRETURN;
1609 static sigret_t
1610 CoreDump SIGDEFARG
1612 struct display *disp;
1613 char buf[80];
1615 #if defined(SYSVSIGS) && defined(SIGHASARG)
1616 signal(sigsig, SIG_IGN);
1617 #endif
1618 setgid(getgid());
1619 setuid(getuid());
1620 unlink("core");
1621 #ifdef SIGHASARG
1622 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1623 #else
1624 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1625 #endif
1626 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1628 #else /* SHADOWPW && !DEBUG */
1629 " (core dumped)"
1630 #endif /* SHADOWPW && !DEBUG */
1632 for (disp = displays; disp; disp = disp->d_next)
1634 fcntl(disp->d_userfd, F_SETFL, 0);
1635 SetTTY(disp->d_userfd, &D_OldMode);
1636 write(disp->d_userfd, buf, strlen(buf));
1637 Kill(disp->d_userpid, SIG_BYE);
1639 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1640 Kill(getpid(), SIGKILL);
1641 eexit(11);
1642 #else /* SHADOWPW && !DEBUG */
1643 abort();
1644 #endif /* SHADOWPW && !DEBUG */
1645 SIGRETURN;
1648 static void
1649 DoWait()
1651 register int pid;
1652 struct win *p, *next;
1653 #ifdef BSDWAIT
1654 union wait wstat;
1655 #else
1656 int wstat;
1657 #endif
1659 #ifdef BSDJOBS
1660 # ifndef BSDWAIT
1661 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1662 # else
1663 # ifdef USE_WAIT2
1665 * From: rouilj@sni-usa.com (John Rouillard)
1666 * note that WUNTRACED is not documented to work, but it is defined in
1667 * /usr/include/sys/wait.h, so it may work
1669 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1670 # else /* USE_WAIT2 */
1671 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1672 # endif /* USE_WAIT2 */
1673 # endif
1674 #else /* BSDJOBS */
1675 while ((pid = wait(&wstat)) < 0)
1676 if (errno != EINTR)
1677 break;
1678 if (pid > 0)
1679 #endif /* BSDJOBS */
1681 for (p = windows; p; p = next)
1683 next = p->w_next;
1684 if ( (p->w_pid && pid == p->w_pid) ||
1685 (p->w_deadpid && pid == p->w_deadpid) )
1687 /* child has ceased to exist */
1688 p->w_pid = 0;
1690 #ifdef BSDJOBS
1691 if (WIFSTOPPED(wstat))
1693 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1694 #ifdef SIGTTIN
1695 if (WSTOPSIG(wstat) == SIGTTIN)
1697 Msg(0, "Suspended (tty input)");
1698 continue;
1700 #endif
1701 #ifdef SIGTTOU
1702 if (WSTOPSIG(wstat) == SIGTTOU)
1704 Msg(0, "Suspended (tty output)");
1705 continue;
1707 #endif
1708 /* Try to restart process */
1709 Msg(0, "Child has been stopped, restarting.");
1710 if (killpg(pid, SIGCONT))
1711 kill(pid, SIGCONT);
1713 else
1714 #endif
1716 WindowDied(p, wstat, 1);
1718 break;
1720 #ifdef PSEUDOS
1721 if (p->w_pwin && pid == p->w_pwin->p_pid)
1723 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1724 FreePseudowin(p);
1725 break;
1727 #endif
1729 if (p == 0)
1731 debug1("pid %d not found - hope that's ok\n", pid);
1737 static sigret_t
1738 FinitHandler SIGDEFARG
1740 #ifdef SIGHASARG
1741 debug1("FinitHandler called, sig %d.\n", sigsig);
1742 #else
1743 debug("FinitHandler called.\n");
1744 #endif
1745 Finit(1);
1746 SIGRETURN;
1749 void
1750 Finit(i)
1751 int i;
1753 signal(SIGCHLD, SIG_DFL);
1754 signal(SIGHUP, SIG_IGN);
1755 debug1("Finit(%d);\n", i);
1757 ScriptFinit();
1759 while (windows)
1761 struct win *p = windows;
1762 windows = windows->w_next;
1763 FreeWindow(p);
1765 if (ServerSocket != -1)
1767 debug1("we unlink(%s)\n", SockPath);
1768 #ifdef USE_SETEUID
1769 xseteuid(real_uid);
1770 xsetegid(real_gid);
1771 #endif
1772 (void) unlink(SockPath);
1773 #ifdef USE_SETEUID
1774 xseteuid(eff_uid);
1775 xsetegid(eff_gid);
1776 #endif
1778 for (display = displays; display; display = display->d_next)
1780 if (D_status)
1781 RemoveStatus();
1782 FinitTerm();
1783 #ifdef UTMPOK
1784 RestoreLoginSlot();
1785 #endif
1786 AddStr("[screen is terminating]\r\n");
1787 Flush();
1788 SetTTY(D_userfd, &D_OldMode);
1789 fcntl(D_userfd, F_SETFL, 0);
1790 freetty();
1791 Kill(D_userpid, SIG_BYE);
1794 * we _cannot_ call eexit(i) here,
1795 * instead of playing with the Socket above. Sigh.
1797 exit(i);
1800 void
1801 eexit(e)
1802 int e;
1804 debug("eexit\n");
1805 if (ServerSocket != -1)
1807 debug1("we unlink(%s)\n", SockPath);
1808 setgid(real_gid);
1809 setuid(real_uid);
1810 (void) unlink(SockPath);
1812 exit(e);
1815 void
1816 Hangup()
1818 if (display == 0)
1819 return;
1820 debug1("Hangup %x\n", display);
1821 if (D_userfd >= 0)
1823 close(D_userfd);
1824 D_userfd = -1;
1826 if (auto_detach || displays->d_next)
1827 Detach(D_HANGUP);
1828 else
1829 Finit(0);
1833 * Detach now has the following modes:
1834 *D_DETACH SIG_BYE detach backend and exit attacher
1835 *D_HANGUP SIG_BYE detach backend and exit attacher
1836 *D_STOP SIG_STOP stop attacher (and detach backend)
1837 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1838 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1839 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1840 *D_LOCK SIG_LOCK lock the attacher
1841 * (jw)
1842 * we always remove our utmp slots. (even when "lock" or "stop")
1843 * Note: Take extra care here, we may be called by interrupt!
1845 void
1846 Detach(mode)
1847 int mode;
1849 int sign = 0, pid;
1850 struct canvas *cv;
1851 struct win *p;
1853 if (display == 0)
1854 return;
1856 signal(SIGHUP, SIG_IGN);
1857 debug1("Detach(%d)\n", mode);
1858 if (D_status)
1859 RemoveStatus();
1860 FinitTerm();
1861 if (!display)
1862 return;
1863 switch (mode)
1865 case D_HANGUP:
1866 sign = SIG_BYE;
1867 break;
1868 case D_DETACH:
1869 AddStr("[detached]\r\n");
1870 sign = SIG_BYE;
1871 break;
1872 #ifdef BSDJOBS
1873 case D_STOP:
1874 sign = SIG_STOP;
1875 break;
1876 #endif
1877 #ifdef REMOTE_DETACH
1878 case D_REMOTE:
1879 AddStr("[remote detached]\r\n");
1880 sign = SIG_BYE;
1881 break;
1882 #endif
1883 #ifdef POW_DETACH
1884 case D_POWER:
1885 AddStr("[power detached]\r\n");
1886 if (PowDetachString)
1888 AddStr(PowDetachString);
1889 AddStr("\r\n");
1891 sign = SIG_POWER_BYE;
1892 break;
1893 #ifdef REMOTE_DETACH
1894 case D_REMOTE_POWER:
1895 AddStr("[remote power detached]\r\n");
1896 if (PowDetachString)
1898 AddStr(PowDetachString);
1899 AddStr("\r\n");
1901 sign = SIG_POWER_BYE;
1902 break;
1903 #endif
1904 #endif
1905 case D_LOCK:
1906 ClearAll();
1907 sign = SIG_LOCK;
1908 /* tell attacher to lock terminal with a lockprg. */
1909 break;
1911 #ifdef UTMPOK
1912 if (displays->d_next == 0)
1914 for (p = windows; p; p = p->w_next)
1916 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1918 RemoveUtmp(p);
1920 * Set the slot to 0 to get the window
1921 * logged in again.
1923 p->w_slot = (slot_t) 0;
1927 if (mode != D_HANGUP)
1928 RestoreLoginSlot();
1929 #endif
1930 if (displays->d_next == 0 && console_window)
1932 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1934 debug("could not release console - killing window\n");
1935 KillWindow(console_window);
1936 display = displays; /* restore display */
1939 if (D_fore)
1941 #ifdef MULTIUSER
1942 ReleaseAutoWritelock(display, D_fore);
1943 #endif
1944 D_user->u_detachwin = D_fore->w_number;
1945 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1947 AutosaveLayout(D_layout);
1948 layout_last = D_layout;
1949 for (cv = D_cvlist; cv; cv = cv->c_next)
1951 p = Layer2Window(cv->c_layer);
1952 SetCanvasWindow(cv, 0);
1953 if (p)
1954 WindowChanged(p, 'u');
1957 pid = D_userpid;
1958 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1959 FreeDisplay();
1960 if (displays == 0)
1961 /* Flag detached-ness */
1962 (void) chsock();
1964 * tell father what to do. We do that after we
1965 * freed the tty, thus getty feels more comfortable on hpux
1966 * if it was a power detach.
1968 Kill(pid, sign);
1969 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1970 debug("Detach returns, we are successfully detached.\n");
1971 signal(SIGHUP, SigHup);
1974 static int
1975 IsSymbol(e, s)
1976 char *e, *s;
1978 register int l;
1980 l = strlen(s);
1981 return strncmp(e, s, l) == 0 && e[l] == '=';
1984 void
1985 MakeNewEnv()
1987 register char **op, **np;
1988 static char stybuf[MAXSTR];
1990 for (op = environ; *op; ++op)
1992 if (NewEnv)
1993 free((char *)NewEnv);
1994 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
1995 if (!NewEnv)
1996 Panic(0, strnomem);
1997 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
1998 *np++ = stybuf; /* NewEnv[0] */
1999 *np++ = Term; /* NewEnv[1] */
2000 np++; /* room for SHELL */
2001 #ifdef TIOCSWINSZ
2002 np += 2; /* room for TERMCAP and WINDOW */
2003 #else
2004 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2005 #endif
2007 for (op = environ; *op; ++op)
2009 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2010 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2011 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2012 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
2014 *np++ = *op;
2016 *np = 0;
2019 void
2020 /*VARARGS2*/
2021 #if defined(USEVARARGS) && defined(__STDC__)
2022 Msg(int err, char *fmt, VA_DOTS)
2023 #else
2024 Msg(err, fmt, VA_DOTS)
2025 int err;
2026 char *fmt;
2027 VA_DECL
2028 #endif
2030 VA_LIST(ap)
2031 char buf[MAXPATHLEN*2];
2032 char *p = buf;
2034 VA_START(ap, fmt);
2035 fmt = DoNLS(fmt);
2036 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2037 VA_END(ap);
2038 if (err)
2040 p += strlen(p);
2041 *p++ = ':';
2042 *p++ = ' ';
2043 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2044 buf[sizeof(buf) - 1] = 0;
2046 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2048 if (display && displays)
2049 MakeStatus(buf);
2050 else if (displays)
2052 for (display = displays; display; display = display->d_next)
2053 MakeStatus(buf);
2055 else if (display)
2057 /* no displays but a display - must have forked.
2058 * send message to backend!
2060 char *tty = D_usertty;
2061 struct display *olddisplay = display;
2062 display = 0; /* only send once */
2063 SendErrorMsg(tty, buf);
2064 display = olddisplay;
2066 else
2067 printf("%s\r\n", buf);
2071 * Call FinitTerm for all displays, write a message to each and call eexit();
2073 void
2074 /*VARARGS2*/
2075 #if defined(USEVARARGS) && defined(__STDC__)
2076 Panic(int err, char *fmt, VA_DOTS)
2077 #else
2078 Panic(err, fmt, VA_DOTS)
2079 int err;
2080 char *fmt;
2081 VA_DECL
2082 #endif
2084 VA_LIST(ap)
2085 char buf[MAXPATHLEN*2];
2086 char *p = buf;
2088 VA_START(ap, fmt);
2089 fmt = DoNLS(fmt);
2090 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2091 VA_END(ap);
2092 if (err)
2094 p += strlen(p);
2095 *p++ = ':';
2096 *p++ = ' ';
2097 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2098 buf[sizeof(buf) - 1] = 0;
2100 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2101 if (displays == 0 && display == 0)
2103 printf("%s\r\n", buf);
2104 if (PanicPid)
2105 Kill(PanicPid, SIG_BYE);
2107 else if (displays == 0)
2109 /* no displays but a display - must have forked.
2110 * send message to backend!
2112 char *tty = D_usertty;
2113 display = 0;
2114 SendErrorMsg(tty, buf);
2115 sleep(2);
2116 _exit(1);
2118 else
2119 for (display = displays; display; display = display->d_next)
2121 if (D_status)
2122 RemoveStatus();
2123 FinitTerm();
2124 Flush();
2125 #ifdef UTMPOK
2126 RestoreLoginSlot();
2127 #endif
2128 SetTTY(D_userfd, &D_OldMode);
2129 fcntl(D_userfd, F_SETFL, 0);
2130 write(D_userfd, buf, strlen(buf));
2131 write(D_userfd, "\n", 1);
2132 freetty();
2133 if (D_userpid)
2134 Kill(D_userpid, SIG_BYE);
2136 #ifdef MULTIUSER
2137 if (tty_oldmode >= 0)
2139 # ifdef USE_SETEUID
2140 if (setuid(own_uid))
2141 xseteuid(own_uid); /* may be a loop. sigh. */
2142 # else
2143 setuid(own_uid);
2144 # endif
2145 debug1("Panic: changing back modes from %s\n", attach_tty);
2146 chmod(attach_tty, tty_oldmode);
2148 #endif
2149 eexit(1);
2154 * '^' is allowed as an escape mechanism for control characters. jw.
2156 * Added time insertion using ideas/code from /\ndy Jones
2157 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2161 #ifndef USE_LOCALE
2162 static const char days[] = "SunMonTueWedThuFriSat";
2163 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2164 #endif
2166 static char winmsg_buf[MAXSTR];
2167 #define MAX_WINMSG_REND 16 /* rendition changes */
2168 static int winmsg_rend[MAX_WINMSG_REND];
2169 static int winmsg_rendpos[MAX_WINMSG_REND];
2170 static int winmsg_numrend;
2172 static char *
2173 pad_expand(buf, p, numpad, padlen)
2174 char *buf;
2175 char *p;
2176 int numpad;
2177 int padlen;
2179 char *pn, *pn2;
2180 int i, r;
2182 padlen = padlen - (p - buf); /* space for rent */
2183 if (padlen < 0)
2184 padlen = 0;
2185 pn2 = pn = p + padlen;
2186 r = winmsg_numrend;
2187 while (p >= buf)
2189 if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
2191 winmsg_rendpos[--r] = pn - buf;
2192 continue;
2194 *pn-- = *p;
2195 if (*p-- == 127)
2197 pn[1] = ' ';
2198 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2199 padlen -= i;
2200 while (i-- > 0)
2201 *pn-- = ' ';
2202 numpad--;
2203 if (r && p - buf == winmsg_rendpos[r - 1])
2204 winmsg_rendpos[--r] = pn - buf;
2207 return pn2;
2210 struct backtick {
2211 struct backtick *next;
2212 int num;
2213 int tick;
2214 int lifespan;
2215 time_t bestbefore;
2216 char result[MAXSTR];
2217 char **cmdv;
2218 struct event ev;
2219 char *buf;
2220 int bufi;
2223 struct backtick *backticks;
2225 static void
2226 backtick_filter(bt)
2227 struct backtick *bt;
2229 char *p, *q;
2230 int c;
2232 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2234 if (c == '\t')
2235 c = ' ';
2236 if (c >= ' ' || c == '\005')
2237 *q++ = c;
2239 *q = 0;
2242 static void
2243 backtick_fn(ev, data)
2244 struct event *ev;
2245 char *data;
2247 struct backtick *bt;
2248 int i, j, k, l;
2250 bt = (struct backtick *)data;
2251 debug1("backtick_fn for #%d\n", bt->num);
2252 i = bt->bufi;
2253 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2254 if (l <= 0)
2256 debug1("EOF on backtick #%d\n", bt->num);
2257 evdeq(ev);
2258 close(ev->fd);
2259 ev->fd = -1;
2260 return;
2262 debug1("read %d bytes\n", l);
2263 i += l;
2264 for (j = 0; j < l; j++)
2265 if (bt->buf[i - j - 1] == '\n')
2266 break;
2267 if (j < l)
2269 for (k = i - j - 2; k >= 0; k--)
2270 if (bt->buf[k] == '\n')
2271 break;
2272 k++;
2273 bcopy(bt->buf + k, bt->result, i - j - k);
2274 bt->result[i - j - k - 1] = 0;
2275 backtick_filter(bt);
2276 WindowChanged(0, '`');
2278 if (j == l && i == MAXSTR)
2280 j = MAXSTR/2;
2281 l = j + 1;
2283 if (j < l)
2285 if (j)
2286 bcopy(bt->buf + i - j, bt->buf, j);
2287 i = j;
2289 bt->bufi = i;
2292 void
2293 setbacktick(num, lifespan, tick, cmdv)
2294 int num;
2295 int lifespan;
2296 int tick;
2297 char **cmdv;
2299 struct backtick **btp, *bt;
2300 char **v;
2302 debug1("setbacktick called for backtick #%d\n", num);
2303 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2304 if (bt->num == num)
2305 break;
2306 if (!bt && !cmdv)
2307 return;
2308 if (bt)
2310 for (v = bt->cmdv; *v; v++)
2311 free(*v);
2312 free(bt->cmdv);
2313 if (bt->buf)
2314 free(bt->buf);
2315 if (bt->ev.fd >= 0)
2316 close(bt->ev.fd);
2317 evdeq(&bt->ev);
2319 if (bt && !cmdv)
2321 *btp = bt->next;
2322 free(bt);
2323 return;
2325 if (!bt)
2327 bt = (struct backtick *)malloc(sizeof *bt);
2328 if (!bt)
2330 Msg(0, strnomem);
2331 return;
2333 bzero(bt, sizeof(*bt));
2334 bt->next = 0;
2335 *btp = bt;
2337 bt->num = num;
2338 bt->tick = tick;
2339 bt->lifespan = lifespan;
2340 bt->bestbefore = 0;
2341 bt->result[0] = 0;
2342 bt->buf = 0;
2343 bt->bufi = 0;
2344 bt->cmdv = cmdv;
2345 bt->ev.fd = -1;
2346 if (bt->tick == 0 && bt->lifespan == 0)
2348 debug("setbacktick: continuous mode\n");
2349 bt->buf = (char *)malloc(MAXSTR);
2350 if (bt->buf == 0)
2352 Msg(0, strnomem);
2353 setbacktick(num, 0, 0, (char **)0);
2354 return;
2356 bt->ev.type = EV_READ;
2357 bt->ev.fd = readpipe(bt->cmdv);
2358 bt->ev.handler = backtick_fn;
2359 bt->ev.data = (char *)bt;
2360 if (bt->ev.fd >= 0)
2361 evenq(&bt->ev);
2365 static char *
2366 runbacktick(bt, tickp, now)
2367 struct backtick *bt;
2368 int *tickp;
2369 time_t now;
2371 int f, i, l, j;
2372 time_t now2;
2374 debug1("runbacktick called for backtick #%d\n", bt->num);
2375 if (bt->tick && (!*tickp || bt->tick < *tickp))
2376 *tickp = bt->tick;
2377 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2379 debug1("returning old result (%d)\n", bt->lifespan);
2380 return bt->result;
2382 f = readpipe(bt->cmdv);
2383 if (f == -1)
2384 return bt->result;
2385 i = 0;
2386 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2388 debug1("runbacktick: read %d bytes\n", l);
2389 i += l;
2390 for (j = 1; j < l; j++)
2391 if (bt->result[i - j - 1] == '\n')
2392 break;
2393 if (j == l && i == sizeof(bt->result))
2395 j = sizeof(bt->result) / 2;
2396 l = j + 1;
2398 if (j < l)
2400 bcopy(bt->result + i - j, bt->result, j);
2401 i = j;
2404 close(f);
2405 bt->result[sizeof(bt->result) - 1] = '\n';
2406 if (i && bt->result[i - 1] == '\n')
2407 i--;
2408 debug1("runbacktick: finished, %d bytes\n", i);
2409 bt->result[i] = 0;
2410 backtick_filter(bt);
2411 (void)time(&now2);
2412 bt->bestbefore = now2 + bt->lifespan;
2413 return bt->result;
2416 void
2417 AppendWinMsgRend(str, color)
2418 char *str, *color;
2420 char *p;
2421 int r = -1;
2422 if (winmsg_numrend >= MAX_WINMSG_REND)
2423 return;
2424 p = winmsg_buf + strlen(winmsg_buf);
2425 if (color)
2427 if (*color != '-')
2429 r = ParseAttrColor(color, 0, 0);
2430 if (r == -1)
2431 return;
2433 winmsg_rend[winmsg_numrend] = r;
2434 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2435 winmsg_numrend++;
2437 strncpy(p, str, winmsg_buf + sizeof(winmsg_buf) - p);
2440 char *
2441 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2442 char *str;
2443 struct win *win;
2444 int esc;
2445 int padlen;
2446 struct event *ev;
2447 int rec;
2449 static int tick;
2450 char *s = str;
2451 register char *p = winmsg_buf;
2452 register int ctrl;
2453 struct timeval now;
2454 struct tm *tm;
2455 int l, i, r;
2456 int num;
2457 int zeroflg;
2458 int longflg;
2459 int minusflg;
2460 int plusflg;
2461 int qmflag = 0, omflag = 0, qmnumrend = 0;
2462 char *qmpos = 0;
2463 int numpad = 0;
2464 int lastpad = 0;
2465 int truncpos = -1;
2466 int truncper = 0;
2467 int trunclong = 0;
2468 struct backtick *bt;
2470 if (winmsg_numrend >= 0)
2471 winmsg_numrend = 0;
2472 else
2473 winmsg_numrend = -winmsg_numrend;
2475 *p = '\0';
2476 if (ScriptProcessCaption(str, win, padlen))
2477 return winmsg_buf;
2478 if (!display)
2479 return winmsg_buf;
2481 tick = 0;
2482 tm = 0;
2483 ctrl = 0;
2484 gettimeofday(&now, NULL);
2485 for (s = str; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2487 *p = *s;
2488 if (ctrl)
2490 ctrl = 0;
2491 if (*s != '^' && *s >= 64)
2492 *p &= 0x1f;
2493 continue;
2495 if (*s != esc)
2497 if (esc == '%')
2499 switch (*s)
2501 #if 0
2502 case '~':
2503 *p = BELL;
2504 break;
2505 #endif
2506 case '^':
2507 ctrl = 1;
2508 *p-- = '^';
2509 break;
2510 default:
2511 break;
2514 continue;
2516 if (*++s == esc) /* double escape ? */
2517 continue;
2518 if ((plusflg = *s == '+') != 0)
2519 s++;
2520 if ((minusflg = *s == '-') != 0)
2521 s++;
2522 if ((zeroflg = *s == '0') != 0)
2523 s++;
2524 num = 0;
2525 while(*s >= '0' && *s <= '9')
2526 num = num * 10 + (*s++ - '0');
2527 if ((longflg = *s == 'L') != 0)
2528 s++;
2529 switch (*s)
2531 case '?':
2532 p--;
2533 if (qmpos)
2535 if ((!qmflag && !omflag) || omflag == 1)
2537 p = qmpos;
2538 if (qmnumrend < winmsg_numrend)
2539 winmsg_numrend = qmnumrend;
2541 qmpos = 0;
2542 break;
2544 qmpos = p;
2545 qmnumrend = winmsg_numrend;
2546 qmflag = omflag = 0;
2547 break;
2548 case ':':
2549 p--;
2550 if (!qmpos)
2551 break;
2552 if (qmflag && omflag != 1)
2554 omflag = 1;
2555 qmpos = p;
2556 qmnumrend = winmsg_numrend;
2558 else
2560 p = qmpos;
2561 if (qmnumrend < winmsg_numrend)
2562 winmsg_numrend = qmnumrend;
2563 omflag = -1;
2565 break;
2566 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2567 case 'a': case 'A': case 's': case 'c': case 'C':
2568 if (l < 4)
2569 break;
2570 if (tm == 0)
2572 time_t nowsec = now.tv_sec;
2573 tm = localtime(&nowsec);
2575 qmflag = 1;
2576 if (!tick || tick > 3600)
2577 tick = 3600;
2578 switch (*s)
2580 case 'd':
2581 sprintf(p, "%02d", tm->tm_mday % 100);
2582 break;
2583 case 'D':
2584 #ifdef USE_LOCALE
2585 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2586 #else
2587 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2588 #endif
2589 break;
2590 case 'm':
2591 sprintf(p, "%02d", tm->tm_mon + 1);
2592 break;
2593 case 'M':
2594 #ifdef USE_LOCALE
2595 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2596 #else
2597 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2598 #endif
2599 break;
2600 case 'y':
2601 sprintf(p, "%02d", tm->tm_year % 100);
2602 break;
2603 case 'Y':
2604 sprintf(p, "%04d", tm->tm_year + 1900);
2605 break;
2606 case 'a':
2607 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2608 break;
2609 case 'A':
2610 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2611 break;
2612 case 's':
2613 sprintf(p, "%02d", tm->tm_sec);
2614 tick = 1;
2615 break;
2616 case 'c':
2617 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2618 if (!tick || tick > 60)
2619 tick = 60;
2620 break;
2621 case 'C':
2622 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2623 if (!tick || tick > 60)
2624 tick = 60;
2625 break;
2626 default:
2627 break;
2629 p += strlen(p) - 1;
2630 break;
2631 case 'l':
2632 #ifdef LOADAV
2633 *p = 0;
2634 if (l > 20)
2635 AddLoadav(p);
2636 if (*p)
2638 qmflag = 1;
2639 p += strlen(p) - 1;
2641 else
2642 *p = '?';
2643 if (!tick || tick > 60)
2644 tick = 60;
2645 #else
2646 *p = '?';
2647 #endif
2648 p += strlen(p) - 1;
2649 break;
2650 case '`':
2651 case 'h':
2652 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2654 p--;
2655 break;
2657 if (*s == '`')
2659 for (bt = backticks; bt; bt = bt->next)
2660 if (bt->num == num)
2661 break;
2662 if (bt == 0)
2664 p--;
2665 break;
2669 char savebuf[sizeof(winmsg_buf)];
2670 int oldtick = tick;
2671 int oldnumrend = winmsg_numrend;
2673 *p = 0;
2674 strcpy(savebuf, winmsg_buf);
2675 winmsg_numrend = -winmsg_numrend;
2676 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2677 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2678 if (!tick || oldtick < tick)
2679 tick = oldtick;
2680 if ((int)strlen(winmsg_buf) < l)
2681 strcat(savebuf, winmsg_buf);
2682 strcpy(winmsg_buf, savebuf);
2683 while (oldnumrend < winmsg_numrend)
2684 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2685 if (*p)
2686 qmflag = 1;
2687 p += strlen(p) - 1;
2689 break;
2690 case 'w':
2691 case 'W':
2693 struct win *oldfore = 0;
2694 char *ss;
2696 if (display)
2698 oldfore = D_fore;
2699 D_fore = win;
2701 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
2702 if (minusflg)
2703 *ss = 0;
2704 if (display)
2705 D_fore = oldfore;
2707 if (*p)
2708 qmflag = 1;
2709 p += strlen(p) - 1;
2710 break;
2711 case 'u':
2712 *p = 0;
2713 if (win)
2714 AddOtherUsers(p, l - 1, win);
2715 if (*p)
2716 qmflag = 1;
2717 p += strlen(p) - 1;
2718 break;
2719 case 'f':
2720 *p = 0;
2721 if (win)
2722 AddWindowFlags(p, l - 1, win);
2723 if (*p)
2724 qmflag = 1;
2725 p += strlen(p) - 1;
2726 break;
2727 case 't':
2728 *p = 0;
2729 if (win && (int)strlen(win->w_title) < l)
2731 strcpy(p, win->w_title);
2732 if (*p)
2733 qmflag = 1;
2735 p += strlen(p) - 1;
2736 break;
2737 case '{':
2739 char rbuf[128];
2740 s++;
2741 for (i = 0; i < 127; i++)
2742 if (s[i] && s[i] != '}')
2743 rbuf[i] = s[i];
2744 else
2745 break;
2746 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2748 r = -1;
2749 rbuf[i] = 0;
2750 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2751 if (i != 1 || rbuf[0] != '-')
2752 r = ParseAttrColor(rbuf, (char *)0, 0);
2753 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2755 winmsg_rend[winmsg_numrend] = r;
2756 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2757 winmsg_numrend++;
2760 s += i;
2761 p--;
2763 break;
2764 case 'H':
2765 *p = 0;
2766 if ((int)strlen(HostName) < l)
2768 strcpy(p, HostName);
2769 if (*p)
2770 qmflag = 1;
2772 p += strlen(p) - 1;
2773 break;
2774 case 'S':
2776 char *session_name;
2777 *p = 0;
2778 session_name = strchr(SockName, '.') + 1;
2779 if ((int)strlen(session_name) < l)
2781 strcpy(p, session_name);
2782 if (*p)
2783 qmflag = 1;
2785 p += strlen(p) - 1;
2787 break;
2788 case 'p':
2790 sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2791 p += strlen(p) - 1;
2793 break;
2794 case 'F':
2795 p--;
2796 /* small hack */
2797 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2798 minusflg = !minusflg;
2799 if (minusflg)
2800 qmflag = 1;
2801 break;
2802 case '>':
2803 truncpos = p - winmsg_buf;
2804 truncper = num > 100 ? 100 : num;
2805 trunclong = longflg;
2806 p--;
2807 break;
2808 case '=':
2809 case '<':
2810 *p = ' ';
2811 if (num || zeroflg || plusflg || longflg || (*s != '='))
2813 /* expand all pads */
2814 if (minusflg)
2816 num = (plusflg ? lastpad : padlen) - num;
2817 if (!plusflg && padlen == 0)
2818 num = p - winmsg_buf;
2819 plusflg = 0;
2821 else if (!zeroflg)
2823 if (*s != '=' && num == 0 && !plusflg)
2824 num = 100;
2825 if (num > 100)
2826 num = 100;
2827 if (padlen == 0)
2828 num = p - winmsg_buf;
2829 else
2830 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2832 if (num < 0)
2833 num = 0;
2834 if (plusflg)
2835 num += lastpad;
2836 if (num > MAXSTR - 1)
2837 num = MAXSTR - 1;
2838 if (numpad)
2839 p = pad_expand(winmsg_buf, p, numpad, num);
2840 numpad = 0;
2841 if (p - winmsg_buf > num && !longflg)
2843 int left, trunc;
2845 if (truncpos == -1)
2847 truncpos = lastpad;
2848 truncper = 0;
2850 trunc = lastpad + truncper * (num - lastpad) / 100;
2851 if (trunc > num)
2852 trunc = num;
2853 if (trunc < lastpad)
2854 trunc = lastpad;
2855 left = truncpos - trunc;
2856 if (left > p - winmsg_buf - num)
2857 left = p - winmsg_buf - num;
2858 debug1("lastpad = %d, ", lastpad);
2859 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2860 if (left > 0)
2862 if (left + lastpad > p - winmsg_buf)
2863 left = p - winmsg_buf - lastpad;
2864 if (p - winmsg_buf - lastpad - left > 0)
2865 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2866 p -= left;
2867 r = winmsg_numrend;
2868 while (r && winmsg_rendpos[r - 1] > lastpad)
2870 r--;
2871 winmsg_rendpos[r] -= left;
2872 if (winmsg_rendpos[r] < lastpad)
2873 winmsg_rendpos[r] = lastpad;
2875 if (trunclong)
2877 if (p - winmsg_buf > lastpad)
2878 winmsg_buf[lastpad] = '.';
2879 if (p - winmsg_buf > lastpad + 1)
2880 winmsg_buf[lastpad + 1] = '.';
2881 if (p - winmsg_buf > lastpad + 2)
2882 winmsg_buf[lastpad + 2] = '.';
2885 if (p - winmsg_buf > num)
2887 p = winmsg_buf + num;
2888 if (trunclong)
2890 if (num - 1 >= lastpad)
2891 p[-1] = '.';
2892 if (num - 2 >= lastpad)
2893 p[-2] = '.';
2894 if (num - 3 >= lastpad)
2895 p[-3] = '.';
2897 r = winmsg_numrend;
2898 while (r && winmsg_rendpos[r - 1] > num)
2899 winmsg_rendpos[--r] = num;
2901 truncpos = -1;
2902 trunclong = 0;
2903 if (lastpad > p - winmsg_buf)
2904 lastpad = p - winmsg_buf;
2905 debug1("lastpad now %d\n", lastpad);
2907 if (*s == '=')
2909 while (p - winmsg_buf < num)
2910 *p++ = ' ';
2911 lastpad = p - winmsg_buf;
2912 truncpos = -1;
2913 trunclong = 0;
2914 debug1("lastpad2 now %d\n", lastpad);
2916 p--;
2918 else if (padlen)
2920 *p = 127; /* internal pad representation */
2921 numpad++;
2923 break;
2924 case 'n':
2925 s++;
2926 /* FALLTHROUGH */
2927 default:
2928 s--;
2929 if (l > 10 + num)
2931 if (num == 0)
2932 num = 1;
2933 if (!win)
2934 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2935 else
2936 sprintf(p, "%*d", num, win->w_number);
2937 qmflag = 1;
2938 p += strlen(p) - 1;
2940 break;
2943 if (qmpos && !qmflag)
2944 p = qmpos + 1;
2945 *p = '\0';
2946 if (numpad)
2948 if (padlen > MAXSTR - 1)
2949 padlen = MAXSTR - 1;
2950 p = pad_expand(winmsg_buf, p, numpad, padlen);
2952 if (ev)
2954 evdeq(ev); /* just in case */
2955 ev->timeout.tv_sec = 0;
2956 ev->timeout.tv_usec = 0;
2958 if (ev && tick)
2960 now.tv_usec = 100000;
2961 if (tick == 1)
2962 now.tv_sec++;
2963 else
2964 now.tv_sec += tick - (now.tv_sec % tick);
2965 ev->timeout = now;
2966 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2968 return winmsg_buf;
2971 char *
2972 MakeWinMsg(s, win, esc)
2973 char *s;
2974 struct win *win;
2975 int esc;
2977 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2980 void
2981 PutWinMsg(s, start, max)
2982 char *s;
2983 int start, max;
2985 int i, p, l, r, n;
2986 struct mchar rend;
2987 struct mchar rendstack[MAX_WINMSG_REND];
2988 int rendstackn = 0;
2990 if (s != winmsg_buf)
2992 /* sorry, no fancy coloring available */
2993 debug1("PutWinMsg %s plain\n", s);
2994 l = strlen(s);
2995 if (l > max)
2996 l = max;
2997 l -= start;
2998 s += start;
2999 while (l-- > 0)
3000 PUTCHARLP(*s++);
3001 return;
3003 rend = D_rend;
3004 p = 0;
3005 l = strlen(s);
3006 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3007 for (i = 0; i < winmsg_numrend && max > 0; i++)
3009 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3010 break;
3011 if (p < winmsg_rendpos[i])
3013 n = winmsg_rendpos[i] - p;
3014 if (n > max)
3015 n = max;
3016 max -= n;
3017 p += n;
3018 while(n-- > 0)
3020 if (start-- > 0)
3021 s++;
3022 else
3023 PUTCHARLP(*s++);
3026 r = winmsg_rend[i];
3027 if (r == -1)
3029 if (rendstackn > 0)
3030 rend = rendstack[--rendstackn];
3032 else
3034 rendstack[rendstackn++] = rend;
3035 ApplyAttrColor(r, &rend);
3037 SetRendition(&rend);
3039 if (p < l)
3041 n = l - p;
3042 if (n > max)
3043 n = max;
3044 while(n-- > 0)
3046 if (start-- > 0)
3047 s++;
3048 else
3049 PUTCHARLP(*s++);
3055 #ifdef DEBUG
3056 static void
3057 fds1(i, j)
3058 int i, j;
3060 while (i < j)
3062 debug1("%d ", i);
3063 i++;
3065 if ((j = open("/dev/null", 0)) >= 0)
3067 fds1(i + 1, j);
3068 close(j);
3070 else
3072 while (dup(++i) < 0 && errno != EBADF)
3073 debug1("%d ", i);
3074 debug1(" [%d]\n", i);
3078 static void
3079 fds()
3081 debug("fds: ");
3082 fds1(-1, -1);
3084 #endif
3086 static void
3087 serv_read_fn(ev, data)
3088 struct event *ev;
3089 char *data;
3091 debug("Knock - knock!\n");
3092 ReceiveMsg();
3095 static void
3096 serv_select_fn(ev, data)
3097 struct event *ev;
3098 char *data;
3100 struct win *p;
3102 debug("serv_select_fn called\n");
3103 /* XXX: messages?? */
3104 if (GotSigChld)
3106 SigChldHandler();
3108 if (InterruptPlease)
3110 debug("Backend received interrupt\n");
3111 /* This approach is rather questionable in a multi-display
3112 * environment */
3113 if (fore && displays)
3115 #if defined(TERMIO) || defined(POSIX)
3116 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3117 #else
3118 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3119 #endif
3120 #ifdef PSEUDOS
3121 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3122 &ibuf, 1);
3123 debug1("Backend wrote interrupt to %d", fore->w_number);
3124 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3125 #else
3126 write(fore->w_ptyfd, &ibuf, 1);
3127 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3128 #endif
3130 InterruptPlease = 0;
3133 for (p = windows; p; p = p->w_next)
3135 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3137 struct canvas *cv;
3138 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3139 p->w_bell = BELL_ON;
3140 for (display = displays; display; display = display->d_next)
3142 for (cv = D_cvlist; cv; cv = cv->c_next)
3143 if (cv->c_layer->l_bottom == &p->w_layer)
3144 break;
3145 if (cv == 0)
3147 p->w_bell = BELL_DONE;
3148 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3150 else if (visual && !D_VB && (!D_status || !D_status_bell))
3152 Msg(0, "%s", VisualBellString);
3153 if (D_status)
3155 D_status_bell = 1;
3156 debug1("using vbell timeout %d\n", VBellWait);
3157 SetTimeout(&D_statusev, VBellWait );
3161 /* don't annoy the user with two messages */
3162 if (p->w_monitor == MON_FOUND)
3163 p->w_monitor = MON_DONE;
3164 WindowChanged(p, 'f');
3166 if (p->w_monitor == MON_FOUND)
3168 struct canvas *cv;
3169 p->w_monitor = MON_ON;
3170 for (display = displays; display; display = display->d_next)
3172 for (cv = D_cvlist; cv; cv = cv->c_next)
3173 if (cv->c_layer->l_bottom == &p->w_layer)
3174 break;
3175 if (cv)
3176 continue; /* user already sees window */
3177 #ifdef MULTIUSER
3178 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3179 continue; /* user doesn't care */
3180 #endif
3181 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3182 p->w_monitor = MON_DONE;
3184 WindowChanged(p, 'f');
3188 for (display = displays; display; display = display->d_next)
3190 struct canvas *cv;
3191 if (D_status == STATUS_ON_WIN)
3192 continue;
3193 /* XXX: should use display functions! */
3194 for (cv = D_cvlist; cv; cv = cv->c_next)
3196 int lx, ly;
3198 /* normalize window, see resize.c */
3199 lx = cv->c_layer->l_x;
3200 ly = cv->c_layer->l_y;
3201 if (lx == cv->c_layer->l_width)
3202 lx--;
3203 if (ly + cv->c_yoff < cv->c_ys)
3205 int i, n = cv->c_ys - (ly + cv->c_yoff);
3206 cv->c_yoff = cv->c_ys - ly;
3207 RethinkViewportOffsets(cv);
3208 if (n > cv->c_layer->l_height)
3209 n = cv->c_layer->l_height;
3210 CV_CALL(cv,
3211 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3212 LayRedisplayLine(-1, -1, -1, 1);
3213 for (i = 0; i < n; i++)
3214 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3215 if (cv == cv->c_display->d_forecv)
3216 LaySetCursor();
3219 else if (ly + cv->c_yoff > cv->c_ye)
3221 int i, n = ly + cv->c_yoff - cv->c_ye;
3222 cv->c_yoff = cv->c_ye - ly;
3223 RethinkViewportOffsets(cv);
3224 if (n > cv->c_layer->l_height)
3225 n = cv->c_layer->l_height;
3226 CV_CALL(cv,
3227 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3228 LayRedisplayLine(-1, -1, -1, 1);
3229 for (i = 0; i < n; i++)
3230 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3231 if (cv == cv->c_display->d_forecv)
3232 LaySetCursor();
3235 if (lx + cv->c_xoff < cv->c_xs)
3237 int i, n = cv->c_xs - (lx + cv->c_xoff);
3238 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3239 n = (cv->c_xe - cv->c_xs + 1) / 2;
3240 if (cv->c_xoff + n > cv->c_xs)
3241 n = cv->c_xs - cv->c_xoff;
3242 cv->c_xoff += n;
3243 RethinkViewportOffsets(cv);
3244 if (n > cv->c_layer->l_width)
3245 n = cv->c_layer->l_width;
3246 CV_CALL(cv,
3247 LayRedisplayLine(-1, -1, -1, 1);
3248 for (i = 0; i < flayer->l_height; i++)
3250 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3251 LayRedisplayLine(i, 0, n - 1, 1);
3253 if (cv == cv->c_display->d_forecv)
3254 LaySetCursor();
3257 else if (lx + cv->c_xoff > cv->c_xe)
3259 int i, n = lx + cv->c_xoff - cv->c_xe;
3260 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3261 n = (cv->c_xe - cv->c_xs + 1) / 2;
3262 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3263 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3264 cv->c_xoff -= n;
3265 RethinkViewportOffsets(cv);
3266 if (n > cv->c_layer->l_width)
3267 n = cv->c_layer->l_width;
3268 CV_CALL(cv,
3269 LayRedisplayLine(-1, -1, -1, 1);
3270 for (i = 0; i < flayer->l_height; i++)
3272 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3273 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3275 if (cv == cv->c_display->d_forecv)
3276 LaySetCursor();
3282 for (display = displays; display; display = display->d_next)
3284 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3285 continue;
3286 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3287 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3291 static void
3292 logflush_fn(ev, data)
3293 struct event *ev;
3294 char *data;
3296 struct win *p;
3297 char *buf;
3298 int n;
3300 if (!islogfile(NULL))
3301 return; /* no more logfiles */
3302 logfflush(NULL);
3303 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3304 if (n)
3306 SetTimeout(ev, n * 1000);
3307 evenq(ev); /* re-enqueue ourself */
3309 if (!logtstamp_on)
3310 return;
3311 /* write fancy time-stamp */
3312 for (p = windows; p; p = p->w_next)
3314 if (!p->w_log)
3315 continue;
3316 p->w_logsilence += n;
3317 if (p->w_logsilence < logtstamp_after)
3318 continue;
3319 if (p->w_logsilence - n >= logtstamp_after)
3320 continue;
3321 buf = MakeWinMsg(logtstamp_string, p, '%');
3322 logfwrite(p->w_log, buf, strlen(buf));
3327 * Interprets ^?, ^@ and other ^-control-char notation.
3328 * Interprets \ddd octal notation
3330 * The result is placed in *cp, p is advanced behind the parsed expression and
3331 * returned.
3333 static char *
3334 ParseChar(p, cp)
3335 char *p, *cp;
3337 if (*p == 0)
3338 return 0;
3339 if (*p == '^' && p[1])
3341 if (*++p == '?')
3342 *cp = '\177';
3343 else if (*p >= '@')
3344 *cp = Ctrl(*p);
3345 else
3346 return 0;
3347 ++p;
3349 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3351 *cp = 0;
3353 *cp = *cp * 8 + *p - '0';
3354 while (*++p <= '7' && *p >= '0');
3356 else
3357 *cp = *p++;
3358 return p;
3361 static int
3362 ParseEscape(p)
3363 char *p;
3365 unsigned char buf[2];
3367 if (*p == 0)
3368 SetEscape((struct acluser *)0, -1, -1);
3369 else
3371 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3372 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3373 return -1;
3374 SetEscape((struct acluser *)0, buf[0], buf[1]);
3376 return 0;