Allow symlinked SockDir.
[screen-lua.git] / src / screen.c
blob212bee274e5e26024bd4d20bcc00fe68bc5f435e
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 adaptflag;
212 #ifdef MULTIUSER
213 char *multi;
214 char *multi_home;
215 int multi_uid;
216 int own_uid;
217 int multiattach;
218 int tty_mode;
219 int tty_oldmode = -1;
220 #endif
222 char HostName[MAXSTR];
223 int MasterPid, PanicPid;
224 int real_uid, real_gid, eff_uid, eff_gid;
225 int default_startup;
226 int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
227 char *preselect = NULL; /* only used in Attach() */
229 #ifdef UTF8
230 char *screenencodings;
231 #endif
233 #ifdef DW_CHARS
234 int cjkwidth;
235 #endif
237 #ifdef NETHACK
238 int nethackflag = 0;
239 #endif
240 int maxwin = MAXWIN;
243 struct layer *flayer;
244 struct win *fore;
245 struct win *windows;
246 struct win *console_window;
251 * Do this last
253 #include "extern.h"
255 char strnomem[] = "Out of memory.";
258 static int InterruptPlease;
259 static int GotSigChld;
261 static int
262 lf_secreopen(name, wantfd, l)
263 char *name;
264 int wantfd;
265 struct logfile *l;
267 int got_fd;
269 close(wantfd);
270 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
271 lf_move_fd(got_fd, wantfd) < 0)
273 logfclose(l);
274 debug1("lf_secreopen: failed for %s\n", name);
275 return -1;
277 l->st->st_ino = l->st->st_dev = 0;
278 debug2("lf_secreopen: %d = %s\n", wantfd, name);
279 return 0;
282 /********************************************************************/
283 /********************************************************************/
284 /********************************************************************/
287 static struct passwd *
288 getpwbyname(name, ppp)
289 char *name;
290 struct passwd *ppp;
292 int n;
293 #ifdef SHADOWPW
294 struct spwd *sss = NULL;
295 static char *spw = NULL;
296 #endif
298 if (!ppp && !(ppp = getpwnam(name)))
299 return NULL;
301 /* Do password sanity check..., allow ##user for SUN_C2 security */
302 #ifdef SHADOWPW
303 pw_try_again:
304 #endif
305 n = 0;
306 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
307 strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
308 n = 13;
309 for (; n < 13; n++)
311 char c = ppp->pw_passwd[n];
312 if (!(c == '.' || c == '/' || c == '$' ||
313 (c >= '0' && c <= '9') ||
314 (c >= 'a' && c <= 'z') ||
315 (c >= 'A' && c <= 'Z')))
316 break;
319 #ifdef SHADOWPW
320 /* try to determine real password */
321 if (n < 13 && sss == 0)
323 sss = getspnam(ppp->pw_name);
324 if (sss)
326 if (spw)
327 free(spw);
328 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
329 endspent(); /* this should delete all buffers ... */
330 goto pw_try_again;
332 endspent(); /* this should delete all buffers ... */
334 #endif
335 if (n < 13)
336 ppp->pw_passwd = 0;
337 #ifdef linux
338 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
339 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
340 #endif
342 return ppp;
345 static char *
346 locale_name(void)
348 static char *s;
350 if (!s)
352 s = getenv("LC_ALL");
353 if (s == NULL)
354 s = getenv("LC_CTYPE");
355 if (s == NULL)
356 s = getenv("LANG");
358 return s;
362 main(ac, av)
363 int ac;
364 char **av;
366 register int n;
367 char *ap;
368 char *av0;
369 char socknamebuf[2 * MAXSTR];
370 int mflag = 0;
371 char *myname = (ac == 0) ? "screen" : av[0];
372 char *SockDir;
373 struct stat st;
374 #ifdef _MODE_T /* (jw) */
375 mode_t oumask;
376 #else
377 int oumask;
378 #endif
379 #if defined(SYSV) && !defined(ISC)
380 struct utsname utsnam;
381 #endif
382 struct NewWindow nwin;
383 int detached = 0; /* start up detached */
384 #ifdef MULTIUSER
385 char *sockp;
386 #endif
387 char *sty = 0;
389 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
390 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
391 #endif
392 #if defined(sun) && defined(SVR4)
394 /* Solaris' login blocks SIGHUP! This is _very bad_ */
395 sigset_t sset;
396 sigemptyset(&sset);
397 sigprocmask(SIG_SETMASK, &sset, 0);
399 #endif
402 * First, close all unused descriptors
403 * (otherwise, we might have problems with the select() call)
405 closeallfiles(0);
406 #ifdef DEBUG
407 opendebug(1, 0);
408 #endif
409 snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
410 PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
411 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
412 debug2("-- screen debug started %s (%s)\n", *av, version);
413 #ifdef POSIX
414 debug("POSIX\n");
415 #endif
416 #ifdef TERMIO
417 debug("TERMIO\n");
418 #endif
419 #ifdef SYSV
420 debug("SYSV\n");
421 #endif
422 #ifdef SYSVSIGS
423 debug("SYSVSIGS\n");
424 #endif
425 #ifdef NAMEDPIPE
426 debug("NAMEDPIPE\n");
427 #endif
428 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
429 debug("Window size changing enabled\n");
430 #endif
431 #ifdef HAVE_SETREUID
432 debug("SETREUID\n");
433 #endif
434 #ifdef HAVE_SETEUID
435 debug("SETEUID\n");
436 #endif
437 #ifdef hpux
438 debug("hpux\n");
439 #endif
440 #ifdef USEBCOPY
441 debug("USEBCOPY\n");
442 #endif
443 #ifdef UTMPOK
444 debug("UTMPOK\n");
445 #endif
446 #ifdef LOADAV
447 debug("LOADAV\n");
448 #endif
449 #ifdef NETHACK
450 debug("NETHACK\n");
451 #endif
452 #ifdef TERMINFO
453 debug("TERMINFO\n");
454 #endif
455 #ifdef SHADOWPW
456 debug("SHADOWPW\n");
457 #endif
458 #ifdef NAME_MAX
459 debug1("NAME_MAX = %d\n", NAME_MAX);
460 #endif
462 BellString = SaveStr("Bell in window %n");
463 VisualBellString = SaveStr(" Wuff, Wuff!! ");
464 ActivityString = SaveStr("Activity in window %n");
465 screenlogfile = SaveStr("screenlog.%n");
466 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
467 hstatusstring = SaveStr("%h");
468 captionstring = SaveStr("%3n %t");
469 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
470 wlisttit = SaveStr("Num Name%=Flags");
471 wliststr = SaveStr("%3n %t%=%f");
472 #ifdef COPY_PASTE
473 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
474 #endif
475 ShellProg = NULL;
476 #ifdef POW_DETACH
477 PowDetachString = 0;
478 #endif
479 default_startup = (ac > 1) ? 0 : 1;
480 adaptflag = 0;
481 VBellWait = VBELLWAIT * 1000;
482 MsgWait = MSGWAIT * 1000;
483 MsgMinWait = MSGMINWAIT * 1000;
484 SilenceWait = SILENCEWAIT;
485 #ifdef HAVE_BRAILLE
486 InitBraille();
487 #endif
488 #ifdef ZMODEM
489 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
490 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
491 #endif
493 #ifdef COPY_PASTE
494 CompileKeys((char *)0, 0, mark_key_tab);
495 #endif
496 #ifdef UTF8
497 InitBuiltinTabs();
498 screenencodings = SaveStr(SCREENENCODINGS);
499 #endif
500 #ifdef DW_CHARS
501 cjkwidth = 0;
502 #endif
503 nwin = nwin_undef;
504 nwin_options = nwin_undef;
505 strcpy(screenterm, "screen");
507 logreopen_register(lf_secreopen);
509 av0 = *av;
510 /* if this is a login screen, assume -RR */
511 if (*av0 == '-')
513 rflag = 4;
514 #ifdef MULTI
515 xflag = 1;
516 #else
517 dflag = 1;
518 #endif
519 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
521 while (ac > 0)
523 ap = *++av;
524 if (--ac > 0 && *ap == '-')
526 if (ap[1] == '-' && ap[2] == 0)
528 av++;
529 ac--;
530 break;
532 if (ap[1] == '-' && !strcmp(ap, "--version"))
533 Panic(0, "Screen version %s", version);
534 if (ap[1] == '-' && !strcmp(ap, "--help"))
535 exit_with_usage(myname, NULL, NULL);
536 while (ap && *ap && *++ap)
538 switch (*ap)
540 case 'a':
541 nwin_options.aflag = 1;
542 break;
543 case 'A':
544 adaptflag = 1;
545 break;
546 case 'p': /* preselect */
547 if (*++ap)
548 preselect = ap;
549 else
551 if (!--ac)
552 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
553 preselect = *++av;
555 ap = NULL;
556 break;
557 #ifdef HAVE_BRAILLE
558 case 'B':
559 bd.bd_start_braille = 1;
560 break;
561 #endif
562 case 'c':
563 if (*++ap)
564 RcFileName = ap;
565 else
567 if (--ac == 0)
568 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
569 RcFileName = *++av;
571 ap = NULL;
572 break;
573 case 'e':
574 if (!*++ap)
576 if (--ac == 0)
577 exit_with_usage(myname, "Specify command characters with -e", NULL);
578 ap = *++av;
580 if (ParseEscape(ap))
581 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
582 ap = NULL;
583 break;
584 case 'f':
585 ap++;
586 switch (*ap++)
588 case 'n':
589 case '0':
590 nwin_options.flowflag = FLOW_NOW * 0;
591 break;
592 case '\0':
593 ap--;
594 /* FALLTHROUGH */
595 case 'y':
596 case '1':
597 nwin_options.flowflag = FLOW_NOW * 1;
598 break;
599 case 'a':
600 nwin_options.flowflag = FLOW_AUTOFLAG;
601 break;
602 default:
603 exit_with_usage(myname, "Unknown flow option -%s", --ap);
605 break;
606 case 'h':
607 if (--ac == 0)
608 exit_with_usage(myname, NULL, NULL);
609 nwin_options.histheight = atoi(*++av);
610 if (nwin_options.histheight < 0)
611 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
612 break;
613 case 'i':
614 iflag = 1;
615 break;
616 case 't': /* title, the former AkA == -k */
617 if (--ac == 0)
618 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
619 nwin_options.aka = *++av;
620 break;
621 case 'l':
622 ap++;
623 switch (*ap++)
625 case 'n':
626 case '0':
627 nwin_options.lflag = 0;
628 break;
629 case '\0':
630 ap--;
631 /* FALLTHROUGH */
632 case 'y':
633 case '1':
634 nwin_options.lflag = 1;
635 break;
636 case 'a':
637 nwin_options.lflag = 3;
638 break;
639 case 's': /* -ls */
640 case 'i': /* -list */
641 lsflag = 1;
642 if (ac > 1 && !SockMatch)
644 SockMatch = *++av;
645 ac--;
647 ap = NULL;
648 break;
649 default:
650 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
652 break;
653 case 'w':
654 lsflag = 1;
655 wipeflag = 1;
656 if (ac > 1 && !SockMatch)
658 SockMatch = *++av;
659 ac--;
661 break;
662 case 'L':
663 nwin_options.Lflag = 1;
664 break;
665 case 'm':
666 mflag = 1;
667 break;
668 case 'O': /* to be (or not to be?) deleted. jw. */
669 force_vt = 0;
670 break;
671 case 'T':
672 if (--ac == 0)
673 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
674 if (strlen(*++av) < 20)
675 strcpy(screenterm, *av);
676 else
677 Panic(0, "-T: terminal name too long. (max. 20 char)");
678 nwin_options.term = screenterm;
679 break;
680 case 'q':
681 quietflag = 1;
682 break;
683 case 'r':
684 case 'R':
685 #ifdef MULTI
686 case 'x':
687 #endif
688 if (ac > 1 && *av[1] != '-' && !SockMatch)
690 SockMatch = *++av;
691 ac--;
692 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
694 #ifdef MULTI
695 if (*ap == 'x')
696 xflag = 1;
697 #endif
698 if (rflag)
699 rflag = 2;
700 rflag += (*ap == 'R') ? 2 : 1;
701 break;
702 #ifdef REMOTE_DETACH
703 case 'd':
704 dflag = 1;
705 /* FALLTHROUGH */
706 case 'D':
707 if (!dflag)
708 dflag = 2;
709 if (ac == 2)
711 if (*av[1] != '-' && !SockMatch)
713 SockMatch = *++av;
714 ac--;
715 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
718 break;
719 #endif
720 case 's':
721 if (--ac == 0)
722 exit_with_usage(myname, "Specify shell with -s", NULL);
723 if (ShellProg)
724 free(ShellProg);
725 ShellProg = SaveStr(*++av);
726 debug1("ShellProg: '%s'\n", ShellProg);
727 break;
728 case 'S':
729 if (!SockMatch)
731 if (--ac == 0)
732 exit_with_usage(myname, "Specify session-name with -S", NULL);
733 SockMatch = *++av;
735 if (!*SockMatch)
736 exit_with_usage(myname, "Empty session-name?", NULL);
737 break;
738 case 'X':
739 cmdflag = 1;
740 break;
741 case 'v':
742 Panic(0, "Screen version %s", version);
743 /* NOTREACHED */
744 #ifdef UTF8
745 case 'U':
746 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
747 break;
748 #endif
749 default:
750 exit_with_usage(myname, "Unknown option %s", --ap);
754 else
755 break;
758 real_uid = getuid();
759 real_gid = getgid();
760 eff_uid = geteuid();
761 eff_gid = getegid();
762 if (eff_uid != real_uid)
764 /* if running with s-bit, we must install a special signal
765 * handler routine that resets the s-bit, so that we get a
766 * core file anyway.
768 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
769 signal(SIGBUS, CoreDump);
770 #endif /* SIGBUS */
771 signal(SIGSEGV, CoreDump);
774 #ifdef USE_LOCALE
775 setlocale(LC_ALL, "");
776 #endif
777 #ifdef ENCODINGS
778 if (nwin_options.encoding == -1)
780 /* ask locale if we should start in UTF-8 mode */
781 # ifdef HAVE_NL_LANGINFO
782 # ifndef USE_LOCALE
783 setlocale(LC_CTYPE, "");
784 # endif
785 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
786 debug1("locale says encoding = %d\n", nwin_options.encoding);
787 # else
788 # ifdef UTF8
789 char *s;
790 if ((s = locale_name()) && InStr(s, "UTF-8"))
791 nwin_options.encoding = UTF8;
792 # endif
793 debug1("environment says encoding=%d\n", nwin_options.encoding);
794 #endif
796 # ifdef DW_CHARS
798 char *s;
799 if ((s = locale_name()))
801 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
803 cjkwidth = 1;
807 #endif
808 #endif
809 if (nwin_options.aka)
811 #ifdef ENCODINGS
812 if (nwin_options.encoding > 0)
814 size_t len = strlen(nwin_options.aka);
815 size_t newsz;
816 char *newbuf = malloc(3 * len);
817 if (!newbuf)
818 Panic(0, strnomem);
819 newsz = RecodeBuf(nwin_options.aka, len,
820 nwin_options.encoding, 0, newbuf);
821 newbuf[newsz] = '\0';
822 nwin_options.aka = newbuf;
824 else
825 #endif
827 /* If we just use the original value from av,
828 subsequent shelltitle invocations will attempt to free
829 space we don't own... */
830 nwin_options.aka = SaveStr(nwin_options.aka);
834 if (SockMatch && strlen(SockMatch) >= MAXSTR)
835 Panic(0, "Ridiculously long socketname - try again.");
836 if (cmdflag && !rflag && !dflag && !xflag)
837 xflag = 1;
838 if (!cmdflag && dflag && mflag && !(rflag || xflag))
839 detached = 1;
840 nwin = nwin_options;
841 #ifdef ENCODINGS
842 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
843 #endif
844 if (ac)
845 nwin.args = av;
847 /* make the write() calls return -1 on all errors */
848 #ifdef SIGXFSZ
850 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
851 * It appears that in System V Release 4, UNIX, if you are writing
852 * an output file and you exceed the currently set file size limit,
853 * you _don't_ just get the call to `write' returning with a
854 * failure code. Rather, you get a signal called `SIGXFSZ' which,
855 * if neither handled nor ignored, will cause your program to crash
856 * with a core dump.
858 signal(SIGXFSZ, SIG_IGN);
859 #endif /* SIGXFSZ */
861 #ifdef SIGPIPE
862 signal(SIGPIPE, SIG_IGN);
863 #endif
865 if (!ShellProg)
867 register char *sh;
869 sh = getenv("SHELL");
870 ShellProg = SaveStr(sh ? sh : DefaultShell);
872 ShellArgs[0] = ShellProg;
873 home = getenv("HOME");
874 if (!mflag && !SockMatch)
876 sty = getenv("STY");
877 if (sty && *sty == 0)
878 sty = 0;
881 #ifdef NETHACK
882 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
884 char nethackrc[MAXPATHLEN];
886 if (home && (strlen(home) < (MAXPATHLEN - 20)))
888 sprintf(nethackrc,"%s/.nethackrc", home);
889 nethackflag = !access(nethackrc, F_OK);
892 #endif
894 #ifdef MULTIUSER
895 own_uid = multi_uid = real_uid;
896 if (SockMatch && (sockp = index(SockMatch, '/')))
898 *sockp = 0;
899 multi = SockMatch;
900 SockMatch = sockp + 1;
901 if (*multi)
903 struct passwd *mppp;
904 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
905 Panic(0, "Cannot identify account '%s'.", multi);
906 multi_uid = mppp->pw_uid;
907 multi_home = SaveStr(mppp->pw_dir);
908 if (strlen(multi_home) > MAXPATHLEN - 10)
909 Panic(0, "home directory path too long");
910 # ifdef MULTI
911 /* always fake multi attach mode */
912 if (rflag || lsflag)
913 xflag = 1;
914 # endif /* MULTI */
915 detached = 0;
916 multiattach = 1;
918 /* Special case: effective user is multiuser. */
919 if (eff_uid && (multi_uid != eff_uid))
920 Panic(0, "Must run suid root for multiuser support.");
922 if (SockMatch && *SockMatch == 0)
923 SockMatch = 0;
924 #endif /* MULTIUSER */
926 if ((LoginName = getlogin()) && LoginName[0] != '\0')
928 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
929 if ((int)ppp->pw_uid != real_uid)
930 ppp = (struct passwd *) 0;
932 if (ppp == 0)
934 if ((ppp = getpwuid(real_uid)) == 0)
936 Panic(0, "getpwuid() can't identify your account!");
937 exit(1);
939 LoginName = ppp->pw_name;
941 LoginName = SaveStr(LoginName);
943 ppp = getpwbyname(LoginName, ppp);
945 #if !defined(SOCKDIR) && defined(MULTIUSER)
946 if (multi && !multiattach)
948 if (home && strcmp(home, ppp->pw_dir))
949 Panic(0, "$HOME must match passwd entry for multiuser screens.");
951 #endif
953 #define SET_GUID() do \
955 setgid(real_gid); \
956 setuid(real_uid); \
957 eff_uid = real_uid; \
958 eff_gid = real_gid; \
959 } while (0)
961 #define SET_TTYNAME(fatal) do \
963 if (!(attach_tty = ttyname(0))) \
965 if (fatal) \
966 Panic(0, "Must be connected to a terminal."); \
967 else \
968 attach_tty = ""; \
970 else if (stat(attach_tty, &st)) \
971 Panic(errno, "Cannot access '%s'", attach_tty); \
972 if (strlen(attach_tty) >= MAXPATHLEN) \
973 Panic(0, "TtyName too long - sorry."); \
974 } while (0)
976 if (home == 0 || *home == '\0')
977 home = ppp->pw_dir;
978 if (strlen(LoginName) > 20)
979 Panic(0, "LoginName too long - sorry.");
980 #ifdef MULTIUSER
981 if (multi && strlen(multi) > 20)
982 Panic(0, "Screen owner name too long - sorry.");
983 #endif
984 if (strlen(home) > MAXPATHLEN - 25)
985 Panic(0, "$HOME too long - sorry.");
987 attach_tty = "";
988 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty && !xflag))
990 #ifndef NAMEDPIPE
991 int fl;
992 #endif
994 /* ttyname implies isatty */
995 SET_TTYNAME(1);
996 #ifdef MULTIUSER
997 tty_mode = (int)st.st_mode & 0777;
998 #endif
1000 #ifndef NAMEDPIPE
1001 fl = fcntl(0, F_GETFL, 0);
1002 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1003 attach_fd = 0;
1004 #endif
1005 if (attach_fd == -1)
1007 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1008 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1009 close(n);
1011 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1013 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1014 Panic(0, "Please set a terminal type.");
1015 if (strlen(attach_term) > sizeof(D_termname) - 1)
1016 Panic(0, "$TERM too long - sorry.");
1017 GetTTY(0, &attach_Mode);
1018 #ifdef DEBUGGGGGGGGGGGGGGG
1019 DebugTTY(&attach_Mode);
1020 #endif /* DEBUG */
1023 #ifdef _MODE_T
1024 oumask = umask(0); /* well, unsigned never fails? jw. */
1025 #else
1026 if ((oumask = (int)umask(0)) == -1)
1027 Panic(errno, "Cannot change umask to zero");
1028 #endif
1029 SockDir = getenv("SCREENDIR");
1030 if (SockDir)
1032 if (strlen(SockDir) >= MAXPATHLEN - 1)
1033 Panic(0, "Ridiculously long $SCREENDIR - try again.");
1034 #ifdef MULTIUSER
1035 if (multi)
1036 Panic(0, "No $SCREENDIR with multi screens, please.");
1037 #endif
1039 #ifdef MULTIUSER
1040 if (multiattach)
1042 # ifndef SOCKDIR
1043 sprintf(SockPath, "%s/.screen", multi_home);
1044 SockDir = SockPath;
1045 # else
1046 SockDir = SOCKDIR;
1047 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1048 # endif
1050 else
1051 #endif
1053 #ifndef SOCKDIR
1054 if (SockDir == 0)
1056 sprintf(SockPath, "%s/.screen", home);
1057 SockDir = SockPath;
1059 #endif
1060 if (SockDir)
1062 if (access(SockDir, F_OK))
1064 debug1("SockDir '%s' missing ...\n", SockDir);
1065 if (UserContext() > 0)
1067 if (mkdir(SockDir, 0700))
1068 UserReturn(0);
1069 UserReturn(1);
1071 if (UserStatus() <= 0)
1072 Panic(0, "Cannot make directory '%s'.", SockDir);
1074 if (SockDir != SockPath)
1075 strcpy(SockPath, SockDir);
1077 #ifdef SOCKDIR
1078 else
1080 SockDir = SOCKDIR;
1081 if (stat(SockDir, &st))
1083 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1084 (eff_gid != real_gid) ? 0775 :
1085 #ifdef S_ISVTX
1086 0777|S_ISVTX;
1087 #else
1088 0777;
1089 #endif
1090 if (mkdir(SockDir, n) == -1)
1091 Panic(errno, "Cannot make directory '%s'", SockDir);
1093 else
1095 if (!S_ISDIR(st.st_mode))
1096 Panic(0, "'%s' must be a directory.", SockDir);
1097 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1098 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1099 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1100 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1101 0777;
1102 if (((int)st.st_mode & 0777) != n)
1103 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1105 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1106 if (access(SockPath, F_OK))
1108 if (mkdir(SockPath, 0700) == -1)
1109 Panic(errno, "Cannot make directory '%s'", SockPath);
1110 (void) chown(SockPath, real_uid, real_gid);
1113 #endif
1116 if (stat(SockPath, &st) == -1)
1117 Panic(errno, "Cannot access %s", SockPath);
1118 else
1119 if (!S_ISDIR(st.st_mode))
1120 Panic(0, "%s is not a directory.", SockPath);
1121 #ifdef MULTIUSER
1122 if (multi)
1124 if ((int)st.st_uid != multi_uid)
1125 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1127 else
1128 #endif
1130 if ((int)st.st_uid != real_uid)
1131 Panic(0, "You are not the owner of %s.", SockPath);
1133 if ((st.st_mode & 0777) != 0700)
1134 Panic(0, "Directory %s must have mode 700.", SockPath);
1135 if (SockMatch && index(SockMatch, '/'))
1136 Panic(0, "Bad session name '%s'", SockMatch);
1137 SockName = SockPath + strlen(SockPath) + 1;
1138 *SockName = 0;
1139 (void) umask(oumask);
1140 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1142 #if defined(SYSV) && !defined(ISC)
1143 if (uname(&utsnam) == -1)
1144 Panic(errno, "uname");
1145 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1146 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1147 #else
1148 (void) gethostname(HostName, MAXSTR);
1149 HostName[MAXSTR - 1] = '\0';
1150 #endif
1151 if ((ap = index(HostName, '.')) != NULL)
1152 *ap = '\0';
1154 if (lsflag)
1156 int i, fo, oth;
1158 #ifdef MULTIUSER
1159 if (multi)
1160 real_uid = multi_uid;
1161 #endif
1162 SET_GUID();
1163 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1164 if (quietflag)
1165 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1166 if (fo == 0)
1167 Panic(0, "No Sockets found in %s.\n", SockPath);
1168 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1169 /* NOTREACHED */
1171 signal(SIG_BYE, AttacherFinit); /* prevent races */
1172 if (cmdflag)
1174 /* attach_tty is not mandatory */
1175 SET_TTYNAME(0);
1176 if (!*av)
1177 Panic(0, "Please specify a command.");
1178 SET_GUID();
1179 SendCmdMessage(sty, SockMatch, av);
1180 exit(0);
1182 else if (rflag || xflag)
1184 debug("screen -r: - is there anybody out there?\n");
1185 if (Attach(MSG_ATTACH))
1187 Attacher();
1188 /* NOTREACHED */
1190 #ifdef MULTIUSER
1191 if (multiattach)
1192 Panic(0, "Can't create sessions of other users.");
1193 #endif
1194 debug("screen -r: backend not responding -- still crying\n");
1196 else if (dflag && !mflag)
1198 SET_TTYNAME(0);
1199 Attach(MSG_DETACH);
1200 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1201 eexit(0);
1202 /* NOTREACHED */
1204 if (!SockMatch && !mflag && sty)
1206 /* attach_tty is not mandatory */
1207 SET_TTYNAME(0);
1208 SET_GUID();
1209 nwin_options.args = av;
1210 SendCreateMsg(sty, &nwin);
1211 exit(0);
1212 /* NOTREACHED */
1214 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1216 if (!detached || dflag != 2)
1217 MasterPid = fork();
1218 else
1219 MasterPid = 0;
1221 switch (MasterPid)
1223 case -1:
1224 Panic(errno, "fork");
1225 /* NOTREACHED */
1226 case 0:
1227 break;
1228 default:
1229 if (detached)
1230 exit(0);
1231 if (SockMatch)
1232 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1233 else
1234 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1235 for (ap = socknamebuf; *ap; ap++)
1236 if (*ap == '/')
1237 *ap = '-';
1238 #ifdef NAME_MAX
1239 if (strlen(socknamebuf) > NAME_MAX)
1240 socknamebuf[NAME_MAX] = 0;
1241 #endif
1242 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1243 SET_GUID();
1244 Attacher();
1245 /* NOTREACHED */
1248 if (!detached)
1249 PanicPid = getppid();
1251 if (DefaultEsc == -1)
1252 DefaultEsc = Ctrl('a');
1253 if (DefaultMetaEsc == -1)
1254 DefaultMetaEsc = 'a';
1256 ap = av0 + strlen(av0) - 1;
1257 while (ap >= av0)
1259 if (!strncmp("screen", ap, 6))
1261 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1262 break;
1264 ap--;
1266 if (ap < av0)
1267 *av0 = 'S';
1269 #ifdef DEBUG
1271 char buf[256];
1273 if (dfp && dfp != stderr)
1274 fclose(dfp);
1275 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1276 if ((dfp = fopen(buf, "w")) == NULL)
1277 dfp = stderr;
1278 else
1279 (void) chmod(buf, 0666);
1281 #endif
1282 if (!detached)
1284 if (attach_fd == -1)
1286 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1287 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1289 else
1290 n = dup(attach_fd);
1292 else
1293 n = -1;
1294 freopen("/dev/null", "r", stdin);
1295 freopen("/dev/null", "w", stdout);
1297 #ifdef DEBUG
1298 if (dfp != stderr)
1299 #endif
1300 freopen("/dev/null", "w", stderr);
1301 debug("-- screen.back debug started\n");
1304 * This guarantees that the session owner is listed, even when we
1305 * start detached. From now on we should not refer to 'LoginName'
1306 * any more, use users->u_name instead.
1308 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1309 Panic(0, "Could not create user info");
1310 if (!detached)
1312 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1313 Panic(0, "Could not alloc display");
1314 PanicPid = 0;
1315 #ifdef ENCODINGS
1316 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1317 debug1("D_encoding = %d\n", D_encoding);
1318 #endif
1321 if (SockMatch)
1323 /* user started us with -S option */
1324 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1326 else
1328 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1329 HostName);
1331 for (ap = socknamebuf; *ap; ap++)
1332 if (*ap == '/')
1333 *ap = '-';
1334 #ifdef NAME_MAX
1335 if (strlen(socknamebuf) > NAME_MAX)
1337 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1338 socknamebuf[NAME_MAX] = 0;
1340 #endif
1341 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1343 ServerSocket = MakeServerSocket();
1344 InitKeytab();
1345 #ifdef ETCSCREENRC
1346 # ifdef ALLOW_SYSSCREENRC
1347 if ((ap = getenv("SYSSCREENRC")))
1348 (void)StartRc(ap, 0);
1349 else
1350 # endif
1351 (void)StartRc(ETCSCREENRC, 0);
1352 #endif
1353 (void)StartRc(RcFileName, 0);
1354 # ifdef UTMPOK
1355 # ifndef UTNOKEEP
1356 InitUtmp();
1357 # endif /* UTNOKEEP */
1358 # endif /* UTMPOK */
1359 if (display)
1361 if (InitTermcap(0, 0))
1363 debug("Could not init termcap - exiting\n");
1364 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1365 freetty();
1366 if (D_userpid)
1367 Kill(D_userpid, SIG_BYE);
1368 eexit(1);
1370 MakeDefaultCanvas();
1371 InitTerm(0);
1372 #ifdef UTMPOK
1373 RemoveLoginSlot();
1374 #endif
1376 else
1377 MakeTermcap(1);
1378 #ifdef LOADAV
1379 InitLoadav();
1380 #endif /* LOADAV */
1381 MakeNewEnv();
1382 signal(SIGHUP, SigHup);
1383 signal(SIGINT, FinitHandler);
1384 signal(SIGQUIT, FinitHandler);
1385 signal(SIGTERM, FinitHandler);
1386 #ifdef BSDJOBS
1387 signal(SIGTTIN, SIG_IGN);
1388 signal(SIGTTOU, SIG_IGN);
1389 #endif
1391 if (display)
1393 brktty(D_userfd);
1394 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1395 /* Note: SetMode must be called _before_ FinishRc. */
1396 SetTTY(D_userfd, &D_NewMode);
1397 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1398 Msg(errno, "Warning: NBLOCK fcntl failed");
1400 else
1401 brktty(-1); /* just try */
1402 signal(SIGCHLD, SigChld);
1403 #ifdef ETCSCREENRC
1404 # ifdef ALLOW_SYSSCREENRC
1405 if ((ap = getenv("SYSSCREENRC")))
1406 FinishRc(ap);
1407 else
1408 # endif
1409 FinishRc(ETCSCREENRC);
1410 #endif
1411 FinishRc(RcFileName);
1413 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1414 if (windows == NULL)
1416 debug("We open one default window, as screenrc did not specify one.\n");
1417 if (MakeWindow(&nwin) == -1)
1419 fd_set rfd;
1420 struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
1421 FD_SET(0, &rfd);
1423 Msg(0, "Sorry, could not find a PTY or TTY.");
1424 // allow user to exit early by pressing any key.
1425 select(1, &rfd, NULL, NULL, &tv);
1426 Finit(0);
1427 /* NOTREACHED */
1430 else if (ac) /* Screen was invoked with a command */
1432 MakeWindow(&nwin);
1435 #ifdef HAVE_BRAILLE
1436 StartBraille();
1437 #endif
1439 if (display && default_startup)
1440 display_copyright();
1441 signal(SIGINT, SigInt);
1442 if (rflag && (rflag & 1) == 0 && !quietflag)
1444 Msg(0, "New screen...");
1445 rflag = 0;
1448 serv_read.type = EV_READ;
1449 serv_read.fd = ServerSocket;
1450 serv_read.handler = serv_read_fn;
1451 evenq(&serv_read);
1453 serv_select.pri = -10;
1454 serv_select.type = EV_ALWAYS;
1455 serv_select.handler = serv_select_fn;
1456 evenq(&serv_select);
1458 logflushev.type = EV_TIMEOUT;
1459 logflushev.handler = logflush_fn;
1461 sched();
1462 /* NOTREACHED */
1463 return 0;
1466 void
1467 WindowDied(p, wstat, wstat_valid)
1468 struct win *p;
1469 #ifdef BSDWAIT
1470 union wait wstat;
1471 #else
1472 int wstat;
1473 #endif
1474 int wstat_valid;
1476 int killit = 0;
1478 if (p->w_destroyev.data == (char *)p)
1480 wstat = p->w_exitstatus;
1481 wstat_valid = 1;
1482 evdeq(&p->w_destroyev);
1483 p->w_destroyev.data = 0;
1486 #if defined(BSDJOBS) && !defined(BSDWAIT)
1487 if (!wstat_valid && p->w_pid > 0)
1489 /* EOF on file descriptor. The process is probably also dead.
1490 * try a waitpid */
1491 if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid)
1493 p->w_pid = 0;
1494 wstat_valid = 1;
1497 #endif
1498 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1499 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1500 killit = 1;
1502 if (ZombieKey_destroy && !killit)
1504 char buf[100], *s, reason[100];
1505 time_t now;
1507 if (wstat_valid) {
1508 if (WIFEXITED(wstat))
1509 if (WEXITSTATUS(wstat))
1510 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1511 else
1512 sprintf(reason, "terminated normally");
1513 else if (WIFSIGNALED(wstat))
1514 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1515 #ifdef WCOREDUMP
1516 WCOREDUMP(wstat) ? " (core file generated)" : "");
1517 #else
1518 "");
1519 #endif
1520 } else
1521 sprintf(reason, "detached from window");
1523 (void) time(&now);
1524 s = ctime(&now);
1525 if (s && *s)
1526 s[strlen(s) - 1] = '\0';
1527 debug3("window %d (%s) going into zombie state fd %d",
1528 p->w_number, p->w_title, p->w_ptyfd);
1529 #ifdef UTMPOK
1530 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1532 RemoveUtmp(p);
1533 p->w_slot = 0; /* "detached" */
1535 #endif
1536 CloseDevice(p);
1538 p->w_deadpid = p->w_pid;
1539 p->w_pid = 0;
1540 ResetWindow(p);
1541 /* p->w_y = p->w_bot; */
1542 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1543 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1544 WriteString(p, buf, strlen(buf));
1545 WindowChanged(p, 'f');
1547 else
1548 KillWindow(p);
1549 #ifdef UTMPOK
1550 CarefulUtmp();
1551 #endif
1554 static void
1555 SigChldHandler()
1557 struct stat st;
1558 #ifdef DEBUG
1559 fds();
1560 #endif
1561 while (GotSigChld)
1563 GotSigChld = 0;
1564 DoWait();
1565 #ifdef SYSVSIGS
1566 signal(SIGCHLD, SigChld);
1567 #endif
1569 if (stat(SockPath, &st) == -1)
1571 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1572 if (!RecoverSocket())
1574 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1575 Finit(1);
1577 else
1578 debug1("'%s' reconstructed\n", SockPath);
1580 else
1581 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1584 static sigret_t
1585 SigChld SIGDEFARG
1587 debug("SigChld()\n");
1588 GotSigChld = 1;
1589 SIGRETURN;
1592 sigret_t
1593 SigHup SIGDEFARG
1595 /* Hangup all displays */
1596 while ((display = displays) != 0)
1597 Hangup();
1598 SIGRETURN;
1602 * the backend's Interrupt handler
1603 * we cannot insert the intrc directly, as we never know
1604 * if fore is valid.
1606 static sigret_t
1607 SigInt SIGDEFARG
1609 #if HAZARDOUS
1610 char ibuf;
1612 debug("SigInt()\n");
1613 if (fore && displays)
1615 # if defined(TERMIO) || defined(POSIX)
1616 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1617 # else
1618 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1619 # endif
1620 fore->w_inlen = 0;
1621 write(fore->w_ptyfd, &ibuf, 1);
1623 #else
1624 signal(SIGINT, SigInt);
1625 debug("SigInt() careful\n");
1626 InterruptPlease = 1;
1627 #endif
1628 SIGRETURN;
1631 static sigret_t
1632 CoreDump SIGDEFARG
1634 struct display *disp;
1635 char buf[80];
1637 #if defined(SYSVSIGS) && defined(SIGHASARG)
1638 signal(sigsig, SIG_IGN);
1639 #endif
1640 setgid(getgid());
1641 setuid(getuid());
1642 unlink("core");
1643 #ifdef SIGHASARG
1644 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1645 #else
1646 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1647 #endif
1648 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1650 #else /* SHADOWPW && !DEBUG */
1651 " (core dumped)"
1652 #endif /* SHADOWPW && !DEBUG */
1654 for (disp = displays; disp; disp = disp->d_next)
1656 fcntl(disp->d_userfd, F_SETFL, 0);
1657 SetTTY(disp->d_userfd, &D_OldMode);
1658 write(disp->d_userfd, buf, strlen(buf));
1659 Kill(disp->d_userpid, SIG_BYE);
1661 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1662 Kill(getpid(), SIGKILL);
1663 eexit(11);
1664 #else /* SHADOWPW && !DEBUG */
1665 abort();
1666 #endif /* SHADOWPW && !DEBUG */
1667 SIGRETURN;
1670 static void
1671 DoWait()
1673 register int pid;
1674 struct win *p, *next;
1675 #ifdef BSDWAIT
1676 union wait wstat;
1677 #else
1678 int wstat;
1679 #endif
1681 #ifdef BSDJOBS
1682 # ifndef BSDWAIT
1683 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1684 # else
1685 # ifdef USE_WAIT2
1687 * From: rouilj@sni-usa.com (John Rouillard)
1688 * note that WUNTRACED is not documented to work, but it is defined in
1689 * /usr/include/sys/wait.h, so it may work
1691 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1692 # else /* USE_WAIT2 */
1693 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1694 # endif /* USE_WAIT2 */
1695 # endif
1696 #else /* BSDJOBS */
1697 while ((pid = wait(&wstat)) < 0)
1698 if (errno != EINTR)
1699 break;
1700 if (pid > 0)
1701 #endif /* BSDJOBS */
1703 for (p = windows; p; p = next)
1705 next = p->w_next;
1706 if ( (p->w_pid && pid == p->w_pid) ||
1707 (p->w_deadpid && pid == p->w_deadpid) )
1709 /* child has ceased to exist */
1710 p->w_pid = 0;
1712 #ifdef BSDJOBS
1713 if (WIFSTOPPED(wstat))
1715 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1716 #ifdef SIGTTIN
1717 if (WSTOPSIG(wstat) == SIGTTIN)
1719 Msg(0, "Suspended (tty input)");
1720 continue;
1722 #endif
1723 #ifdef SIGTTOU
1724 if (WSTOPSIG(wstat) == SIGTTOU)
1726 Msg(0, "Suspended (tty output)");
1727 continue;
1729 #endif
1730 /* Try to restart process */
1731 Msg(0, "Child has been stopped, restarting.");
1732 if (killpg(pid, SIGCONT))
1733 kill(pid, SIGCONT);
1735 else
1736 #endif
1738 /* Screen will detect the window has died when the window's
1739 * file descriptor signals EOF (which it will do when the process in
1740 * the window terminates). So do this in a timeout of 10 seconds.
1741 * (not doing this at all might also work)
1742 * See #27061 for more details.
1744 p->w_destroyev.data = (char *)p;
1745 p->w_exitstatus = wstat;
1746 SetTimeout(&p->w_destroyev, 10 * 1000);
1747 evenq(&p->w_destroyev);
1749 break;
1751 #ifdef PSEUDOS
1752 if (p->w_pwin && pid == p->w_pwin->p_pid)
1754 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1755 FreePseudowin(p);
1756 break;
1758 #endif
1760 if (p == 0)
1762 debug1("pid %d not found - hope that's ok\n", pid);
1768 static sigret_t
1769 FinitHandler SIGDEFARG
1771 #ifdef SIGHASARG
1772 debug1("FinitHandler called, sig %d.\n", sigsig);
1773 #else
1774 debug("FinitHandler called.\n");
1775 #endif
1776 Finit(1);
1777 SIGRETURN;
1780 void
1781 Finit(i)
1782 int i;
1784 signal(SIGCHLD, SIG_DFL);
1785 signal(SIGHUP, SIG_IGN);
1786 debug1("Finit(%d);\n", i);
1787 while (windows)
1789 struct win *p = windows;
1790 windows = windows->w_next;
1791 FreeWindow(p);
1793 if (ServerSocket != -1)
1795 debug1("we unlink(%s)\n", SockPath);
1796 #ifdef USE_SETEUID
1797 xseteuid(real_uid);
1798 xsetegid(real_gid);
1799 #endif
1800 (void) unlink(SockPath);
1801 #ifdef USE_SETEUID
1802 xseteuid(eff_uid);
1803 xsetegid(eff_gid);
1804 #endif
1806 for (display = displays; display; display = display->d_next)
1808 if (D_status)
1809 RemoveStatus();
1810 FinitTerm();
1811 #ifdef UTMPOK
1812 RestoreLoginSlot();
1813 #endif
1814 AddStr("[screen is terminating]\r\n");
1815 Flush(3);
1816 SetTTY(D_userfd, &D_OldMode);
1817 fcntl(D_userfd, F_SETFL, 0);
1818 freetty();
1819 Kill(D_userpid, SIG_BYE);
1822 * we _cannot_ call eexit(i) here,
1823 * instead of playing with the Socket above. Sigh.
1825 exit(i);
1828 void
1829 eexit(e)
1830 int e;
1832 debug("eexit\n");
1833 if (ServerSocket != -1)
1835 debug1("we unlink(%s)\n", SockPath);
1836 setgid(real_gid);
1837 setuid(real_uid);
1838 (void) unlink(SockPath);
1840 exit(e);
1843 void
1844 Hangup()
1846 if (display == 0)
1847 return;
1848 debug1("Hangup %x\n", display);
1849 if (D_userfd >= 0)
1851 close(D_userfd);
1852 D_userfd = -1;
1854 if (auto_detach || displays->d_next)
1855 Detach(D_HANGUP);
1856 else
1857 Finit(0);
1861 * Detach now has the following modes:
1862 *D_DETACH SIG_BYE detach backend and exit attacher
1863 *D_HANGUP SIG_BYE detach backend and exit attacher
1864 *D_STOP SIG_STOP stop attacher (and detach backend)
1865 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1866 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1867 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1868 *D_LOCK SIG_LOCK lock the attacher
1869 * (jw)
1870 * we always remove our utmp slots. (even when "lock" or "stop")
1871 * Note: Take extra care here, we may be called by interrupt!
1873 void
1874 Detach(mode)
1875 int mode;
1877 int sign = 0, pid;
1878 struct canvas *cv;
1879 struct win *p;
1881 if (display == 0)
1882 return;
1884 #define AddStrSock(msg) do { \
1885 if (SockName) \
1887 AddStr("[" msg " from "); \
1888 AddStr(SockName); \
1889 AddStr("]\r\n"); \
1891 else \
1892 AddStr("[" msg "]\r\n"); \
1893 } while (0)
1895 signal(SIGHUP, SIG_IGN);
1896 debug1("Detach(%d)\n", mode);
1897 if (D_status)
1898 RemoveStatus();
1899 FinitTerm();
1900 if (!display)
1901 return;
1902 switch (mode)
1904 case D_HANGUP:
1905 sign = SIG_BYE;
1906 break;
1907 case D_DETACH:
1908 AddStrSock("detached");
1909 sign = SIG_BYE;
1910 break;
1911 #ifdef BSDJOBS
1912 case D_STOP:
1913 sign = SIG_STOP;
1914 break;
1915 #endif
1916 #ifdef REMOTE_DETACH
1917 case D_REMOTE:
1918 AddStrSock("remote detached");
1919 sign = SIG_BYE;
1920 break;
1921 #endif
1922 #ifdef POW_DETACH
1923 case D_POWER:
1924 AddStrSock("power detached");
1925 if (PowDetachString)
1927 AddStr(PowDetachString);
1928 AddStr("\r\n");
1930 sign = SIG_POWER_BYE;
1931 break;
1932 #ifdef REMOTE_DETACH
1933 case D_REMOTE_POWER:
1934 AddStrSock("remote power detached");
1935 if (PowDetachString)
1937 AddStr(PowDetachString);
1938 AddStr("\r\n");
1940 sign = SIG_POWER_BYE;
1941 break;
1942 #endif
1943 #endif
1944 case D_LOCK:
1945 ClearAll();
1946 sign = SIG_LOCK;
1947 /* tell attacher to lock terminal with a lockprg. */
1948 break;
1950 #ifdef UTMPOK
1951 if (displays->d_next == 0)
1953 for (p = windows; p; p = p->w_next)
1955 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1957 RemoveUtmp(p);
1959 * Set the slot to 0 to get the window
1960 * logged in again.
1962 p->w_slot = (slot_t) 0;
1966 if (mode != D_HANGUP)
1967 RestoreLoginSlot();
1968 #endif
1969 if (displays->d_next == 0 && console_window)
1971 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1973 debug("could not release console - killing window\n");
1974 KillWindow(console_window);
1975 display = displays; /* restore display */
1978 if (D_fore)
1980 #ifdef MULTIUSER
1981 ReleaseAutoWritelock(display, D_fore);
1982 #endif
1983 D_user->u_detachwin = D_fore->w_number;
1984 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1986 AutosaveLayout(D_layout);
1987 layout_last = D_layout;
1988 for (cv = D_cvlist; cv; cv = cv->c_next)
1990 p = Layer2Window(cv->c_layer);
1991 SetCanvasWindow(cv, 0);
1992 if (p)
1993 WindowChanged(p, 'u');
1996 pid = D_userpid;
1997 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1998 FreeDisplay();
1999 if (displays == 0)
2000 /* Flag detached-ness */
2001 (void) chsock();
2003 * tell father what to do. We do that after we
2004 * freed the tty, thus getty feels more comfortable on hpux
2005 * if it was a power detach.
2007 Kill(pid, sign);
2008 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
2009 debug("Detach returns, we are successfully detached.\n");
2010 signal(SIGHUP, SigHup);
2011 #undef AddStrSock
2014 static int
2015 IsSymbol(e, s)
2016 char *e, *s;
2018 register int l;
2020 l = strlen(s);
2021 return strncmp(e, s, l) == 0 && e[l] == '=';
2024 void
2025 MakeNewEnv()
2027 register char **op, **np;
2028 static char stybuf[MAXSTR];
2030 for (op = environ; *op; ++op)
2032 if (NewEnv)
2033 free((char *)NewEnv);
2034 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
2035 if (!NewEnv)
2036 Panic(0, strnomem);
2037 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
2038 *np++ = stybuf; /* NewEnv[0] */
2039 *np++ = Term; /* NewEnv[1] */
2040 np++; /* room for SHELL */
2041 #ifdef TIOCSWINSZ
2042 np += 2; /* room for TERMCAP and WINDOW */
2043 #else
2044 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2045 #endif
2047 for (op = environ; *op; ++op)
2049 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2050 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2051 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2052 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
2054 *np++ = *op;
2056 *np = 0;
2059 void
2060 /*VARARGS2*/
2061 #if defined(USEVARARGS) && defined(__STDC__)
2062 Msg(int err, const char *fmt, VA_DOTS)
2063 #else
2064 Msg(err, fmt, VA_DOTS)
2065 int err;
2066 const char *fmt;
2067 VA_DECL
2068 #endif
2070 VA_LIST(ap)
2071 char buf[MAXPATHLEN*2];
2072 char *p = buf;
2074 VA_START(ap, fmt);
2075 fmt = DoNLS(fmt);
2076 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2077 VA_END(ap);
2078 if (err)
2080 p += strlen(p);
2081 *p++ = ':';
2082 *p++ = ' ';
2083 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2084 buf[sizeof(buf) - 1] = 0;
2086 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2088 if (display && displays)
2089 MakeStatus(buf);
2090 else if (displays)
2092 for (display = displays; display; display = display->d_next)
2093 MakeStatus(buf);
2095 else if (display)
2097 /* no displays but a display - must have forked.
2098 * send message to backend!
2100 char *tty = D_usertty;
2101 struct display *olddisplay = display;
2102 display = 0; /* only send once */
2103 SendErrorMsg(tty, buf);
2104 display = olddisplay;
2106 else
2107 printf("%s\r\n", buf);
2111 * Call FinitTerm for all displays, write a message to each and call eexit();
2113 void
2114 /*VARARGS2*/
2115 #if defined(USEVARARGS) && defined(__STDC__)
2116 Panic(int err, const char *fmt, VA_DOTS)
2117 #else
2118 Panic(err, fmt, VA_DOTS)
2119 int err;
2120 const char *fmt;
2121 VA_DECL
2122 #endif
2124 VA_LIST(ap)
2125 char buf[MAXPATHLEN*2];
2126 char *p = buf;
2128 VA_START(ap, fmt);
2129 fmt = DoNLS(fmt);
2130 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2131 VA_END(ap);
2132 if (err)
2134 p += strlen(p);
2135 *p++ = ':';
2136 *p++ = ' ';
2137 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2138 buf[sizeof(buf) - 1] = 0;
2140 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2141 if (displays == 0 && display == 0)
2143 printf("%s\r\n", buf);
2144 if (PanicPid)
2145 Kill(PanicPid, SIG_BYE);
2147 else if (displays == 0)
2149 /* no displays but a display - must have forked.
2150 * send message to backend!
2152 char *tty = D_usertty;
2153 display = 0;
2154 SendErrorMsg(tty, buf);
2155 sleep(2);
2156 _exit(1);
2158 else
2159 for (display = displays; display; display = display->d_next)
2161 if (D_status)
2162 RemoveStatus();
2163 FinitTerm();
2164 Flush(3);
2165 #ifdef UTMPOK
2166 RestoreLoginSlot();
2167 #endif
2168 SetTTY(D_userfd, &D_OldMode);
2169 fcntl(D_userfd, F_SETFL, 0);
2170 write(D_userfd, buf, strlen(buf));
2171 write(D_userfd, "\n", 1);
2172 freetty();
2173 if (D_userpid)
2174 Kill(D_userpid, SIG_BYE);
2176 #ifdef MULTIUSER
2177 if (tty_oldmode >= 0)
2179 # ifdef USE_SETEUID
2180 if (setuid(own_uid))
2181 xseteuid(own_uid); /* may be a loop. sigh. */
2182 # else
2183 setuid(own_uid);
2184 # endif
2185 debug1("Panic: changing back modes from %s\n", attach_tty);
2186 chmod(attach_tty, tty_oldmode);
2188 #endif
2189 eexit(1);
2194 * '^' is allowed as an escape mechanism for control characters. jw.
2196 * Added time insertion using ideas/code from /\ndy Jones
2197 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2201 #ifndef USE_LOCALE
2202 static const char days[] = "SunMonTueWedThuFriSat";
2203 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2204 #endif
2206 static char winmsg_buf[MAXSTR];
2207 #define MAX_WINMSG_REND 16 /* rendition changes */
2208 static int winmsg_rend[MAX_WINMSG_REND];
2209 static int winmsg_rendpos[MAX_WINMSG_REND];
2210 static int winmsg_numrend;
2212 static char *
2213 pad_expand(buf, p, numpad, padlen)
2214 char *buf;
2215 char *p;
2216 int numpad;
2217 int padlen;
2219 char *pn, *pn2;
2220 int i, r;
2222 padlen = padlen - (p - buf); /* space for rent */
2223 if (padlen < 0)
2224 padlen = 0;
2225 pn2 = pn = p + padlen;
2226 r = winmsg_numrend;
2227 while (p >= buf)
2229 if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
2231 winmsg_rendpos[--r] = pn - buf;
2232 continue;
2234 *pn-- = *p;
2235 if (*p-- == 127)
2237 pn[1] = ' ';
2238 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2239 padlen -= i;
2240 while (i-- > 0)
2241 *pn-- = ' ';
2242 numpad--;
2243 if (r && p - buf == winmsg_rendpos[r - 1])
2244 winmsg_rendpos[--r] = pn - buf;
2247 return pn2;
2250 struct backtick {
2251 struct backtick *next;
2252 int num;
2253 int tick;
2254 int lifespan;
2255 time_t bestbefore;
2256 char result[MAXSTR];
2257 char **cmdv;
2258 struct event ev;
2259 char *buf;
2260 int bufi;
2263 struct backtick *backticks;
2265 static void
2266 backtick_filter(bt)
2267 struct backtick *bt;
2269 char *p, *q;
2270 int c;
2272 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2274 if (c == '\t')
2275 c = ' ';
2276 if (c >= ' ' || c == '\005')
2277 *q++ = c;
2279 *q = 0;
2282 static void
2283 backtick_fn(ev, data)
2284 struct event *ev;
2285 char *data;
2287 struct backtick *bt;
2288 int i, j, k, l;
2290 bt = (struct backtick *)data;
2291 debug1("backtick_fn for #%d\n", bt->num);
2292 i = bt->bufi;
2293 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2294 if (l <= 0)
2296 debug1("EOF on backtick #%d\n", bt->num);
2297 evdeq(ev);
2298 close(ev->fd);
2299 ev->fd = -1;
2300 return;
2302 debug1("read %d bytes\n", l);
2303 i += l;
2304 for (j = 0; j < l; j++)
2305 if (bt->buf[i - j - 1] == '\n')
2306 break;
2307 if (j < l)
2309 for (k = i - j - 2; k >= 0; k--)
2310 if (bt->buf[k] == '\n')
2311 break;
2312 k++;
2313 bcopy(bt->buf + k, bt->result, i - j - k);
2314 bt->result[i - j - k - 1] = 0;
2315 backtick_filter(bt);
2316 WindowChanged(0, '`');
2318 if (j == l && i == MAXSTR)
2320 j = MAXSTR/2;
2321 l = j + 1;
2323 if (j < l)
2325 if (j)
2326 bcopy(bt->buf + i - j, bt->buf, j);
2327 i = j;
2329 bt->bufi = i;
2332 void
2333 setbacktick(num, lifespan, tick, cmdv)
2334 int num;
2335 int lifespan;
2336 int tick;
2337 char **cmdv;
2339 struct backtick **btp, *bt;
2340 char **v;
2342 debug1("setbacktick called for backtick #%d\n", num);
2343 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2344 if (bt->num == num)
2345 break;
2346 if (!bt && !cmdv)
2347 return;
2348 if (bt)
2350 for (v = bt->cmdv; *v; v++)
2351 free(*v);
2352 free(bt->cmdv);
2353 if (bt->buf)
2354 free(bt->buf);
2355 if (bt->ev.fd >= 0)
2356 close(bt->ev.fd);
2357 evdeq(&bt->ev);
2359 if (bt && !cmdv)
2361 *btp = bt->next;
2362 free(bt);
2363 return;
2365 if (!bt)
2367 bt = (struct backtick *)malloc(sizeof *bt);
2368 if (!bt)
2370 Msg(0, strnomem);
2371 return;
2373 bzero(bt, sizeof(*bt));
2374 bt->next = 0;
2375 *btp = bt;
2377 bt->num = num;
2378 bt->tick = tick;
2379 bt->lifespan = lifespan;
2380 bt->bestbefore = 0;
2381 bt->result[0] = 0;
2382 bt->buf = 0;
2383 bt->bufi = 0;
2384 bt->cmdv = cmdv;
2385 bt->ev.fd = -1;
2386 if (bt->tick == 0 && bt->lifespan == 0)
2388 debug("setbacktick: continuous mode\n");
2389 bt->buf = (char *)malloc(MAXSTR);
2390 if (bt->buf == 0)
2392 Msg(0, strnomem);
2393 setbacktick(num, 0, 0, (char **)0);
2394 return;
2396 bt->ev.type = EV_READ;
2397 bt->ev.fd = readpipe(bt->cmdv);
2398 bt->ev.handler = backtick_fn;
2399 bt->ev.data = (char *)bt;
2400 if (bt->ev.fd >= 0)
2401 evenq(&bt->ev);
2405 static char *
2406 runbacktick(bt, tickp, now)
2407 struct backtick *bt;
2408 int *tickp;
2409 time_t now;
2411 int f, i, l, j;
2412 time_t now2;
2414 debug1("runbacktick called for backtick #%d\n", bt->num);
2415 if (bt->tick && (!*tickp || bt->tick < *tickp))
2416 *tickp = bt->tick;
2417 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2419 debug1("returning old result (%d)\n", bt->lifespan);
2420 return bt->result;
2422 f = readpipe(bt->cmdv);
2423 if (f == -1)
2424 return bt->result;
2425 i = 0;
2426 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2428 debug1("runbacktick: read %d bytes\n", l);
2429 i += l;
2430 for (j = 1; j < l; j++)
2431 if (bt->result[i - j - 1] == '\n')
2432 break;
2433 if (j == l && i == sizeof(bt->result))
2435 j = sizeof(bt->result) / 2;
2436 l = j + 1;
2438 if (j < l)
2440 bcopy(bt->result + i - j, bt->result, j);
2441 i = j;
2444 close(f);
2445 bt->result[sizeof(bt->result) - 1] = '\n';
2446 if (i && bt->result[i - 1] == '\n')
2447 i--;
2448 debug1("runbacktick: finished, %d bytes\n", i);
2449 bt->result[i] = 0;
2450 backtick_filter(bt);
2451 (void)time(&now2);
2452 bt->bestbefore = now2 + bt->lifespan;
2453 return bt->result;
2457 AddWinMsgRend(str, r)
2458 const char *str;
2459 int r;
2461 if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf ||
2462 str >= winmsg_buf + MAXSTR)
2463 return -1;
2465 winmsg_rend[winmsg_numrend] = r;
2466 winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
2467 winmsg_numrend++;
2469 return 0;
2472 char *
2473 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2474 char *str;
2475 struct win *win;
2476 int esc;
2477 int padlen;
2478 struct event *ev;
2479 int rec;
2481 static int tick;
2482 char *s = str;
2483 register char *p = winmsg_buf;
2484 register int ctrl;
2485 struct timeval now;
2486 struct tm *tm;
2487 int l, i, r;
2488 int num;
2489 int zeroflg;
2490 int longflg;
2491 int minusflg;
2492 int plusflg;
2493 int qmflag = 0, omflag = 0, qmnumrend = 0;
2494 char *qmpos = 0;
2495 int numpad = 0;
2496 int lastpad = 0;
2497 int truncpos = -1;
2498 int truncper = 0;
2499 int trunclong = 0;
2500 struct backtick *bt;
2502 if (winmsg_numrend >= 0)
2503 winmsg_numrend = 0;
2504 else
2505 winmsg_numrend = -winmsg_numrend;
2507 tick = 0;
2508 tm = 0;
2509 ctrl = 0;
2510 gettimeofday(&now, NULL);
2511 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2513 *p = *s;
2514 if (ctrl)
2516 ctrl = 0;
2517 if (*s != '^' && *s >= 64)
2518 *p &= 0x1f;
2519 continue;
2521 if (*s != esc)
2523 if (esc == '%')
2525 switch (*s)
2527 #if 0
2528 case '~':
2529 *p = BELL;
2530 break;
2531 #endif
2532 case '^':
2533 ctrl = 1;
2534 *p-- = '^';
2535 break;
2536 default:
2537 break;
2540 continue;
2542 if (*++s == esc) /* double escape ? */
2543 continue;
2544 if ((plusflg = *s == '+') != 0)
2545 s++;
2546 if ((minusflg = *s == '-') != 0)
2547 s++;
2548 if ((zeroflg = *s == '0') != 0)
2549 s++;
2550 num = 0;
2551 while(*s >= '0' && *s <= '9')
2552 num = num * 10 + (*s++ - '0');
2553 if ((longflg = *s == 'L') != 0)
2554 s++;
2555 switch (*s)
2557 case '?':
2558 p--;
2559 if (qmpos)
2561 if ((!qmflag && !omflag) || omflag == 1)
2563 p = qmpos;
2564 if (qmnumrend < winmsg_numrend)
2565 winmsg_numrend = qmnumrend;
2567 qmpos = 0;
2568 break;
2570 qmpos = p;
2571 qmnumrend = winmsg_numrend;
2572 qmflag = omflag = 0;
2573 break;
2574 case ':':
2575 p--;
2576 if (!qmpos)
2577 break;
2578 if (qmflag && omflag != 1)
2580 omflag = 1;
2581 qmpos = p;
2582 qmnumrend = winmsg_numrend;
2584 else
2586 p = qmpos;
2587 if (qmnumrend < winmsg_numrend)
2588 winmsg_numrend = qmnumrend;
2589 omflag = -1;
2591 break;
2592 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2593 case 'a': case 'A': case 's': case 'c': case 'C':
2594 if (l < 4)
2595 break;
2596 if (tm == 0)
2598 time_t nowsec = now.tv_sec;
2599 tm = localtime(&nowsec);
2601 qmflag = 1;
2602 if (!tick || tick > 3600)
2603 tick = 3600;
2604 switch (*s)
2606 case 'd':
2607 sprintf(p, "%02d", tm->tm_mday % 100);
2608 break;
2609 case 'D':
2610 #ifdef USE_LOCALE
2611 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2612 #else
2613 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2614 #endif
2615 break;
2616 case 'm':
2617 sprintf(p, "%02d", tm->tm_mon + 1);
2618 break;
2619 case 'M':
2620 #ifdef USE_LOCALE
2621 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2622 #else
2623 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2624 #endif
2625 break;
2626 case 'y':
2627 sprintf(p, "%02d", tm->tm_year % 100);
2628 break;
2629 case 'Y':
2630 sprintf(p, "%04d", tm->tm_year + 1900);
2631 break;
2632 case 'a':
2633 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2634 break;
2635 case 'A':
2636 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2637 break;
2638 case 's':
2639 sprintf(p, "%02d", tm->tm_sec);
2640 tick = 1;
2641 break;
2642 case 'c':
2643 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2644 if (!tick || tick > 60)
2645 tick = 60;
2646 break;
2647 case 'C':
2648 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2649 if (!tick || tick > 60)
2650 tick = 60;
2651 break;
2652 default:
2653 break;
2655 p += strlen(p) - 1;
2656 break;
2657 case 'l':
2658 #ifdef LOADAV
2659 *p = 0;
2660 if (l > 20)
2661 AddLoadav(p);
2662 if (*p)
2664 qmflag = 1;
2665 p += strlen(p) - 1;
2667 else
2668 *p = '?';
2669 if (!tick || tick > 60)
2670 tick = 60;
2671 #else
2672 *p = '?';
2673 #endif
2674 p += strlen(p) - 1;
2675 break;
2676 case '`':
2677 case 'h':
2678 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2680 p--;
2681 break;
2683 if (*s == '`')
2685 for (bt = backticks; bt; bt = bt->next)
2686 if (bt->num == num)
2687 break;
2688 if (bt == 0)
2690 p--;
2691 break;
2695 char savebuf[sizeof(winmsg_buf)];
2696 int oldtick = tick;
2697 int oldnumrend = winmsg_numrend;
2699 *p = 0;
2700 strcpy(savebuf, winmsg_buf);
2701 winmsg_numrend = -winmsg_numrend;
2702 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2703 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2704 if (!tick || oldtick < tick)
2705 tick = oldtick;
2706 if ((int)strlen(winmsg_buf) < l)
2707 strcat(savebuf, winmsg_buf);
2708 strcpy(winmsg_buf, savebuf);
2709 while (oldnumrend < winmsg_numrend)
2710 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2711 if (*p)
2712 qmflag = 1;
2713 p += strlen(p) - 1;
2715 break;
2716 case 'w':
2717 case 'W':
2719 struct win *oldfore = 0;
2720 char *ss;
2722 if (display)
2724 oldfore = D_fore;
2725 D_fore = win;
2727 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1);
2728 if (display)
2729 D_fore = oldfore;
2731 if (*p)
2732 qmflag = 1;
2733 p += strlen(p) - 1;
2734 break;
2735 case 'u':
2736 *p = 0;
2737 if (win)
2738 AddOtherUsers(p, l - 1, win);
2739 if (*p)
2740 qmflag = 1;
2741 p += strlen(p) - 1;
2742 break;
2743 case 'f':
2744 *p = 0;
2745 if (win)
2746 AddWindowFlags(p, l - 1, win);
2747 if (*p)
2748 qmflag = 1;
2749 p += strlen(p) - 1;
2750 break;
2751 case 't':
2752 *p = 0;
2753 if (win && (int)strlen(win->w_title) < l)
2755 strcpy(p, win->w_title);
2756 if (*p)
2757 qmflag = 1;
2759 p += strlen(p) - 1;
2760 break;
2761 case '{':
2763 char rbuf[128];
2764 s++;
2765 for (i = 0; i < 127; i++)
2766 if (s[i] && s[i] != '}')
2767 rbuf[i] = s[i];
2768 else
2769 break;
2770 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2772 r = -1;
2773 rbuf[i] = 0;
2774 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2775 if (i != 1 || rbuf[0] != '-')
2776 r = ParseAttrColor(rbuf, (char *)0, 0);
2777 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2779 winmsg_rend[winmsg_numrend] = r;
2780 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2781 winmsg_numrend++;
2784 s += i;
2785 p--;
2787 break;
2788 case 'H':
2789 *p = 0;
2790 if ((int)strlen(HostName) < l)
2792 strcpy(p, HostName);
2793 if (*p)
2794 qmflag = 1;
2796 p += strlen(p) - 1;
2797 break;
2798 case 'S':
2800 char *session_name;
2801 *p = 0;
2802 session_name = strchr(SockName, '.') + 1;
2803 if ((int)strlen(session_name) < l)
2805 strcpy(p, session_name);
2806 if (*p)
2807 qmflag = 1;
2809 p += strlen(p) - 1;
2811 break;
2812 case 'p':
2814 sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2815 p += strlen(p) - 1;
2817 break;
2818 case 'F':
2819 p--;
2820 /* small hack */
2821 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2822 minusflg = !minusflg;
2823 if (minusflg)
2824 qmflag = 1;
2825 break;
2826 case 'P':
2827 p--;
2828 if (display && ev && ev != &D_hstatusev) /* Hack */
2830 /* Is the layer in the current canvas in copy mode? */
2831 struct canvas *cv = (struct canvas *)ev->data;
2832 if (ev == &cv->c_captev && cv->c_layer->l_layfn == &MarkLf)
2833 qmflag = 1;
2835 break;
2836 case '>':
2837 truncpos = p - winmsg_buf;
2838 truncper = num > 100 ? 100 : num;
2839 trunclong = longflg;
2840 p--;
2841 break;
2842 case '=':
2843 case '<':
2844 *p = ' ';
2845 if (num || zeroflg || plusflg || longflg || (*s != '='))
2847 /* expand all pads */
2848 if (minusflg)
2850 num = (plusflg ? lastpad : padlen) - num;
2851 if (!plusflg && padlen == 0)
2852 num = p - winmsg_buf;
2853 plusflg = 0;
2855 else if (!zeroflg)
2857 if (*s != '=' && num == 0 && !plusflg)
2858 num = 100;
2859 if (num > 100)
2860 num = 100;
2861 if (padlen == 0)
2862 num = p - winmsg_buf;
2863 else
2864 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2866 if (num < 0)
2867 num = 0;
2868 if (plusflg)
2869 num += lastpad;
2870 if (num > MAXSTR - 1)
2871 num = MAXSTR - 1;
2872 if (numpad)
2873 p = pad_expand(winmsg_buf, p, numpad, num);
2874 numpad = 0;
2875 if (p - winmsg_buf > num && !longflg)
2877 int left, trunc;
2879 if (truncpos == -1)
2881 truncpos = lastpad;
2882 truncper = 0;
2884 trunc = lastpad + truncper * (num - lastpad) / 100;
2885 if (trunc > num)
2886 trunc = num;
2887 if (trunc < lastpad)
2888 trunc = lastpad;
2889 left = truncpos - trunc;
2890 if (left > p - winmsg_buf - num)
2891 left = p - winmsg_buf - num;
2892 debug1("lastpad = %d, ", lastpad);
2893 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2894 if (left > 0)
2896 if (left + lastpad > p - winmsg_buf)
2897 left = p - winmsg_buf - lastpad;
2898 if (p - winmsg_buf - lastpad - left > 0)
2899 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2900 p -= left;
2901 r = winmsg_numrend;
2902 while (r && winmsg_rendpos[r - 1] > lastpad)
2904 r--;
2905 winmsg_rendpos[r] -= left;
2906 if (winmsg_rendpos[r] < lastpad)
2907 winmsg_rendpos[r] = lastpad;
2909 if (trunclong)
2911 if (p - winmsg_buf > lastpad)
2912 winmsg_buf[lastpad] = '.';
2913 if (p - winmsg_buf > lastpad + 1)
2914 winmsg_buf[lastpad + 1] = '.';
2915 if (p - winmsg_buf > lastpad + 2)
2916 winmsg_buf[lastpad + 2] = '.';
2919 if (p - winmsg_buf > num)
2921 p = winmsg_buf + num;
2922 if (trunclong)
2924 if (num - 1 >= lastpad)
2925 p[-1] = '.';
2926 if (num - 2 >= lastpad)
2927 p[-2] = '.';
2928 if (num - 3 >= lastpad)
2929 p[-3] = '.';
2931 r = winmsg_numrend;
2932 while (r && winmsg_rendpos[r - 1] > num)
2933 winmsg_rendpos[--r] = num;
2935 truncpos = -1;
2936 trunclong = 0;
2937 if (lastpad > p - winmsg_buf)
2938 lastpad = p - winmsg_buf;
2939 debug1("lastpad now %d\n", lastpad);
2941 if (*s == '=')
2943 while (p - winmsg_buf < num)
2944 *p++ = ' ';
2945 lastpad = p - winmsg_buf;
2946 truncpos = -1;
2947 trunclong = 0;
2948 debug1("lastpad2 now %d\n", lastpad);
2950 p--;
2952 else if (padlen)
2954 *p = 127; /* internal pad representation */
2955 numpad++;
2957 break;
2958 case 'n':
2959 s++;
2960 /* FALLTHROUGH */
2961 default:
2962 s--;
2963 if (l > 10 + num)
2965 if (num == 0)
2966 num = 1;
2967 if (!win)
2968 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2969 else
2970 sprintf(p, "%*d", num, win->w_number);
2971 qmflag = 1;
2972 p += strlen(p) - 1;
2974 break;
2977 if (qmpos && !qmflag)
2978 p = qmpos + 1;
2979 *p = '\0';
2980 if (numpad)
2982 if (padlen > MAXSTR - 1)
2983 padlen = MAXSTR - 1;
2984 p = pad_expand(winmsg_buf, p, numpad, padlen);
2986 if (ev)
2988 evdeq(ev); /* just in case */
2989 ev->timeout.tv_sec = 0;
2990 ev->timeout.tv_usec = 0;
2992 if (ev && tick)
2994 now.tv_usec = 100000;
2995 if (tick == 1)
2996 now.tv_sec++;
2997 else
2998 now.tv_sec += tick - (now.tv_sec % tick);
2999 ev->timeout = now;
3000 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
3002 return winmsg_buf;
3005 char *
3006 MakeWinMsg(s, win, esc)
3007 char *s;
3008 struct win *win;
3009 int esc;
3011 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
3014 void
3015 PutWinMsg(s, start, max)
3016 char *s;
3017 int start, max;
3019 int i, p, l, r, n;
3020 struct mchar rend;
3021 struct mchar rendstack[MAX_WINMSG_REND];
3022 int rendstackn = 0;
3024 if (s != winmsg_buf)
3026 /* sorry, no fancy coloring available */
3027 debug1("PutWinMsg %s plain\n", s);
3028 l = strlen(s);
3029 if (l > max)
3030 l = max;
3031 l -= start;
3032 s += start;
3033 while (l-- > 0)
3034 PUTCHARLP(*s++);
3035 return;
3037 rend = D_rend;
3038 p = 0;
3039 l = strlen(s);
3040 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3041 for (i = 0; i < winmsg_numrend && max > 0; i++)
3043 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3044 break;
3045 if (p < winmsg_rendpos[i])
3047 n = winmsg_rendpos[i] - p;
3048 if (n > max)
3049 n = max;
3050 max -= n;
3051 p += n;
3052 while(n-- > 0)
3054 if (start-- > 0)
3055 s++;
3056 else
3057 PUTCHARLP(*s++);
3060 r = winmsg_rend[i];
3061 if (r == -1)
3063 if (rendstackn > 0)
3064 rend = rendstack[--rendstackn];
3066 else
3068 rendstack[rendstackn++] = rend;
3069 ApplyAttrColor(r, &rend);
3071 SetRendition(&rend);
3073 if (p < l)
3075 n = l - p;
3076 if (n > max)
3077 n = max;
3078 while(n-- > 0)
3080 if (start-- > 0)
3081 s++;
3082 else
3083 PUTCHARLP(*s++);
3089 #ifdef DEBUG
3090 static void
3091 fds1(i, j)
3092 int i, j;
3094 while (i < j)
3096 debug1("%d ", i);
3097 i++;
3099 if ((j = open("/dev/null", 0)) >= 0)
3101 fds1(i + 1, j);
3102 close(j);
3104 else
3106 while (dup(++i) < 0 && errno != EBADF)
3107 debug1("%d ", i);
3108 debug1(" [%d]\n", i);
3112 static void
3113 fds()
3115 debug("fds: ");
3116 fds1(-1, -1);
3118 #endif
3120 static void
3121 serv_read_fn(ev, data)
3122 struct event *ev;
3123 char *data;
3125 debug("Knock - knock!\n");
3126 ReceiveMsg();
3129 static void
3130 serv_select_fn(ev, data)
3131 struct event *ev;
3132 char *data;
3134 struct win *p;
3136 debug("serv_select_fn called\n");
3137 /* XXX: messages?? */
3138 if (GotSigChld)
3140 SigChldHandler();
3142 if (InterruptPlease)
3144 debug("Backend received interrupt\n");
3145 /* This approach is rather questionable in a multi-display
3146 * environment */
3147 if (fore && displays)
3149 #if defined(TERMIO) || defined(POSIX)
3150 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3151 #else
3152 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3153 #endif
3154 #ifdef PSEUDOS
3155 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3156 &ibuf, 1);
3157 debug1("Backend wrote interrupt to %d", fore->w_number);
3158 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3159 #else
3160 write(fore->w_ptyfd, &ibuf, 1);
3161 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3162 #endif
3164 InterruptPlease = 0;
3167 for (p = windows; p; p = p->w_next)
3169 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3171 struct canvas *cv;
3172 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3173 p->w_bell = BELL_ON;
3174 for (display = displays; display; display = display->d_next)
3176 for (cv = D_cvlist; cv; cv = cv->c_next)
3177 if (cv->c_layer->l_bottom == &p->w_layer)
3178 break;
3179 if (cv == 0)
3181 p->w_bell = BELL_DONE;
3182 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3184 else if (visual && !D_VB && (!D_status || !D_status_bell))
3186 Msg(0, "%s", VisualBellString);
3187 if (D_status)
3189 D_status_bell = 1;
3190 debug1("using vbell timeout %d\n", VBellWait);
3191 SetTimeout(&D_statusev, VBellWait );
3195 /* don't annoy the user with two messages */
3196 if (p->w_monitor == MON_FOUND)
3197 p->w_monitor = MON_DONE;
3198 WindowChanged(p, 'f');
3200 if (p->w_monitor == MON_FOUND)
3202 struct canvas *cv;
3203 p->w_monitor = MON_ON;
3204 for (display = displays; display; display = display->d_next)
3206 for (cv = D_cvlist; cv; cv = cv->c_next)
3207 if (cv->c_layer->l_bottom == &p->w_layer)
3208 break;
3209 if (cv)
3210 continue; /* user already sees window */
3211 #ifdef MULTIUSER
3212 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3213 continue; /* user doesn't care */
3214 #endif
3215 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3216 p->w_monitor = MON_DONE;
3218 WindowChanged(p, 'f');
3222 for (display = displays; display; display = display->d_next)
3224 struct canvas *cv;
3225 if (D_status == STATUS_ON_WIN)
3226 continue;
3227 /* XXX: should use display functions! */
3228 for (cv = D_cvlist; cv; cv = cv->c_next)
3230 int lx, ly;
3232 /* normalize window, see resize.c */
3233 lx = cv->c_layer->l_x;
3234 ly = cv->c_layer->l_y;
3235 if (lx == cv->c_layer->l_width)
3236 lx--;
3237 if (ly + cv->c_yoff < cv->c_ys)
3239 int i, n = cv->c_ys - (ly + cv->c_yoff);
3240 cv->c_yoff = cv->c_ys - ly;
3241 RethinkViewportOffsets(cv);
3242 if (n > cv->c_layer->l_height)
3243 n = cv->c_layer->l_height;
3244 CV_CALL(cv,
3245 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3246 LayRedisplayLine(-1, -1, -1, 1);
3247 for (i = 0; i < n; i++)
3248 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3249 if (cv == cv->c_display->d_forecv)
3250 LaySetCursor();
3253 else if (ly + cv->c_yoff > cv->c_ye)
3255 int i, n = ly + cv->c_yoff - cv->c_ye;
3256 cv->c_yoff = cv->c_ye - ly;
3257 RethinkViewportOffsets(cv);
3258 if (n > cv->c_layer->l_height)
3259 n = cv->c_layer->l_height;
3260 CV_CALL(cv,
3261 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3262 LayRedisplayLine(-1, -1, -1, 1);
3263 for (i = 0; i < n; i++)
3264 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3265 if (cv == cv->c_display->d_forecv)
3266 LaySetCursor();
3269 if (lx + cv->c_xoff < cv->c_xs)
3271 int i, n = cv->c_xs - (lx + cv->c_xoff);
3272 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3273 n = (cv->c_xe - cv->c_xs + 1) / 2;
3274 if (cv->c_xoff + n > cv->c_xs)
3275 n = cv->c_xs - cv->c_xoff;
3276 cv->c_xoff += n;
3277 RethinkViewportOffsets(cv);
3278 if (n > cv->c_layer->l_width)
3279 n = cv->c_layer->l_width;
3280 CV_CALL(cv,
3281 LayRedisplayLine(-1, -1, -1, 1);
3282 for (i = 0; i < flayer->l_height; i++)
3284 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3285 LayRedisplayLine(i, 0, n - 1, 1);
3287 if (cv == cv->c_display->d_forecv)
3288 LaySetCursor();
3291 else if (lx + cv->c_xoff > cv->c_xe)
3293 int i, n = lx + cv->c_xoff - cv->c_xe;
3294 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3295 n = (cv->c_xe - cv->c_xs + 1) / 2;
3296 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3297 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3298 cv->c_xoff -= n;
3299 RethinkViewportOffsets(cv);
3300 if (n > cv->c_layer->l_width)
3301 n = cv->c_layer->l_width;
3302 CV_CALL(cv,
3303 LayRedisplayLine(-1, -1, -1, 1);
3304 for (i = 0; i < flayer->l_height; i++)
3306 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3307 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3309 if (cv == cv->c_display->d_forecv)
3310 LaySetCursor();
3316 for (display = displays; display; display = display->d_next)
3318 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3319 continue;
3320 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3321 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3325 static void
3326 logflush_fn(ev, data)
3327 struct event *ev;
3328 char *data;
3330 struct win *p;
3331 char *buf;
3332 int n;
3334 if (!islogfile(NULL))
3335 return; /* no more logfiles */
3336 logfflush(NULL);
3337 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3338 if (n)
3340 SetTimeout(ev, n * 1000);
3341 evenq(ev); /* re-enqueue ourself */
3343 if (!logtstamp_on)
3344 return;
3345 /* write fancy time-stamp */
3346 for (p = windows; p; p = p->w_next)
3348 if (!p->w_log)
3349 continue;
3350 p->w_logsilence += n;
3351 if (p->w_logsilence < logtstamp_after)
3352 continue;
3353 if (p->w_logsilence - n >= logtstamp_after)
3354 continue;
3355 buf = MakeWinMsg(logtstamp_string, p, '%');
3356 logfwrite(p->w_log, buf, strlen(buf));
3361 * Interprets ^?, ^@ and other ^-control-char notation.
3362 * Interprets \ddd octal notation
3364 * The result is placed in *cp, p is advanced behind the parsed expression and
3365 * returned.
3367 static char *
3368 ParseChar(p, cp)
3369 char *p, *cp;
3371 if (*p == 0)
3372 return 0;
3373 if (*p == '^' && p[1])
3375 if (*++p == '?')
3376 *cp = '\177';
3377 else if (*p >= '@')
3378 *cp = Ctrl(*p);
3379 else
3380 return 0;
3381 ++p;
3383 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3385 *cp = 0;
3387 *cp = *cp * 8 + *p - '0';
3388 while (*++p <= '7' && *p >= '0');
3390 else
3391 *cp = *p++;
3392 return p;
3395 static int
3396 ParseEscape(p)
3397 char *p;
3399 unsigned char buf[2];
3401 if (*p == 0)
3402 SetEscape((struct acluser *)0, -1, -1);
3403 else
3405 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3406 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3407 return -1;
3408 SetEscape((struct acluser *)0, buf[0], buf[1]);
3410 return 0;