Merge branch 'master' into query-result
[screen-lua.git] / src / screen.c
blobce081ac01f4df0f459a48cecb1f4181df32ee537
1 /* Copyright (c) 2008, 2009
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Micah Cowan (micah@cowan.name)
5 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
6 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
7 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
8 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
9 * Copyright (c) 1987 Oliver Laumann
10 #ifdef HAVE_BRAILLE
11 * Modified by:
12 * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu
13 * Bill Barry barryb@dots.physics.orst.edu
14 * Randy Lundquist randyl@dots.physics.orst.edu
16 * Modifications Copyright (c) 1995 by
17 * Science Access Project, Oregon State University.
18 #endif
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 3, or (at your option)
23 * any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program (see the file COPYING); if not, see
32 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
33 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
35 ****************************************************************
38 #include <sys/types.h>
39 #include <ctype.h>
41 #include <fcntl.h>
43 #ifdef sgi
44 # include <sys/sysmacros.h>
45 #endif
47 #include <sys/stat.h>
48 #ifndef sun
49 # include <sys/ioctl.h>
50 #endif
52 #ifndef SIGINT
53 # include <signal.h>
54 #endif
56 #include "config.h"
58 #ifdef HAVE_STROPTS_H
59 # include <sys/stropts.h>
60 #endif
62 #if defined(SYSV) && !defined(ISC)
63 # include <sys/utsname.h>
64 #endif
66 #if defined(sequent) || defined(SVR4)
67 # include <sys/resource.h>
68 #endif /* sequent || SVR4 */
70 #ifdef ISC
71 # include <sys/tty.h>
72 # include <sys/sioctl.h>
73 # include <sys/pty.h>
74 #endif /* ISC */
76 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
77 # include <compat.h>
78 #endif
79 #if defined(USE_LOCALE) || defined(ENCODINGS)
80 # include <locale.h>
81 #endif
82 #if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
83 # include <langinfo.h>
84 #endif
86 #include "screen.h"
87 #ifdef HAVE_BRAILLE
88 # include "braille.h"
89 #endif
91 #include "patchlevel.h"
94 * At the moment we only need the real password if the
95 * builtin lock is used. Therefore disable SHADOWPW if
96 * we do not really need it (kind of security thing).
98 #ifndef LOCK
99 # undef SHADOWPW
100 #endif
102 #include <pwd.h>
103 #ifdef SHADOWPW
104 # include <shadow.h>
105 #endif /* SHADOWPW */
107 #include "logfile.h" /* islogfile, logfflush */
109 #ifdef DEBUG
110 FILE *dfp;
111 #endif
114 extern char Term[], screenterm[], **environ, Termcap[];
115 int force_vt = 1;
116 int VBellWait, MsgWait, MsgMinWait, SilenceWait;
118 extern struct acluser *users;
119 extern struct display *displays, *display;
121 extern struct LayFuncs MarkLf;
124 extern int visual_bell;
125 #ifdef COPY_PASTE
126 extern unsigned char mark_key_tab[];
127 #endif
128 extern char version[];
129 extern char DefaultShell[];
130 #ifdef ZMODEM
131 extern char *zmodem_sendcmd;
132 extern char *zmodem_recvcmd;
133 #endif
134 extern struct layout *layout_last;
137 char *ShellProg;
138 char *ShellArgs[2];
140 extern struct NewWindow nwin_undef, nwin_default, nwin_options;
141 struct backtick;
143 static struct passwd *getpwbyname __P((char *, struct passwd *));
144 static void SigChldHandler __P((void));
145 static sigret_t SigChld __P(SIGPROTOARG);
146 static sigret_t SigInt __P(SIGPROTOARG);
147 static sigret_t CoreDump __P(SIGPROTOARG);
148 static sigret_t FinitHandler __P(SIGPROTOARG);
149 static void DoWait __P((void));
150 static void serv_read_fn __P((struct event *, char *));
151 static void serv_select_fn __P((struct event *, char *));
152 static void logflush_fn __P((struct event *, char *));
153 static void backtick_filter __P((struct backtick *));
154 static void backtick_fn __P((struct event *, char *));
155 static char *runbacktick __P((struct backtick *, int *, time_t));
156 static int IsSymbol __P((char *, char *));
157 static char *ParseChar __P((char *, char *));
158 static int ParseEscape __P((char *));
159 static char *pad_expand __P((char *, char *, int, int));
160 #ifdef DEBUG
161 static void fds __P((void));
162 #endif
164 int nversion; /* numerical version, used for secondary DA */
166 /* the attacher */
167 struct passwd *ppp;
168 char *attach_tty;
169 int attach_fd = -1;
170 char *attach_term;
171 char *LoginName;
172 struct mode attach_Mode;
174 char SockPath[MAXPATHLEN + 2 * MAXSTR];
175 char *SockName; /* SockName is pointer in SockPath */
176 char *SockMatch = NULL; /* session id command line argument */
177 int ServerSocket = -1;
178 struct event serv_read;
179 struct event serv_select;
180 struct event logflushev;
182 char **NewEnv = NULL;
184 char *RcFileName = NULL;
185 char *home;
187 char *screenlogfile; /* filename layout */
188 int log_flush = 10; /* flush interval in seconds */
189 int logtstamp_on = 0; /* tstamp disabled */
190 char *logtstamp_string; /* stamp layout */
191 int logtstamp_after = 120; /* first tstamp after 120s */
192 char *hardcopydir = NULL;
193 char *BellString;
194 char *VisualBellString;
195 char *ActivityString;
196 #ifdef COPY_PASTE
197 char *BufferFile;
198 #endif
199 #ifdef POW_DETACH
200 char *PowDetachString;
201 #endif
202 char *hstatusstring;
203 char *captionstring;
204 char *timestring;
205 char *wliststr;
206 char *wlisttit;
207 int auto_detach = 1;
208 int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
209 int cmdflag;
210 int queryflag;
211 int adaptflag;
213 #ifdef MULTIUSER
214 char *multi;
215 char *multi_home;
216 int multi_uid;
217 int own_uid;
218 int multiattach;
219 int tty_mode;
220 int tty_oldmode = -1;
221 #endif
223 char HostName[MAXSTR];
224 int MasterPid, PanicPid;
225 int real_uid, real_gid, eff_uid, eff_gid;
226 int default_startup;
227 int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
228 char *preselect = NULL; /* only used in Attach() */
230 #ifdef UTF8
231 char *screenencodings;
232 #endif
234 #ifdef DW_CHARS
235 int cjkwidth;
236 #endif
238 #ifdef NETHACK
239 int nethackflag = 0;
240 #endif
241 int maxwin;
244 struct layer *flayer;
245 struct win *fore;
246 struct win *windows;
247 struct win *console_window;
252 * Do this last
254 #include "extern.h"
256 char strnomem[] = "Out of memory.";
259 static int InterruptPlease;
260 static int GotSigChld;
262 static int
263 lf_secreopen(name, wantfd, l)
264 char *name;
265 int wantfd;
266 struct logfile *l;
268 int got_fd;
270 close(wantfd);
271 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
272 lf_move_fd(got_fd, wantfd) < 0)
274 logfclose(l);
275 debug1("lf_secreopen: failed for %s\n", name);
276 return -1;
278 l->st->st_ino = l->st->st_dev = 0;
279 debug2("lf_secreopen: %d = %s\n", wantfd, name);
280 return 0;
283 /********************************************************************/
284 /********************************************************************/
285 /********************************************************************/
288 static struct passwd *
289 getpwbyname(name, ppp)
290 char *name;
291 struct passwd *ppp;
293 int n;
294 #ifdef SHADOWPW
295 struct spwd *sss = NULL;
296 static char *spw = NULL;
297 #endif
299 if (!ppp && !(ppp = getpwnam(name)))
300 return NULL;
302 /* Do password sanity check..., allow ##user for SUN_C2 security */
303 #ifdef SHADOWPW
304 pw_try_again:
305 #endif
306 n = 0;
307 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
308 strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
309 n = 13;
310 for (; n < 13; n++)
312 char c = ppp->pw_passwd[n];
313 if (!(c == '.' || c == '/' || c == '$' ||
314 (c >= '0' && c <= '9') ||
315 (c >= 'a' && c <= 'z') ||
316 (c >= 'A' && c <= 'Z')))
317 break;
320 #ifdef SHADOWPW
321 /* try to determine real password */
322 if (n < 13 && sss == 0)
324 sss = getspnam(ppp->pw_name);
325 if (sss)
327 if (spw)
328 free(spw);
329 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
330 endspent(); /* this should delete all buffers ... */
331 goto pw_try_again;
333 endspent(); /* this should delete all buffers ... */
335 #endif
336 if (n < 13)
337 ppp->pw_passwd = 0;
338 #ifdef linux
339 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
340 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
341 #endif
343 return ppp;
346 static char *
347 locale_name(void)
349 static char *s;
351 if (!s)
353 s = getenv("LC_ALL");
354 if (s == NULL)
355 s = getenv("LC_CTYPE");
356 if (s == NULL)
357 s = getenv("LANG");
359 return s;
363 main(ac, av)
364 int ac;
365 char **av;
367 register int n;
368 char *ap;
369 char *av0;
370 char socknamebuf[2 * MAXSTR];
371 int mflag = 0;
372 char *myname = (ac == 0) ? "screen" : av[0];
373 char *SockDir;
374 struct stat st;
375 #ifdef _MODE_T /* (jw) */
376 mode_t oumask;
377 #else
378 int oumask;
379 #endif
380 #if defined(SYSV) && !defined(ISC)
381 struct utsname utsnam;
382 #endif
383 struct NewWindow nwin;
384 int detached = 0; /* start up detached */
385 #ifdef MULTIUSER
386 char *sockp;
387 #endif
388 char *sty = 0;
390 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
391 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
392 #endif
393 #if defined(sun) && defined(SVR4)
395 /* Solaris' login blocks SIGHUP! This is _very bad_ */
396 sigset_t sset;
397 sigemptyset(&sset);
398 sigprocmask(SIG_SETMASK, &sset, 0);
400 #endif
403 * First, close all unused descriptors
404 * (otherwise, we might have problems with the select() call)
406 closeallfiles(0);
407 #ifdef DEBUG
408 opendebug(1, 0);
409 #endif
410 snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
411 PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
412 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
413 debug2("-- screen debug started %s (%s)\n", *av, version);
414 #ifdef POSIX
415 debug("POSIX\n");
416 #endif
417 #ifdef TERMIO
418 debug("TERMIO\n");
419 #endif
420 #ifdef SYSV
421 debug("SYSV\n");
422 #endif
423 #ifdef SYSVSIGS
424 debug("SYSVSIGS\n");
425 #endif
426 #ifdef NAMEDPIPE
427 debug("NAMEDPIPE\n");
428 #endif
429 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
430 debug("Window size changing enabled\n");
431 #endif
432 #ifdef HAVE_SETREUID
433 debug("SETREUID\n");
434 #endif
435 #ifdef HAVE_SETEUID
436 debug("SETEUID\n");
437 #endif
438 #ifdef hpux
439 debug("hpux\n");
440 #endif
441 #ifdef USEBCOPY
442 debug("USEBCOPY\n");
443 #endif
444 #ifdef UTMPOK
445 debug("UTMPOK\n");
446 #endif
447 #ifdef LOADAV
448 debug("LOADAV\n");
449 #endif
450 #ifdef NETHACK
451 debug("NETHACK\n");
452 #endif
453 #ifdef TERMINFO
454 debug("TERMINFO\n");
455 #endif
456 #ifdef SHADOWPW
457 debug("SHADOWPW\n");
458 #endif
459 #ifdef NAME_MAX
460 debug1("NAME_MAX = %d\n", NAME_MAX);
461 #endif
463 BellString = SaveStr("Bell in window %n");
464 VisualBellString = SaveStr(" Wuff, Wuff!! ");
465 ActivityString = SaveStr("Activity in window %n");
466 screenlogfile = SaveStr("screenlog.%n");
467 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
468 hstatusstring = SaveStr("%h");
469 captionstring = SaveStr("%4n %t");
470 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
471 wlisttit = SaveStr(" Num Name%=Flags");
472 wliststr = SaveStr("%4n %t%=%f");
473 #ifdef COPY_PASTE
474 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
475 #endif
476 ShellProg = NULL;
477 #ifdef POW_DETACH
478 PowDetachString = 0;
479 #endif
480 default_startup = (ac > 1) ? 0 : 1;
481 adaptflag = 0;
482 VBellWait = VBELLWAIT * 1000;
483 MsgWait = MSGWAIT * 1000;
484 MsgMinWait = MSGMINWAIT * 1000;
485 SilenceWait = SILENCEWAIT;
486 #ifdef HAVE_BRAILLE
487 InitBraille();
488 #endif
489 #ifdef ZMODEM
490 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
491 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
492 #endif
494 #ifdef COPY_PASTE
495 CompileKeys((char *)0, 0, mark_key_tab);
496 #endif
497 #ifdef UTF8
498 InitBuiltinTabs();
499 screenencodings = SaveStr(SCREENENCODINGS);
500 #endif
501 #ifdef DW_CHARS
502 cjkwidth = 0;
503 #endif
504 nwin = nwin_undef;
505 nwin_options = nwin_undef;
506 strcpy(screenterm, "screen");
508 logreopen_register(lf_secreopen);
510 av0 = *av;
511 /* if this is a login screen, assume -RR */
512 if (*av0 == '-')
514 rflag = 4;
515 #ifdef MULTI
516 xflag = 1;
517 #else
518 dflag = 1;
519 #endif
520 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
522 while (ac > 0)
524 ap = *++av;
525 if (--ac > 0 && *ap == '-')
527 if (ap[1] == '-' && ap[2] == 0)
529 av++;
530 ac--;
531 break;
533 if (ap[1] == '-' && !strcmp(ap, "--version"))
534 Panic(0, "Screen version %s", version);
535 if (ap[1] == '-' && !strcmp(ap, "--help"))
536 exit_with_usage(myname, NULL, NULL);
537 while (ap && *ap && *++ap)
539 switch (*ap)
541 case 'a':
542 nwin_options.aflag = 1;
543 break;
544 case 'A':
545 adaptflag = 1;
546 break;
547 case 'p': /* preselect */
548 if (*++ap)
549 preselect = ap;
550 else
552 if (!--ac)
553 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
554 preselect = *++av;
556 ap = NULL;
557 break;
558 #ifdef HAVE_BRAILLE
559 case 'B':
560 bd.bd_start_braille = 1;
561 break;
562 #endif
563 case 'c':
564 if (*++ap)
565 RcFileName = ap;
566 else
568 if (--ac == 0)
569 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
570 RcFileName = *++av;
572 ap = NULL;
573 break;
574 case 'e':
575 if (!*++ap)
577 if (--ac == 0)
578 exit_with_usage(myname, "Specify command characters with -e", NULL);
579 ap = *++av;
581 if (ParseEscape(ap))
582 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
583 ap = NULL;
584 break;
585 case 'f':
586 ap++;
587 switch (*ap++)
589 case 'n':
590 case '0':
591 nwin_options.flowflag = FLOW_NOW * 0;
592 break;
593 case '\0':
594 ap--;
595 /* FALLTHROUGH */
596 case 'y':
597 case '1':
598 nwin_options.flowflag = FLOW_NOW * 1;
599 break;
600 case 'a':
601 nwin_options.flowflag = FLOW_AUTOFLAG;
602 break;
603 default:
604 exit_with_usage(myname, "Unknown flow option -%s", --ap);
606 break;
607 case 'h':
608 if (--ac == 0)
609 exit_with_usage(myname, NULL, NULL);
610 nwin_options.histheight = atoi(*++av);
611 if (nwin_options.histheight < 0)
612 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
613 break;
614 case 'i':
615 iflag = 1;
616 break;
617 case 't': /* title, the former AkA == -k */
618 if (--ac == 0)
619 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
620 nwin_options.aka = *++av;
621 break;
622 case 'l':
623 ap++;
624 switch (*ap++)
626 case 'n':
627 case '0':
628 nwin_options.lflag = 0;
629 break;
630 case '\0':
631 ap--;
632 /* FALLTHROUGH */
633 case 'y':
634 case '1':
635 nwin_options.lflag = 1;
636 break;
637 case 'a':
638 nwin_options.lflag = 3;
639 break;
640 case 's': /* -ls */
641 case 'i': /* -list */
642 lsflag = 1;
643 if (ac > 1 && !SockMatch)
645 SockMatch = *++av;
646 ac--;
648 ap = NULL;
649 break;
650 default:
651 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
653 break;
654 case 'w':
655 lsflag = 1;
656 wipeflag = 1;
657 if (ac > 1 && !SockMatch)
659 SockMatch = *++av;
660 ac--;
662 break;
663 case 'L':
664 nwin_options.Lflag = 1;
665 break;
666 case 'm':
667 mflag = 1;
668 break;
669 case 'O': /* to be (or not to be?) deleted. jw. */
670 force_vt = 0;
671 break;
672 case 'T':
673 if (--ac == 0)
674 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
675 if (strlen(*++av) < 20)
676 strcpy(screenterm, *av);
677 else
678 Panic(0, "-T: terminal name too long. (max. 20 char)");
679 nwin_options.term = screenterm;
680 break;
681 case 'q':
682 quietflag = 1;
683 break;
684 case 'Q':
685 queryflag = 1;
686 cmdflag = 1;
687 break;
688 case 'r':
689 case 'R':
690 #ifdef MULTI
691 case 'x':
692 #endif
693 if (ac > 1 && *av[1] != '-' && !SockMatch)
695 SockMatch = *++av;
696 ac--;
697 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
699 #ifdef MULTI
700 if (*ap == 'x')
701 xflag = 1;
702 #endif
703 if (rflag)
704 rflag = 2;
705 rflag += (*ap == 'R') ? 2 : 1;
706 break;
707 #ifdef REMOTE_DETACH
708 case 'd':
709 dflag = 1;
710 /* FALLTHROUGH */
711 case 'D':
712 if (!dflag)
713 dflag = 2;
714 if (ac == 2)
716 if (*av[1] != '-' && !SockMatch)
718 SockMatch = *++av;
719 ac--;
720 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
723 break;
724 #endif
725 case 's':
726 if (--ac == 0)
727 exit_with_usage(myname, "Specify shell with -s", NULL);
728 if (ShellProg)
729 free(ShellProg);
730 ShellProg = SaveStr(*++av);
731 debug1("ShellProg: '%s'\n", ShellProg);
732 break;
733 case 'S':
734 if (!SockMatch)
736 if (--ac == 0)
737 exit_with_usage(myname, "Specify session-name with -S", NULL);
738 SockMatch = *++av;
740 if (!*SockMatch)
741 exit_with_usage(myname, "Empty session-name?", NULL);
742 break;
743 case 'X':
744 cmdflag = 1;
745 break;
746 case 'v':
747 Panic(0, "Screen version %s", version);
748 /* NOTREACHED */
749 #ifdef UTF8
750 case 'U':
751 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
752 break;
753 #endif
754 default:
755 exit_with_usage(myname, "Unknown option %s", --ap);
759 else
760 break;
763 real_uid = getuid();
764 real_gid = getgid();
765 eff_uid = geteuid();
766 eff_gid = getegid();
767 if (eff_uid != real_uid)
769 /* if running with s-bit, we must install a special signal
770 * handler routine that resets the s-bit, so that we get a
771 * core file anyway.
773 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
774 signal(SIGBUS, CoreDump);
775 #endif /* SIGBUS */
776 signal(SIGSEGV, CoreDump);
779 #ifdef USE_LOCALE
780 setlocale(LC_ALL, "");
781 #endif
782 #ifdef ENCODINGS
783 if (nwin_options.encoding == -1)
785 /* ask locale if we should start in UTF-8 mode */
786 # ifdef HAVE_NL_LANGINFO
787 # ifndef USE_LOCALE
788 setlocale(LC_CTYPE, "");
789 # endif
790 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
791 debug1("locale says encoding = %d\n", nwin_options.encoding);
792 # else
793 # ifdef UTF8
794 char *s;
795 if ((s = locale_name()) && InStr(s, "UTF-8"))
796 nwin_options.encoding = UTF8;
797 # endif
798 debug1("environment says encoding=%d\n", nwin_options.encoding);
799 #endif
801 # ifdef DW_CHARS
803 char *s;
804 if ((s = locale_name()))
806 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
808 cjkwidth = 1;
812 #endif
813 #endif
814 if (nwin_options.aka)
816 #ifdef ENCODINGS
817 if (nwin_options.encoding > 0)
819 size_t len = strlen(nwin_options.aka);
820 size_t newsz;
821 char *newbuf = malloc(3 * len);
822 if (!newbuf)
823 Panic(0, strnomem);
824 newsz = RecodeBuf(nwin_options.aka, len,
825 nwin_options.encoding, 0, newbuf);
826 newbuf[newsz] = '\0';
827 nwin_options.aka = newbuf;
829 else
830 #endif
832 /* If we just use the original value from av,
833 subsequent shelltitle invocations will attempt to free
834 space we don't own... */
835 nwin_options.aka = SaveStr(nwin_options.aka);
839 if (SockMatch && strlen(SockMatch) >= MAXSTR)
840 Panic(0, "Ridiculously long socketname - try again.");
841 if (cmdflag && !rflag && !dflag && !xflag)
842 xflag = 1;
843 if (!cmdflag && dflag && mflag && !(rflag || xflag))
844 detached = 1;
845 nwin = nwin_options;
846 #ifdef ENCODINGS
847 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
848 #endif
849 if (ac)
850 nwin.args = av;
852 /* make the write() calls return -1 on all errors */
853 #ifdef SIGXFSZ
855 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
856 * It appears that in System V Release 4, UNIX, if you are writing
857 * an output file and you exceed the currently set file size limit,
858 * you _don't_ just get the call to `write' returning with a
859 * failure code. Rather, you get a signal called `SIGXFSZ' which,
860 * if neither handled nor ignored, will cause your program to crash
861 * with a core dump.
863 signal(SIGXFSZ, SIG_IGN);
864 #endif /* SIGXFSZ */
866 #ifdef SIGPIPE
867 signal(SIGPIPE, SIG_IGN);
868 #endif
870 if (!ShellProg)
872 register char *sh;
874 sh = getenv("SHELL");
875 ShellProg = SaveStr(sh ? sh : DefaultShell);
877 ShellArgs[0] = ShellProg;
878 home = getenv("HOME");
879 if (!mflag && !SockMatch)
881 sty = getenv("STY");
882 if (sty && *sty == 0)
883 sty = 0;
886 #ifdef NETHACK
887 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
889 char nethackrc[MAXPATHLEN];
891 if (home && (strlen(home) < (MAXPATHLEN - 20)))
893 sprintf(nethackrc,"%s/.nethackrc", home);
894 nethackflag = !access(nethackrc, F_OK);
897 #endif
899 #ifdef MULTIUSER
900 own_uid = multi_uid = real_uid;
901 if (SockMatch && (sockp = index(SockMatch, '/')))
903 *sockp = 0;
904 multi = SockMatch;
905 SockMatch = sockp + 1;
906 if (*multi)
908 struct passwd *mppp;
909 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
910 Panic(0, "Cannot identify account '%s'.", multi);
911 multi_uid = mppp->pw_uid;
912 multi_home = SaveStr(mppp->pw_dir);
913 if (strlen(multi_home) > MAXPATHLEN - 10)
914 Panic(0, "home directory path too long");
915 # ifdef MULTI
916 /* always fake multi attach mode */
917 if (rflag || lsflag)
918 xflag = 1;
919 # endif /* MULTI */
920 detached = 0;
921 multiattach = 1;
923 /* Special case: effective user is multiuser. */
924 if (eff_uid && (multi_uid != eff_uid))
925 Panic(0, "Must run suid root for multiuser support.");
927 if (SockMatch && *SockMatch == 0)
928 SockMatch = 0;
929 #endif /* MULTIUSER */
931 if ((LoginName = getlogin()) && LoginName[0] != '\0')
933 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
934 if ((int)ppp->pw_uid != real_uid)
935 ppp = (struct passwd *) 0;
937 if (ppp == 0)
939 if ((ppp = getpwuid(real_uid)) == 0)
941 Panic(0, "getpwuid() can't identify your account!");
942 exit(1);
944 LoginName = ppp->pw_name;
946 LoginName = SaveStr(LoginName);
948 ppp = getpwbyname(LoginName, ppp);
950 #if !defined(SOCKDIR) && defined(MULTIUSER)
951 if (multi && !multiattach)
953 if (home && strcmp(home, ppp->pw_dir))
954 Panic(0, "$HOME must match passwd entry for multiuser screens.");
956 #endif
958 #define SET_GUID() do \
960 setgid(real_gid); \
961 setuid(real_uid); \
962 eff_uid = real_uid; \
963 eff_gid = real_gid; \
964 } while (0)
966 #define SET_TTYNAME(fatal) do \
968 if (!(attach_tty = ttyname(0))) \
970 if (fatal) \
971 Panic(0, "Must be connected to a terminal."); \
972 else \
973 attach_tty = ""; \
975 else if (stat(attach_tty, &st)) \
976 Panic(errno, "Cannot access '%s'", attach_tty); \
977 if (strlen(attach_tty) >= MAXPATHLEN) \
978 Panic(0, "TtyName too long - sorry."); \
979 } while (0)
981 if (home == 0 || *home == '\0')
982 home = ppp->pw_dir;
983 if (strlen(LoginName) > 20)
984 Panic(0, "LoginName too long - sorry.");
985 #ifdef MULTIUSER
986 if (multi && strlen(multi) > 20)
987 Panic(0, "Screen owner name too long - sorry.");
988 #endif
989 if (strlen(home) > MAXPATHLEN - 25)
990 Panic(0, "$HOME too long - sorry.");
992 attach_tty = "";
993 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty && !xflag))
995 #ifndef NAMEDPIPE
996 int fl;
997 #endif
999 /* ttyname implies isatty */
1000 SET_TTYNAME(1);
1001 #ifdef MULTIUSER
1002 tty_mode = (int)st.st_mode & 0777;
1003 #endif
1005 #ifndef NAMEDPIPE
1006 fl = fcntl(0, F_GETFL, 0);
1007 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1008 attach_fd = 0;
1009 #endif
1010 if (attach_fd == -1)
1012 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1013 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1014 close(n);
1016 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1018 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1019 Panic(0, "Please set a terminal type.");
1020 if (strlen(attach_term) > sizeof(D_termname) - 1)
1021 Panic(0, "$TERM too long - sorry.");
1022 GetTTY(0, &attach_Mode);
1023 #ifdef DEBUGGGGGGGGGGGGGGG
1024 DebugTTY(&attach_Mode);
1025 #endif /* DEBUG */
1028 #ifdef _MODE_T
1029 oumask = umask(0); /* well, unsigned never fails? jw. */
1030 #else
1031 if ((oumask = (int)umask(0)) == -1)
1032 Panic(errno, "Cannot change umask to zero");
1033 #endif
1034 SockDir = getenv("SCREENDIR");
1035 if (SockDir)
1037 if (strlen(SockDir) >= MAXPATHLEN - 1)
1038 Panic(0, "Ridiculously long $SCREENDIR - try again.");
1039 #ifdef MULTIUSER
1040 if (multi)
1041 Panic(0, "No $SCREENDIR with multi screens, please.");
1042 #endif
1044 #ifdef MULTIUSER
1045 if (multiattach)
1047 # ifndef SOCKDIR
1048 sprintf(SockPath, "%s/.screen", multi_home);
1049 SockDir = SockPath;
1050 # else
1051 SockDir = SOCKDIR;
1052 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1053 # endif
1055 else
1056 #endif
1058 #ifndef SOCKDIR
1059 if (SockDir == 0)
1061 sprintf(SockPath, "%s/.screen", home);
1062 SockDir = SockPath;
1064 #endif
1065 if (SockDir)
1067 if (access(SockDir, F_OK))
1069 debug1("SockDir '%s' missing ...\n", SockDir);
1070 if (UserContext() > 0)
1072 if (mkdir(SockDir, 0700))
1073 UserReturn(0);
1074 UserReturn(1);
1076 if (UserStatus() <= 0)
1077 Panic(0, "Cannot make directory '%s'.", SockDir);
1079 if (SockDir != SockPath)
1080 strcpy(SockPath, SockDir);
1082 #ifdef SOCKDIR
1083 else
1085 SockDir = SOCKDIR;
1086 if (stat(SockDir, &st))
1088 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1089 (eff_gid != real_gid) ? 0775 :
1090 #ifdef S_ISVTX
1091 0777|S_ISVTX;
1092 #else
1093 0777;
1094 #endif
1095 if (mkdir(SockDir, n) == -1)
1096 Panic(errno, "Cannot make directory '%s'", SockDir);
1098 else
1100 if (!S_ISDIR(st.st_mode))
1101 Panic(0, "'%s' must be a directory.", SockDir);
1102 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1103 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1104 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1105 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1106 0777;
1107 if (((int)st.st_mode & 0777) != n)
1108 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1110 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1111 if (access(SockPath, F_OK))
1113 if (mkdir(SockPath, 0700) == -1)
1114 Panic(errno, "Cannot make directory '%s'", SockPath);
1115 (void) chown(SockPath, real_uid, real_gid);
1118 #endif
1121 if (stat(SockPath, &st) == -1)
1122 Panic(errno, "Cannot access %s", SockPath);
1123 else
1124 if (!S_ISDIR(st.st_mode))
1125 Panic(0, "%s is not a directory.", SockPath);
1126 #ifdef MULTIUSER
1127 if (multi)
1129 if ((int)st.st_uid != multi_uid)
1130 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1132 else
1133 #endif
1135 if ((int)st.st_uid != real_uid)
1136 Panic(0, "You are not the owner of %s.", SockPath);
1138 if ((st.st_mode & 0777) != 0700)
1139 Panic(0, "Directory %s must have mode 700.", SockPath);
1140 if (SockMatch && index(SockMatch, '/'))
1141 Panic(0, "Bad session name '%s'", SockMatch);
1142 SockName = SockPath + strlen(SockPath) + 1;
1143 *SockName = 0;
1144 (void) umask(oumask);
1145 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1147 #if defined(SYSV) && !defined(ISC)
1148 if (uname(&utsnam) == -1)
1149 Panic(errno, "uname");
1150 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1151 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1152 #else
1153 (void) gethostname(HostName, MAXSTR);
1154 HostName[MAXSTR - 1] = '\0';
1155 #endif
1156 if ((ap = index(HostName, '.')) != NULL)
1157 *ap = '\0';
1159 if (lsflag)
1161 int i, fo, oth;
1163 #ifdef MULTIUSER
1164 if (multi)
1165 real_uid = multi_uid;
1166 #endif
1167 SET_GUID();
1168 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1169 if (quietflag)
1170 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1171 if (fo == 0)
1172 Panic(0, "No Sockets found in %s.\n", SockPath);
1173 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1174 /* NOTREACHED */
1176 signal(SIG_BYE, AttacherFinit); /* prevent races */
1177 if (cmdflag)
1179 /* attach_tty is not mandatory */
1180 SET_TTYNAME(0);
1181 if (!*av)
1182 Panic(0, "Please specify a command.");
1183 SET_GUID();
1184 SendCmdMessage(sty, SockMatch, av, queryflag);
1185 exit(0);
1187 else if (rflag || xflag)
1189 debug("screen -r: - is there anybody out there?\n");
1190 if (Attach(MSG_ATTACH))
1192 Attacher();
1193 /* NOTREACHED */
1195 #ifdef MULTIUSER
1196 if (multiattach)
1197 Panic(0, "Can't create sessions of other users.");
1198 #endif
1199 debug("screen -r: backend not responding -- still crying\n");
1201 else if (dflag && !mflag)
1203 SET_TTYNAME(0);
1204 Attach(MSG_DETACH);
1205 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1206 eexit(0);
1207 /* NOTREACHED */
1209 if (!SockMatch && !mflag && sty)
1211 /* attach_tty is not mandatory */
1212 SET_TTYNAME(0);
1213 SET_GUID();
1214 nwin_options.args = av;
1215 SendCreateMsg(sty, &nwin);
1216 exit(0);
1217 /* NOTREACHED */
1219 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1221 if (!detached || dflag != 2)
1222 MasterPid = fork();
1223 else
1224 MasterPid = 0;
1226 switch (MasterPid)
1228 case -1:
1229 Panic(errno, "fork");
1230 /* NOTREACHED */
1231 case 0:
1232 break;
1233 default:
1234 if (detached)
1235 exit(0);
1236 if (SockMatch)
1237 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1238 else
1239 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1240 for (ap = socknamebuf; *ap; ap++)
1241 if (*ap == '/')
1242 *ap = '-';
1243 #ifdef NAME_MAX
1244 if (strlen(socknamebuf) > NAME_MAX)
1245 socknamebuf[NAME_MAX] = 0;
1246 #endif
1247 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1248 SET_GUID();
1249 Attacher();
1250 /* NOTREACHED */
1253 if (!detached)
1254 PanicPid = getppid();
1256 if (DefaultEsc == -1)
1257 DefaultEsc = Ctrl('a');
1258 if (DefaultMetaEsc == -1)
1259 DefaultMetaEsc = 'a';
1261 ap = av0 + strlen(av0) - 1;
1262 while (ap >= av0)
1264 if (!strncmp("screen", ap, 6))
1266 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1267 break;
1269 ap--;
1271 if (ap < av0)
1272 *av0 = 'S';
1274 #ifdef DEBUG
1276 char buf[256];
1278 if (dfp && dfp != stderr)
1279 fclose(dfp);
1280 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1281 if ((dfp = fopen(buf, "w")) == NULL)
1282 dfp = stderr;
1283 else
1284 (void) chmod(buf, 0666);
1286 #endif
1287 if (!detached)
1289 if (attach_fd == -1)
1291 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1292 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1294 else
1295 n = dup(attach_fd);
1297 else
1298 n = -1;
1299 freopen("/dev/null", "r", stdin);
1300 freopen("/dev/null", "w", stdout);
1302 #ifdef DEBUG
1303 if (dfp != stderr)
1304 #endif
1305 freopen("/dev/null", "w", stderr);
1306 debug("-- screen.back debug started\n");
1309 * This guarantees that the session owner is listed, even when we
1310 * start detached. From now on we should not refer to 'LoginName'
1311 * any more, use users->u_name instead.
1313 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1314 Panic(0, "Could not create user info");
1315 if (!detached)
1317 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1318 Panic(0, "Could not alloc display");
1319 PanicPid = 0;
1320 #ifdef ENCODINGS
1321 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1322 debug1("D_encoding = %d\n", D_encoding);
1323 #endif
1326 if (SockMatch)
1328 /* user started us with -S option */
1329 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1331 else
1333 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1334 HostName);
1336 for (ap = socknamebuf; *ap; ap++)
1337 if (*ap == '/')
1338 *ap = '-';
1339 #ifdef NAME_MAX
1340 if (strlen(socknamebuf) > NAME_MAX)
1342 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1343 socknamebuf[NAME_MAX] = 0;
1345 #endif
1346 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1348 ServerSocket = MakeServerSocket();
1349 InitKeytab();
1350 #ifdef ETCSCREENRC
1351 # ifdef ALLOW_SYSSCREENRC
1352 if ((ap = getenv("SYSSCREENRC")))
1353 (void)StartRc(ap, 0);
1354 else
1355 # endif
1356 (void)StartRc(ETCSCREENRC, 0);
1357 #endif
1358 (void)StartRc(RcFileName, 0);
1359 # ifdef UTMPOK
1360 # ifndef UTNOKEEP
1361 InitUtmp();
1362 # endif /* UTNOKEEP */
1363 # endif /* UTMPOK */
1364 if (display)
1366 if (InitTermcap(0, 0))
1368 debug("Could not init termcap - exiting\n");
1369 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1370 freetty();
1371 if (D_userpid)
1372 Kill(D_userpid, SIG_BYE);
1373 eexit(1);
1375 MakeDefaultCanvas();
1376 InitTerm(0);
1377 #ifdef UTMPOK
1378 RemoveLoginSlot();
1379 #endif
1381 else
1382 MakeTermcap(1);
1383 #ifdef LOADAV
1384 InitLoadav();
1385 #endif /* LOADAV */
1386 MakeNewEnv();
1387 signal(SIGHUP, SigHup);
1388 signal(SIGINT, FinitHandler);
1389 signal(SIGQUIT, FinitHandler);
1390 signal(SIGTERM, FinitHandler);
1391 #ifdef BSDJOBS
1392 signal(SIGTTIN, SIG_IGN);
1393 signal(SIGTTOU, SIG_IGN);
1394 #endif
1396 if (display)
1398 brktty(D_userfd);
1399 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1400 /* Note: SetMode must be called _before_ FinishRc. */
1401 SetTTY(D_userfd, &D_NewMode);
1402 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1403 Msg(errno, "Warning: NBLOCK fcntl failed");
1405 else
1406 brktty(-1); /* just try */
1407 signal(SIGCHLD, SigChld);
1408 #ifdef ETCSCREENRC
1409 # ifdef ALLOW_SYSSCREENRC
1410 if ((ap = getenv("SYSSCREENRC")))
1411 FinishRc(ap);
1412 else
1413 # endif
1414 FinishRc(ETCSCREENRC);
1415 #endif
1416 FinishRc(RcFileName);
1418 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1419 if (windows == NULL)
1421 debug("We open one default window, as screenrc did not specify one.\n");
1422 if (MakeWindow(&nwin) == -1)
1424 fd_set rfd;
1425 struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
1426 FD_SET(0, &rfd);
1428 Msg(0, "Sorry, could not find a PTY or TTY.");
1429 // allow user to exit early by pressing any key.
1430 select(1, &rfd, NULL, NULL, &tv);
1431 Finit(0);
1432 /* NOTREACHED */
1435 else if (ac) /* Screen was invoked with a command */
1437 MakeWindow(&nwin);
1440 #ifdef HAVE_BRAILLE
1441 StartBraille();
1442 #endif
1444 if (display && default_startup)
1445 display_copyright();
1446 signal(SIGINT, SigInt);
1447 if (rflag && (rflag & 1) == 0 && !quietflag)
1449 Msg(0, "New screen...");
1450 rflag = 0;
1453 serv_read.type = EV_READ;
1454 serv_read.fd = ServerSocket;
1455 serv_read.handler = serv_read_fn;
1456 evenq(&serv_read);
1458 serv_select.pri = -10;
1459 serv_select.type = EV_ALWAYS;
1460 serv_select.handler = serv_select_fn;
1461 evenq(&serv_select);
1463 logflushev.type = EV_TIMEOUT;
1464 logflushev.handler = logflush_fn;
1466 sched();
1467 /* NOTREACHED */
1468 return 0;
1471 void
1472 WindowDied(p, wstat, wstat_valid)
1473 struct win *p;
1474 #ifdef BSDWAIT
1475 union wait wstat;
1476 #else
1477 int wstat;
1478 #endif
1479 int wstat_valid;
1481 int killit = 0;
1483 if (p->w_destroyev.data == (char *)p)
1485 wstat = p->w_exitstatus;
1486 wstat_valid = 1;
1487 evdeq(&p->w_destroyev);
1488 p->w_destroyev.data = 0;
1491 #if defined(BSDJOBS) && !defined(BSDWAIT)
1492 if (!wstat_valid && p->w_pid > 0)
1494 /* EOF on file descriptor. The process is probably also dead.
1495 * try a waitpid */
1496 if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid)
1498 p->w_pid = 0;
1499 wstat_valid = 1;
1502 #endif
1503 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1504 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1505 killit = 1;
1507 if (ZombieKey_destroy && !killit)
1509 char buf[100], *s, reason[100];
1510 time_t now;
1512 if (wstat_valid) {
1513 if (WIFEXITED(wstat))
1514 if (WEXITSTATUS(wstat))
1515 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1516 else
1517 sprintf(reason, "terminated normally");
1518 else if (WIFSIGNALED(wstat))
1519 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1520 #ifdef WCOREDUMP
1521 WCOREDUMP(wstat) ? " (core file generated)" : "");
1522 #else
1523 "");
1524 #endif
1525 } else
1526 sprintf(reason, "detached from window");
1528 (void) time(&now);
1529 s = ctime(&now);
1530 if (s && *s)
1531 s[strlen(s) - 1] = '\0';
1532 debug3("window %d (%s) going into zombie state fd %d",
1533 p->w_number, p->w_title, p->w_ptyfd);
1534 #ifdef UTMPOK
1535 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1537 RemoveUtmp(p);
1538 p->w_slot = 0; /* "detached" */
1540 #endif
1541 CloseDevice(p);
1543 p->w_deadpid = p->w_pid;
1544 p->w_pid = 0;
1545 ResetWindow(p);
1546 /* p->w_y = p->w_bot; */
1547 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1548 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1549 WriteString(p, buf, strlen(buf));
1550 WindowChanged(p, 'f');
1552 else
1553 KillWindow(p);
1554 #ifdef UTMPOK
1555 CarefulUtmp();
1556 #endif
1559 static void
1560 SigChldHandler()
1562 struct stat st;
1563 #ifdef DEBUG
1564 fds();
1565 #endif
1566 while (GotSigChld)
1568 GotSigChld = 0;
1569 DoWait();
1570 #ifdef SYSVSIGS
1571 signal(SIGCHLD, SigChld);
1572 #endif
1574 if (stat(SockPath, &st) == -1)
1576 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1577 if (!RecoverSocket())
1579 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1580 Finit(1);
1582 else
1583 debug1("'%s' reconstructed\n", SockPath);
1585 else
1586 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1589 static sigret_t
1590 SigChld SIGDEFARG
1592 debug("SigChld()\n");
1593 GotSigChld = 1;
1594 SIGRETURN;
1597 sigret_t
1598 SigHup SIGDEFARG
1600 /* Hangup all displays */
1601 while ((display = displays) != 0)
1602 Hangup();
1603 SIGRETURN;
1607 * the backend's Interrupt handler
1608 * we cannot insert the intrc directly, as we never know
1609 * if fore is valid.
1611 static sigret_t
1612 SigInt SIGDEFARG
1614 #if HAZARDOUS
1615 char ibuf;
1617 debug("SigInt()\n");
1618 if (fore && displays)
1620 # if defined(TERMIO) || defined(POSIX)
1621 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1622 # else
1623 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1624 # endif
1625 fore->w_inlen = 0;
1626 write(fore->w_ptyfd, &ibuf, 1);
1628 #else
1629 signal(SIGINT, SigInt);
1630 debug("SigInt() careful\n");
1631 InterruptPlease = 1;
1632 #endif
1633 SIGRETURN;
1636 static sigret_t
1637 CoreDump SIGDEFARG
1639 struct display *disp;
1640 char buf[80];
1642 #if defined(SYSVSIGS) && defined(SIGHASARG)
1643 signal(sigsig, SIG_IGN);
1644 #endif
1645 setgid(getgid());
1646 setuid(getuid());
1647 unlink("core");
1648 #ifdef SIGHASARG
1649 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1650 #else
1651 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1652 #endif
1653 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1655 #else /* SHADOWPW && !DEBUG */
1656 " (core dumped)"
1657 #endif /* SHADOWPW && !DEBUG */
1659 for (disp = displays; disp; disp = disp->d_next)
1661 fcntl(disp->d_userfd, F_SETFL, 0);
1662 SetTTY(disp->d_userfd, &D_OldMode);
1663 write(disp->d_userfd, buf, strlen(buf));
1664 Kill(disp->d_userpid, SIG_BYE);
1666 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1667 Kill(getpid(), SIGKILL);
1668 eexit(11);
1669 #else /* SHADOWPW && !DEBUG */
1670 abort();
1671 #endif /* SHADOWPW && !DEBUG */
1672 SIGRETURN;
1675 static void
1676 DoWait()
1678 register int pid;
1679 struct win *p, *next;
1680 #ifdef BSDWAIT
1681 union wait wstat;
1682 #else
1683 int wstat;
1684 #endif
1686 #ifdef BSDJOBS
1687 # ifndef BSDWAIT
1688 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1689 # else
1690 # ifdef USE_WAIT2
1692 * From: rouilj@sni-usa.com (John Rouillard)
1693 * note that WUNTRACED is not documented to work, but it is defined in
1694 * /usr/include/sys/wait.h, so it may work
1696 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1697 # else /* USE_WAIT2 */
1698 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1699 # endif /* USE_WAIT2 */
1700 # endif
1701 #else /* BSDJOBS */
1702 while ((pid = wait(&wstat)) < 0)
1703 if (errno != EINTR)
1704 break;
1705 if (pid > 0)
1706 #endif /* BSDJOBS */
1708 for (p = windows; p; p = next)
1710 next = p->w_next;
1711 if ( (p->w_pid && pid == p->w_pid) ||
1712 (p->w_deadpid && pid == p->w_deadpid) )
1714 /* child has ceased to exist */
1715 p->w_pid = 0;
1717 #ifdef BSDJOBS
1718 if (WIFSTOPPED(wstat))
1720 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1721 #ifdef SIGTTIN
1722 if (WSTOPSIG(wstat) == SIGTTIN)
1724 Msg(0, "Suspended (tty input)");
1725 continue;
1727 #endif
1728 #ifdef SIGTTOU
1729 if (WSTOPSIG(wstat) == SIGTTOU)
1731 Msg(0, "Suspended (tty output)");
1732 continue;
1734 #endif
1735 /* Try to restart process */
1736 Msg(0, "Child has been stopped, restarting.");
1737 if (killpg(pid, SIGCONT))
1738 kill(pid, SIGCONT);
1740 else
1741 #endif
1743 /* Screen will detect the window has died when the window's
1744 * file descriptor signals EOF (which it will do when the process in
1745 * the window terminates). So do this in a timeout of 10 seconds.
1746 * (not doing this at all might also work)
1747 * See #27061 for more details.
1749 p->w_destroyev.data = (char *)p;
1750 p->w_exitstatus = wstat;
1751 SetTimeout(&p->w_destroyev, 10 * 1000);
1752 evenq(&p->w_destroyev);
1754 break;
1756 #ifdef PSEUDOS
1757 if (p->w_pwin && pid == p->w_pwin->p_pid)
1759 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1760 FreePseudowin(p);
1761 break;
1763 #endif
1765 if (p == 0)
1767 debug1("pid %d not found - hope that's ok\n", pid);
1773 static sigret_t
1774 FinitHandler SIGDEFARG
1776 #ifdef SIGHASARG
1777 debug1("FinitHandler called, sig %d.\n", sigsig);
1778 #else
1779 debug("FinitHandler called.\n");
1780 #endif
1781 Finit(1);
1782 SIGRETURN;
1785 void
1786 Finit(i)
1787 int i;
1789 signal(SIGCHLD, SIG_DFL);
1790 signal(SIGHUP, SIG_IGN);
1791 debug1("Finit(%d);\n", i);
1792 while (windows)
1794 struct win *p = windows;
1795 windows = windows->w_next;
1796 FreeWindow(p);
1798 if (ServerSocket != -1)
1800 debug1("we unlink(%s)\n", SockPath);
1801 #ifdef USE_SETEUID
1802 xseteuid(real_uid);
1803 xsetegid(real_gid);
1804 #endif
1805 (void) unlink(SockPath);
1806 #ifdef USE_SETEUID
1807 xseteuid(eff_uid);
1808 xsetegid(eff_gid);
1809 #endif
1811 for (display = displays; display; display = display->d_next)
1813 if (D_status)
1814 RemoveStatus();
1815 FinitTerm();
1816 #ifdef UTMPOK
1817 RestoreLoginSlot();
1818 #endif
1819 AddStr("[screen is terminating]\r\n");
1820 Flush(3);
1821 SetTTY(D_userfd, &D_OldMode);
1822 fcntl(D_userfd, F_SETFL, 0);
1823 freetty();
1824 Kill(D_userpid, SIG_BYE);
1827 * we _cannot_ call eexit(i) here,
1828 * instead of playing with the Socket above. Sigh.
1830 exit(i);
1833 void
1834 eexit(e)
1835 int e;
1837 debug("eexit\n");
1838 if (ServerSocket != -1)
1840 debug1("we unlink(%s)\n", SockPath);
1841 setgid(real_gid);
1842 setuid(real_uid);
1843 (void) unlink(SockPath);
1845 exit(e);
1848 void
1849 Hangup()
1851 if (display == 0)
1852 return;
1853 debug1("Hangup %x\n", display);
1854 if (D_userfd >= 0)
1856 close(D_userfd);
1857 D_userfd = -1;
1859 if (auto_detach || displays->d_next)
1860 Detach(D_HANGUP);
1861 else
1862 Finit(0);
1866 * Detach now has the following modes:
1867 *D_DETACH SIG_BYE detach backend and exit attacher
1868 *D_HANGUP SIG_BYE detach backend and exit attacher
1869 *D_STOP SIG_STOP stop attacher (and detach backend)
1870 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1871 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1872 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1873 *D_LOCK SIG_LOCK lock the attacher
1874 * (jw)
1875 * we always remove our utmp slots. (even when "lock" or "stop")
1876 * Note: Take extra care here, we may be called by interrupt!
1878 void
1879 Detach(mode)
1880 int mode;
1882 int sign = 0, pid;
1883 struct canvas *cv;
1884 struct win *p;
1886 if (display == 0)
1887 return;
1889 #define AddStrSock(msg) do { \
1890 if (SockName) \
1892 AddStr("[" msg " from "); \
1893 AddStr(SockName); \
1894 AddStr("]\r\n"); \
1896 else \
1897 AddStr("[" msg "]\r\n"); \
1898 } while (0)
1900 signal(SIGHUP, SIG_IGN);
1901 debug1("Detach(%d)\n", mode);
1902 if (D_status)
1903 RemoveStatus();
1904 FinitTerm();
1905 if (!display)
1906 return;
1907 switch (mode)
1909 case D_HANGUP:
1910 sign = SIG_BYE;
1911 break;
1912 case D_DETACH:
1913 AddStrSock("detached");
1914 sign = SIG_BYE;
1915 break;
1916 #ifdef BSDJOBS
1917 case D_STOP:
1918 sign = SIG_STOP;
1919 break;
1920 #endif
1921 #ifdef REMOTE_DETACH
1922 case D_REMOTE:
1923 AddStrSock("remote detached");
1924 sign = SIG_BYE;
1925 break;
1926 #endif
1927 #ifdef POW_DETACH
1928 case D_POWER:
1929 AddStrSock("power detached");
1930 if (PowDetachString)
1932 AddStr(PowDetachString);
1933 AddStr("\r\n");
1935 sign = SIG_POWER_BYE;
1936 break;
1937 #ifdef REMOTE_DETACH
1938 case D_REMOTE_POWER:
1939 AddStrSock("remote power detached");
1940 if (PowDetachString)
1942 AddStr(PowDetachString);
1943 AddStr("\r\n");
1945 sign = SIG_POWER_BYE;
1946 break;
1947 #endif
1948 #endif
1949 case D_LOCK:
1950 ClearAll();
1951 sign = SIG_LOCK;
1952 /* tell attacher to lock terminal with a lockprg. */
1953 break;
1955 #ifdef UTMPOK
1956 if (displays->d_next == 0)
1958 for (p = windows; p; p = p->w_next)
1960 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1962 RemoveUtmp(p);
1964 * Set the slot to 0 to get the window
1965 * logged in again.
1967 p->w_slot = (slot_t) 0;
1971 if (mode != D_HANGUP)
1972 RestoreLoginSlot();
1973 #endif
1974 if (displays->d_next == 0 && console_window)
1976 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1978 debug("could not release console - killing window\n");
1979 KillWindow(console_window);
1980 display = displays; /* restore display */
1983 if (D_fore)
1985 #ifdef MULTIUSER
1986 ReleaseAutoWritelock(display, D_fore);
1987 #endif
1988 D_user->u_detachwin = D_fore->w_number;
1989 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1991 AutosaveLayout(D_layout);
1992 layout_last = D_layout;
1993 for (cv = D_cvlist; cv; cv = cv->c_next)
1995 p = Layer2Window(cv->c_layer);
1996 SetCanvasWindow(cv, 0);
1997 if (p)
1998 WindowChanged(p, 'u');
2001 pid = D_userpid;
2002 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
2003 FreeDisplay();
2004 if (displays == 0)
2005 /* Flag detached-ness */
2006 (void) chsock();
2008 * tell father what to do. We do that after we
2009 * freed the tty, thus getty feels more comfortable on hpux
2010 * if it was a power detach.
2012 Kill(pid, sign);
2013 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
2014 debug("Detach returns, we are successfully detached.\n");
2015 signal(SIGHUP, SigHup);
2016 #undef AddStrSock
2019 static int
2020 IsSymbol(e, s)
2021 char *e, *s;
2023 register int l;
2025 l = strlen(s);
2026 return strncmp(e, s, l) == 0 && e[l] == '=';
2029 void
2030 MakeNewEnv()
2032 register char **op, **np;
2033 static char stybuf[MAXSTR];
2035 for (op = environ; *op; ++op)
2037 if (NewEnv)
2038 free((char *)NewEnv);
2039 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
2040 if (!NewEnv)
2041 Panic(0, strnomem);
2042 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
2043 *np++ = stybuf; /* NewEnv[0] */
2044 *np++ = Term; /* NewEnv[1] */
2045 np++; /* room for SHELL */
2046 #ifdef TIOCSWINSZ
2047 np += 2; /* room for TERMCAP and WINDOW */
2048 #else
2049 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2050 #endif
2052 for (op = environ; *op; ++op)
2054 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2055 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2056 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2057 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
2059 *np++ = *op;
2061 *np = 0;
2064 void
2065 /*VARARGS2*/
2066 #if defined(USEVARARGS) && defined(__STDC__)
2067 Msg(int err, const char *fmt, VA_DOTS)
2068 #else
2069 Msg(err, fmt, VA_DOTS)
2070 int err;
2071 const char *fmt;
2072 VA_DECL
2073 #endif
2075 VA_LIST(ap)
2076 char buf[MAXPATHLEN*2];
2077 char *p = buf;
2079 VA_START(ap, fmt);
2080 fmt = DoNLS(fmt);
2081 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2082 VA_END(ap);
2083 if (err)
2085 p += strlen(p);
2086 *p++ = ':';
2087 *p++ = ' ';
2088 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2089 buf[sizeof(buf) - 1] = 0;
2091 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2093 if (display && displays)
2094 MakeStatus(buf);
2095 else if (displays)
2097 for (display = displays; display; display = display->d_next)
2098 MakeStatus(buf);
2100 else if (display)
2102 /* no displays but a display - must have forked.
2103 * send message to backend!
2105 char *tty = D_usertty;
2106 struct display *olddisplay = display;
2107 display = 0; /* only send once */
2108 SendErrorMsg(tty, buf);
2109 display = olddisplay;
2111 else
2112 printf("%s\r\n", buf);
2116 * Call FinitTerm for all displays, write a message to each and call eexit();
2118 void
2119 /*VARARGS2*/
2120 #if defined(USEVARARGS) && defined(__STDC__)
2121 Panic(int err, const char *fmt, VA_DOTS)
2122 #else
2123 Panic(err, fmt, VA_DOTS)
2124 int err;
2125 const char *fmt;
2126 VA_DECL
2127 #endif
2129 VA_LIST(ap)
2130 char buf[MAXPATHLEN*2];
2131 char *p = buf;
2133 VA_START(ap, fmt);
2134 fmt = DoNLS(fmt);
2135 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2136 VA_END(ap);
2137 if (err)
2139 p += strlen(p);
2140 *p++ = ':';
2141 *p++ = ' ';
2142 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2143 buf[sizeof(buf) - 1] = 0;
2145 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2146 if (displays == 0 && display == 0)
2148 printf("%s\r\n", buf);
2149 if (PanicPid)
2150 Kill(PanicPid, SIG_BYE);
2152 else if (displays == 0)
2154 /* no displays but a display - must have forked.
2155 * send message to backend!
2157 char *tty = D_usertty;
2158 display = 0;
2159 SendErrorMsg(tty, buf);
2160 sleep(2);
2161 _exit(1);
2163 else
2164 for (display = displays; display; display = display->d_next)
2166 if (D_status)
2167 RemoveStatus();
2168 FinitTerm();
2169 Flush(3);
2170 #ifdef UTMPOK
2171 RestoreLoginSlot();
2172 #endif
2173 SetTTY(D_userfd, &D_OldMode);
2174 fcntl(D_userfd, F_SETFL, 0);
2175 write(D_userfd, buf, strlen(buf));
2176 write(D_userfd, "\n", 1);
2177 freetty();
2178 if (D_userpid)
2179 Kill(D_userpid, SIG_BYE);
2181 #ifdef MULTIUSER
2182 if (tty_oldmode >= 0)
2184 # ifdef USE_SETEUID
2185 if (setuid(own_uid))
2186 xseteuid(own_uid); /* may be a loop. sigh. */
2187 # else
2188 setuid(own_uid);
2189 # endif
2190 debug1("Panic: changing back modes from %s\n", attach_tty);
2191 chmod(attach_tty, tty_oldmode);
2193 #endif
2194 eexit(1);
2199 * '^' is allowed as an escape mechanism for control characters. jw.
2201 * Added time insertion using ideas/code from /\ndy Jones
2202 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2206 #ifndef USE_LOCALE
2207 static const char days[] = "SunMonTueWedThuFriSat";
2208 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2209 #endif
2211 static char winmsg_buf[MAXSTR];
2212 #define MAX_WINMSG_REND 256 /* rendition changes */
2213 static int winmsg_rend[MAX_WINMSG_REND];
2214 static int winmsg_rendpos[MAX_WINMSG_REND];
2215 static int winmsg_numrend;
2217 static char *
2218 pad_expand(buf, p, numpad, padlen)
2219 char *buf;
2220 char *p;
2221 int numpad;
2222 int padlen;
2224 char *pn, *pn2;
2225 int i, r;
2227 padlen = padlen - (p - buf); /* space for rent */
2228 if (padlen < 0)
2229 padlen = 0;
2230 pn2 = pn = p + padlen;
2231 r = winmsg_numrend;
2232 while (p >= buf)
2234 if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
2236 winmsg_rendpos[--r] = pn - buf;
2237 continue;
2239 *pn-- = *p;
2240 if (*p-- == 127)
2242 pn[1] = ' ';
2243 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2244 padlen -= i;
2245 while (i-- > 0)
2246 *pn-- = ' ';
2247 numpad--;
2248 if (r && p - buf == winmsg_rendpos[r - 1])
2249 winmsg_rendpos[--r] = pn - buf;
2252 return pn2;
2255 struct backtick {
2256 struct backtick *next;
2257 int num;
2258 int tick;
2259 int lifespan;
2260 time_t bestbefore;
2261 char result[MAXSTR];
2262 char **cmdv;
2263 struct event ev;
2264 char *buf;
2265 int bufi;
2268 struct backtick *backticks;
2270 static void
2271 backtick_filter(bt)
2272 struct backtick *bt;
2274 char *p, *q;
2275 int c;
2277 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2279 if (c == '\t')
2280 c = ' ';
2281 if (c >= ' ' || c == '\005')
2282 *q++ = c;
2284 *q = 0;
2287 static void
2288 backtick_fn(ev, data)
2289 struct event *ev;
2290 char *data;
2292 struct backtick *bt;
2293 int i, j, k, l;
2295 bt = (struct backtick *)data;
2296 debug1("backtick_fn for #%d\n", bt->num);
2297 i = bt->bufi;
2298 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2299 if (l <= 0)
2301 debug1("EOF on backtick #%d\n", bt->num);
2302 evdeq(ev);
2303 close(ev->fd);
2304 ev->fd = -1;
2305 return;
2307 debug1("read %d bytes\n", l);
2308 i += l;
2309 for (j = 0; j < l; j++)
2310 if (bt->buf[i - j - 1] == '\n')
2311 break;
2312 if (j < l)
2314 for (k = i - j - 2; k >= 0; k--)
2315 if (bt->buf[k] == '\n')
2316 break;
2317 k++;
2318 bcopy(bt->buf + k, bt->result, i - j - k);
2319 bt->result[i - j - k - 1] = 0;
2320 backtick_filter(bt);
2321 WindowChanged(0, '`');
2323 if (j == l && i == MAXSTR)
2325 j = MAXSTR/2;
2326 l = j + 1;
2328 if (j < l)
2330 if (j)
2331 bcopy(bt->buf + i - j, bt->buf, j);
2332 i = j;
2334 bt->bufi = i;
2337 void
2338 setbacktick(num, lifespan, tick, cmdv)
2339 int num;
2340 int lifespan;
2341 int tick;
2342 char **cmdv;
2344 struct backtick **btp, *bt;
2345 char **v;
2347 debug1("setbacktick called for backtick #%d\n", num);
2348 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2349 if (bt->num == num)
2350 break;
2351 if (!bt && !cmdv)
2352 return;
2353 if (bt)
2355 for (v = bt->cmdv; *v; v++)
2356 free(*v);
2357 free(bt->cmdv);
2358 if (bt->buf)
2359 free(bt->buf);
2360 if (bt->ev.fd >= 0)
2361 close(bt->ev.fd);
2362 evdeq(&bt->ev);
2364 if (bt && !cmdv)
2366 *btp = bt->next;
2367 free(bt);
2368 return;
2370 if (!bt)
2372 bt = (struct backtick *)malloc(sizeof *bt);
2373 if (!bt)
2375 Msg(0, strnomem);
2376 return;
2378 bzero(bt, sizeof(*bt));
2379 bt->next = 0;
2380 *btp = bt;
2382 bt->num = num;
2383 bt->tick = tick;
2384 bt->lifespan = lifespan;
2385 bt->bestbefore = 0;
2386 bt->result[0] = 0;
2387 bt->buf = 0;
2388 bt->bufi = 0;
2389 bt->cmdv = cmdv;
2390 bt->ev.fd = -1;
2391 if (bt->tick == 0 && bt->lifespan == 0)
2393 debug("setbacktick: continuous mode\n");
2394 bt->buf = (char *)malloc(MAXSTR);
2395 if (bt->buf == 0)
2397 Msg(0, strnomem);
2398 setbacktick(num, 0, 0, (char **)0);
2399 return;
2401 bt->ev.type = EV_READ;
2402 bt->ev.fd = readpipe(bt->cmdv);
2403 bt->ev.handler = backtick_fn;
2404 bt->ev.data = (char *)bt;
2405 if (bt->ev.fd >= 0)
2406 evenq(&bt->ev);
2410 static char *
2411 runbacktick(bt, tickp, now)
2412 struct backtick *bt;
2413 int *tickp;
2414 time_t now;
2416 int f, i, l, j;
2417 time_t now2;
2419 debug1("runbacktick called for backtick #%d\n", bt->num);
2420 if (bt->tick && (!*tickp || bt->tick < *tickp))
2421 *tickp = bt->tick;
2422 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2424 debug1("returning old result (%d)\n", bt->lifespan);
2425 return bt->result;
2427 f = readpipe(bt->cmdv);
2428 if (f == -1)
2429 return bt->result;
2430 i = 0;
2431 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2433 debug1("runbacktick: read %d bytes\n", l);
2434 i += l;
2435 for (j = 1; j < l; j++)
2436 if (bt->result[i - j - 1] == '\n')
2437 break;
2438 if (j == l && i == sizeof(bt->result))
2440 j = sizeof(bt->result) / 2;
2441 l = j + 1;
2443 if (j < l)
2445 bcopy(bt->result + i - j, bt->result, j);
2446 i = j;
2449 close(f);
2450 bt->result[sizeof(bt->result) - 1] = '\n';
2451 if (i && bt->result[i - 1] == '\n')
2452 i--;
2453 debug1("runbacktick: finished, %d bytes\n", i);
2454 bt->result[i] = 0;
2455 backtick_filter(bt);
2456 (void)time(&now2);
2457 bt->bestbefore = now2 + bt->lifespan;
2458 return bt->result;
2462 AddWinMsgRend(str, r)
2463 const char *str;
2464 int r;
2466 if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf ||
2467 str >= winmsg_buf + MAXSTR)
2468 return -1;
2470 winmsg_rend[winmsg_numrend] = r;
2471 winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
2472 winmsg_numrend++;
2474 return 0;
2477 char *
2478 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2479 char *str;
2480 struct win *win;
2481 int esc;
2482 int padlen;
2483 struct event *ev;
2484 int rec;
2486 static int tick;
2487 char *s = str;
2488 register char *p = winmsg_buf;
2489 register int ctrl;
2490 struct timeval now;
2491 struct tm *tm;
2492 int l, i, r;
2493 int num;
2494 int zeroflg;
2495 int longflg;
2496 int minusflg;
2497 int plusflg;
2498 int qmflag = 0, omflag = 0, qmnumrend = 0;
2499 char *qmpos = 0;
2500 int numpad = 0;
2501 int lastpad = 0;
2502 int truncpos = -1;
2503 int truncper = 0;
2504 int trunclong = 0;
2505 struct backtick *bt;
2507 if (winmsg_numrend >= 0)
2508 winmsg_numrend = 0;
2509 else
2510 winmsg_numrend = -winmsg_numrend;
2512 tick = 0;
2513 tm = 0;
2514 ctrl = 0;
2515 gettimeofday(&now, NULL);
2516 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2518 *p = *s;
2519 if (ctrl)
2521 ctrl = 0;
2522 if (*s != '^' && *s >= 64)
2523 *p &= 0x1f;
2524 continue;
2526 if (*s != esc)
2528 if (esc == '%')
2530 switch (*s)
2532 #if 0
2533 case '~':
2534 *p = BELL;
2535 break;
2536 #endif
2537 case '^':
2538 ctrl = 1;
2539 *p-- = '^';
2540 break;
2541 default:
2542 break;
2545 continue;
2547 if (*++s == esc) /* double escape ? */
2548 continue;
2549 if ((plusflg = *s == '+') != 0)
2550 s++;
2551 if ((minusflg = *s == '-') != 0)
2552 s++;
2553 if ((zeroflg = *s == '0') != 0)
2554 s++;
2555 num = 0;
2556 while(*s >= '0' && *s <= '9')
2557 num = num * 10 + (*s++ - '0');
2558 if ((longflg = *s == 'L') != 0)
2559 s++;
2560 switch (*s)
2562 case '?':
2563 p--;
2564 if (qmpos)
2566 if ((!qmflag && !omflag) || omflag == 1)
2568 p = qmpos;
2569 if (qmnumrend < winmsg_numrend)
2570 winmsg_numrend = qmnumrend;
2572 qmpos = 0;
2573 break;
2575 qmpos = p;
2576 qmnumrend = winmsg_numrend;
2577 qmflag = omflag = 0;
2578 break;
2579 case ':':
2580 p--;
2581 if (!qmpos)
2582 break;
2583 if (qmflag && omflag != 1)
2585 omflag = 1;
2586 qmpos = p;
2587 qmnumrend = winmsg_numrend;
2589 else
2591 p = qmpos;
2592 if (qmnumrend < winmsg_numrend)
2593 winmsg_numrend = qmnumrend;
2594 omflag = -1;
2596 break;
2597 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2598 case 'a': case 'A': case 's': case 'c': case 'C':
2599 if (l < 4)
2600 break;
2601 if (tm == 0)
2603 time_t nowsec = now.tv_sec;
2604 tm = localtime(&nowsec);
2606 qmflag = 1;
2607 if (!tick || tick > 3600)
2608 tick = 3600;
2609 switch (*s)
2611 case 'd':
2612 sprintf(p, "%02d", tm->tm_mday % 100);
2613 break;
2614 case 'D':
2615 #ifdef USE_LOCALE
2616 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2617 #else
2618 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2619 #endif
2620 break;
2621 case 'm':
2622 sprintf(p, "%02d", tm->tm_mon + 1);
2623 break;
2624 case 'M':
2625 #ifdef USE_LOCALE
2626 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2627 #else
2628 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2629 #endif
2630 break;
2631 case 'y':
2632 sprintf(p, "%02d", tm->tm_year % 100);
2633 break;
2634 case 'Y':
2635 sprintf(p, "%04d", tm->tm_year + 1900);
2636 break;
2637 case 'a':
2638 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2639 break;
2640 case 'A':
2641 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2642 break;
2643 case 's':
2644 sprintf(p, "%02d", tm->tm_sec);
2645 tick = 1;
2646 break;
2647 case 'c':
2648 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2649 if (!tick || tick > 60)
2650 tick = 60;
2651 break;
2652 case 'C':
2653 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2654 if (!tick || tick > 60)
2655 tick = 60;
2656 break;
2657 default:
2658 break;
2660 p += strlen(p) - 1;
2661 break;
2662 case 'l':
2663 #ifdef LOADAV
2664 *p = 0;
2665 if (l > 20)
2666 AddLoadav(p);
2667 if (*p)
2669 qmflag = 1;
2670 p += strlen(p) - 1;
2672 else
2673 *p = '?';
2674 if (!tick || tick > 60)
2675 tick = 60;
2676 #else
2677 *p = '?';
2678 #endif
2679 p += strlen(p) - 1;
2680 break;
2681 case '`':
2682 case 'h':
2683 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2685 p--;
2686 break;
2688 if (*s == '`')
2690 for (bt = backticks; bt; bt = bt->next)
2691 if (bt->num == num)
2692 break;
2693 if (bt == 0)
2695 p--;
2696 break;
2700 char savebuf[sizeof(winmsg_buf)];
2701 int oldtick = tick;
2702 int oldnumrend = winmsg_numrend;
2704 *p = 0;
2705 strcpy(savebuf, winmsg_buf);
2706 winmsg_numrend = -winmsg_numrend;
2707 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2708 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2709 if (!tick || oldtick < tick)
2710 tick = oldtick;
2711 if ((int)strlen(winmsg_buf) < l)
2712 strcat(savebuf, winmsg_buf);
2713 strcpy(winmsg_buf, savebuf);
2714 while (oldnumrend < winmsg_numrend)
2715 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2716 if (*p)
2717 qmflag = 1;
2718 p += strlen(p) - 1;
2720 break;
2721 case 'w':
2722 case 'W':
2724 struct win *oldfore = 0;
2725 char *ss;
2727 if (display)
2729 oldfore = D_fore;
2730 D_fore = win;
2732 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1);
2733 if (display)
2734 D_fore = oldfore;
2736 if (*p)
2737 qmflag = 1;
2738 p += strlen(p) - 1;
2739 break;
2740 case 'u':
2741 *p = 0;
2742 if (win)
2743 AddOtherUsers(p, l - 1, win);
2744 if (*p)
2745 qmflag = 1;
2746 p += strlen(p) - 1;
2747 break;
2748 case 'f':
2749 *p = 0;
2750 if (win)
2751 AddWindowFlags(p, l - 1, win);
2752 if (*p)
2753 qmflag = 1;
2754 p += strlen(p) - 1;
2755 break;
2756 case 't':
2757 *p = 0;
2758 if (win && (int)strlen(win->w_title) < l)
2760 strcpy(p, win->w_title);
2761 if (*p)
2762 qmflag = 1;
2764 p += strlen(p) - 1;
2765 break;
2766 case '{':
2768 char rbuf[128];
2769 s++;
2770 for (i = 0; i < 127; i++)
2771 if (s[i] && s[i] != '}')
2772 rbuf[i] = s[i];
2773 else
2774 break;
2775 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2777 r = -1;
2778 rbuf[i] = 0;
2779 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2780 if (i != 1 || rbuf[0] != '-')
2781 r = ParseAttrColor(rbuf, (char *)0, 0);
2782 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2784 winmsg_rend[winmsg_numrend] = r;
2785 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2786 winmsg_numrend++;
2789 s += i;
2790 p--;
2792 break;
2793 case 'H':
2794 *p = 0;
2795 if ((int)strlen(HostName) < l)
2797 strcpy(p, HostName);
2798 if (*p)
2799 qmflag = 1;
2801 p += strlen(p) - 1;
2802 break;
2803 case 'S':
2805 char *session_name;
2806 *p = 0;
2807 session_name = strchr(SockName, '.') + 1;
2808 if ((int)strlen(session_name) < l)
2810 strcpy(p, session_name);
2811 if (*p)
2812 qmflag = 1;
2814 p += strlen(p) - 1;
2816 break;
2817 case 'p':
2819 sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2820 p += strlen(p) - 1;
2822 break;
2823 case 'F':
2824 p--;
2825 /* small hack */
2826 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2827 minusflg = !minusflg;
2828 if (minusflg)
2829 qmflag = 1;
2830 break;
2831 case 'P':
2832 p--;
2833 if (display && ev && ev != &D_hstatusev) /* Hack */
2835 /* Is the layer in the current canvas in copy mode? */
2836 struct canvas *cv = (struct canvas *)ev->data;
2837 if (ev == &cv->c_captev && cv->c_layer->l_layfn == &MarkLf)
2838 qmflag = 1;
2840 break;
2841 case '>':
2842 truncpos = p - winmsg_buf;
2843 truncper = num > 100 ? 100 : num;
2844 trunclong = longflg;
2845 p--;
2846 break;
2847 case '=':
2848 case '<':
2849 *p = ' ';
2850 if (num || zeroflg || plusflg || longflg || (*s != '='))
2852 /* expand all pads */
2853 if (minusflg)
2855 num = (plusflg ? lastpad : padlen) - num;
2856 if (!plusflg && padlen == 0)
2857 num = p - winmsg_buf;
2858 plusflg = 0;
2860 else if (!zeroflg)
2862 if (*s != '=' && num == 0 && !plusflg)
2863 num = 100;
2864 if (num > 100)
2865 num = 100;
2866 if (padlen == 0)
2867 num = p - winmsg_buf;
2868 else
2869 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2871 if (num < 0)
2872 num = 0;
2873 if (plusflg)
2874 num += lastpad;
2875 if (num > MAXSTR - 1)
2876 num = MAXSTR - 1;
2877 if (numpad)
2878 p = pad_expand(winmsg_buf, p, numpad, num);
2879 numpad = 0;
2880 if (p - winmsg_buf > num && !longflg)
2882 int left, trunc;
2884 if (truncpos == -1)
2886 truncpos = lastpad;
2887 truncper = 0;
2889 trunc = lastpad + truncper * (num - lastpad) / 100;
2890 if (trunc > num)
2891 trunc = num;
2892 if (trunc < lastpad)
2893 trunc = lastpad;
2894 left = truncpos - trunc;
2895 if (left > p - winmsg_buf - num)
2896 left = p - winmsg_buf - num;
2897 debug1("lastpad = %d, ", lastpad);
2898 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2899 if (left > 0)
2901 if (left + lastpad > p - winmsg_buf)
2902 left = p - winmsg_buf - lastpad;
2903 if (p - winmsg_buf - lastpad - left > 0)
2904 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2905 p -= left;
2906 r = winmsg_numrend;
2907 while (r && winmsg_rendpos[r - 1] > lastpad)
2909 r--;
2910 winmsg_rendpos[r] -= left;
2911 if (winmsg_rendpos[r] < lastpad)
2912 winmsg_rendpos[r] = lastpad;
2914 if (trunclong)
2916 if (p - winmsg_buf > lastpad)
2917 winmsg_buf[lastpad] = '.';
2918 if (p - winmsg_buf > lastpad + 1)
2919 winmsg_buf[lastpad + 1] = '.';
2920 if (p - winmsg_buf > lastpad + 2)
2921 winmsg_buf[lastpad + 2] = '.';
2924 if (p - winmsg_buf > num)
2926 p = winmsg_buf + num;
2927 if (trunclong)
2929 if (num - 1 >= lastpad)
2930 p[-1] = '.';
2931 if (num - 2 >= lastpad)
2932 p[-2] = '.';
2933 if (num - 3 >= lastpad)
2934 p[-3] = '.';
2936 r = winmsg_numrend;
2937 while (r && winmsg_rendpos[r - 1] > num)
2938 winmsg_rendpos[--r] = num;
2940 truncpos = -1;
2941 trunclong = 0;
2942 if (lastpad > p - winmsg_buf)
2943 lastpad = p - winmsg_buf;
2944 debug1("lastpad now %d\n", lastpad);
2946 if (*s == '=')
2948 while (p - winmsg_buf < num)
2949 *p++ = ' ';
2950 lastpad = p - winmsg_buf;
2951 truncpos = -1;
2952 trunclong = 0;
2953 debug1("lastpad2 now %d\n", lastpad);
2955 p--;
2957 else if (padlen)
2959 *p = 127; /* internal pad representation */
2960 numpad++;
2962 break;
2963 case 'n':
2964 s++;
2965 /* FALLTHROUGH */
2966 default:
2967 s--;
2968 if (l > 10 + num)
2970 if (num == 0)
2971 num = 1;
2972 if (!win)
2973 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2974 else
2975 sprintf(p, "%*d", num, win->w_number);
2976 qmflag = 1;
2977 p += strlen(p) - 1;
2979 break;
2982 if (qmpos && !qmflag)
2983 p = qmpos + 1;
2984 *p = '\0';
2985 if (numpad)
2987 if (padlen > MAXSTR - 1)
2988 padlen = MAXSTR - 1;
2989 p = pad_expand(winmsg_buf, p, numpad, padlen);
2991 if (ev)
2993 evdeq(ev); /* just in case */
2994 ev->timeout.tv_sec = 0;
2995 ev->timeout.tv_usec = 0;
2997 if (ev && tick)
2999 now.tv_usec = 100000;
3000 if (tick == 1)
3001 now.tv_sec++;
3002 else
3003 now.tv_sec += tick - (now.tv_sec % tick);
3004 ev->timeout = now;
3005 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
3007 return winmsg_buf;
3010 char *
3011 MakeWinMsg(s, win, esc)
3012 char *s;
3013 struct win *win;
3014 int esc;
3016 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
3019 void
3020 PutWinMsg(s, start, max)
3021 char *s;
3022 int start, max;
3024 int i, p, l, r, n;
3025 struct mchar rend;
3026 struct mchar rendstack[MAX_WINMSG_REND];
3027 int rendstackn = 0;
3029 if (s != winmsg_buf)
3031 /* sorry, no fancy coloring available */
3032 debug1("PutWinMsg %s plain\n", s);
3033 l = strlen(s);
3034 if (l > max)
3035 l = max;
3036 l -= start;
3037 s += start;
3038 while (l-- > 0)
3039 PUTCHARLP(*s++);
3040 return;
3042 rend = D_rend;
3043 p = 0;
3044 l = strlen(s);
3045 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3046 for (i = 0; i < winmsg_numrend && max > 0; i++)
3048 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3049 break;
3050 if (p < winmsg_rendpos[i])
3052 n = winmsg_rendpos[i] - p;
3053 if (n > max)
3054 n = max;
3055 max -= n;
3056 p += n;
3057 while(n-- > 0)
3059 if (start-- > 0)
3060 s++;
3061 else
3062 PUTCHARLP(*s++);
3065 r = winmsg_rend[i];
3066 if (r == -1)
3068 if (rendstackn > 0)
3069 rend = rendstack[--rendstackn];
3071 else
3073 rendstack[rendstackn++] = rend;
3074 ApplyAttrColor(r, &rend);
3076 SetRendition(&rend);
3078 if (p < l)
3080 n = l - p;
3081 if (n > max)
3082 n = max;
3083 while(n-- > 0)
3085 if (start-- > 0)
3086 s++;
3087 else
3088 PUTCHARLP(*s++);
3094 #ifdef DEBUG
3095 static void
3096 fds1(i, j)
3097 int i, j;
3099 while (i < j)
3101 debug1("%d ", i);
3102 i++;
3104 if ((j = open("/dev/null", 0)) >= 0)
3106 fds1(i + 1, j);
3107 close(j);
3109 else
3111 while (dup(++i) < 0 && errno != EBADF)
3112 debug1("%d ", i);
3113 debug1(" [%d]\n", i);
3117 static void
3118 fds()
3120 debug("fds: ");
3121 fds1(-1, -1);
3123 #endif
3125 static void
3126 serv_read_fn(ev, data)
3127 struct event *ev;
3128 char *data;
3130 debug("Knock - knock!\n");
3131 ReceiveMsg();
3134 static void
3135 serv_select_fn(ev, data)
3136 struct event *ev;
3137 char *data;
3139 struct win *p;
3141 debug("serv_select_fn called\n");
3142 /* XXX: messages?? */
3143 if (GotSigChld)
3145 SigChldHandler();
3147 if (InterruptPlease)
3149 debug("Backend received interrupt\n");
3150 /* This approach is rather questionable in a multi-display
3151 * environment */
3152 if (fore && displays)
3154 #if defined(TERMIO) || defined(POSIX)
3155 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3156 #else
3157 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3158 #endif
3159 #ifdef PSEUDOS
3160 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3161 &ibuf, 1);
3162 debug1("Backend wrote interrupt to %d", fore->w_number);
3163 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3164 #else
3165 write(fore->w_ptyfd, &ibuf, 1);
3166 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3167 #endif
3169 InterruptPlease = 0;
3172 for (p = windows; p; p = p->w_next)
3174 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3176 struct canvas *cv;
3177 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3178 p->w_bell = BELL_ON;
3179 for (display = displays; display; display = display->d_next)
3181 for (cv = D_cvlist; cv; cv = cv->c_next)
3182 if (cv->c_layer->l_bottom == &p->w_layer)
3183 break;
3184 if (cv == 0)
3186 p->w_bell = BELL_DONE;
3187 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3189 else if (visual && !D_VB && (!D_status || !D_status_bell))
3191 Msg(0, "%s", VisualBellString);
3192 if (D_status)
3194 D_status_bell = 1;
3195 debug1("using vbell timeout %d\n", VBellWait);
3196 SetTimeout(&D_statusev, VBellWait );
3200 /* don't annoy the user with two messages */
3201 if (p->w_monitor == MON_FOUND)
3202 p->w_monitor = MON_DONE;
3203 WindowChanged(p, 'f');
3205 if (p->w_monitor == MON_FOUND)
3207 struct canvas *cv;
3208 p->w_monitor = MON_ON;
3209 for (display = displays; display; display = display->d_next)
3211 for (cv = D_cvlist; cv; cv = cv->c_next)
3212 if (cv->c_layer->l_bottom == &p->w_layer)
3213 break;
3214 if (cv)
3215 continue; /* user already sees window */
3216 #ifdef MULTIUSER
3217 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3218 continue; /* user doesn't care */
3219 #endif
3220 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3221 p->w_monitor = MON_DONE;
3223 WindowChanged(p, 'f');
3227 for (display = displays; display; display = display->d_next)
3229 struct canvas *cv;
3230 if (D_status == STATUS_ON_WIN)
3231 continue;
3232 /* XXX: should use display functions! */
3233 for (cv = D_cvlist; cv; cv = cv->c_next)
3235 int lx, ly;
3237 /* normalize window, see resize.c */
3238 lx = cv->c_layer->l_x;
3239 ly = cv->c_layer->l_y;
3240 if (lx == cv->c_layer->l_width)
3241 lx--;
3242 if (ly + cv->c_yoff < cv->c_ys)
3244 int i, n = cv->c_ys - (ly + cv->c_yoff);
3245 cv->c_yoff = cv->c_ys - ly;
3246 RethinkViewportOffsets(cv);
3247 if (n > cv->c_layer->l_height)
3248 n = cv->c_layer->l_height;
3249 CV_CALL(cv,
3250 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3251 LayRedisplayLine(-1, -1, -1, 1);
3252 for (i = 0; i < n; i++)
3253 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3254 if (cv == cv->c_display->d_forecv)
3255 LaySetCursor();
3258 else if (ly + cv->c_yoff > cv->c_ye)
3260 int i, n = ly + cv->c_yoff - cv->c_ye;
3261 cv->c_yoff = cv->c_ye - ly;
3262 RethinkViewportOffsets(cv);
3263 if (n > cv->c_layer->l_height)
3264 n = cv->c_layer->l_height;
3265 CV_CALL(cv,
3266 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3267 LayRedisplayLine(-1, -1, -1, 1);
3268 for (i = 0; i < n; i++)
3269 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3270 if (cv == cv->c_display->d_forecv)
3271 LaySetCursor();
3274 if (lx + cv->c_xoff < cv->c_xs)
3276 int i, n = cv->c_xs - (lx + cv->c_xoff);
3277 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3278 n = (cv->c_xe - cv->c_xs + 1) / 2;
3279 if (cv->c_xoff + n > cv->c_xs)
3280 n = cv->c_xs - cv->c_xoff;
3281 cv->c_xoff += n;
3282 RethinkViewportOffsets(cv);
3283 if (n > cv->c_layer->l_width)
3284 n = cv->c_layer->l_width;
3285 CV_CALL(cv,
3286 LayRedisplayLine(-1, -1, -1, 1);
3287 for (i = 0; i < flayer->l_height; i++)
3289 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3290 LayRedisplayLine(i, 0, n - 1, 1);
3292 if (cv == cv->c_display->d_forecv)
3293 LaySetCursor();
3296 else if (lx + cv->c_xoff > cv->c_xe)
3298 int i, n = lx + cv->c_xoff - cv->c_xe;
3299 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3300 n = (cv->c_xe - cv->c_xs + 1) / 2;
3301 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3302 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3303 cv->c_xoff -= n;
3304 RethinkViewportOffsets(cv);
3305 if (n > cv->c_layer->l_width)
3306 n = cv->c_layer->l_width;
3307 CV_CALL(cv,
3308 LayRedisplayLine(-1, -1, -1, 1);
3309 for (i = 0; i < flayer->l_height; i++)
3311 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3312 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3314 if (cv == cv->c_display->d_forecv)
3315 LaySetCursor();
3321 for (display = displays; display; display = display->d_next)
3323 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3324 continue;
3325 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3326 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3330 static void
3331 logflush_fn(ev, data)
3332 struct event *ev;
3333 char *data;
3335 struct win *p;
3336 char *buf;
3337 int n;
3339 if (!islogfile(NULL))
3340 return; /* no more logfiles */
3341 logfflush(NULL);
3342 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3343 if (n)
3345 SetTimeout(ev, n * 1000);
3346 evenq(ev); /* re-enqueue ourself */
3348 if (!logtstamp_on)
3349 return;
3350 /* write fancy time-stamp */
3351 for (p = windows; p; p = p->w_next)
3353 if (!p->w_log)
3354 continue;
3355 p->w_logsilence += n;
3356 if (p->w_logsilence < logtstamp_after)
3357 continue;
3358 if (p->w_logsilence - n >= logtstamp_after)
3359 continue;
3360 buf = MakeWinMsg(logtstamp_string, p, '%');
3361 logfwrite(p->w_log, buf, strlen(buf));
3366 * Interprets ^?, ^@ and other ^-control-char notation.
3367 * Interprets \ddd octal notation
3369 * The result is placed in *cp, p is advanced behind the parsed expression and
3370 * returned.
3372 static char *
3373 ParseChar(p, cp)
3374 char *p, *cp;
3376 if (*p == 0)
3377 return 0;
3378 if (*p == '^' && p[1])
3380 if (*++p == '?')
3381 *cp = '\177';
3382 else if (*p >= '@')
3383 *cp = Ctrl(*p);
3384 else
3385 return 0;
3386 ++p;
3388 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3390 *cp = 0;
3392 *cp = *cp * 8 + *p - '0';
3393 while (*++p <= '7' && *p >= '0');
3395 else
3396 *cp = *p++;
3397 return p;
3400 static int
3401 ParseEscape(p)
3402 char *p;
3404 unsigned char buf[2];
3406 if (*p == 0)
3407 SetEscape((struct acluser *)0, -1, -1);
3408 else
3410 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3411 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3412 return -1;
3413 SetEscape((struct acluser *)0, buf[0], buf[1]);
3415 return 0;