Document the onsilent/onactivity event.
[screen-lua.git] / src / screen.c
blobd2ec6501b716a7b3cd9932468285c540d1908e76
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;
122 extern int visual_bell;
123 #ifdef COPY_PASTE
124 extern unsigned char mark_key_tab[];
125 #endif
126 extern char version[];
127 extern char DefaultShell[];
128 #ifdef ZMODEM
129 extern char *zmodem_sendcmd;
130 extern char *zmodem_recvcmd;
131 #endif
132 extern struct layout *layout_last;
135 char *ShellProg;
136 char *ShellArgs[2];
138 extern struct NewWindow nwin_undef, nwin_default, nwin_options;
139 struct backtick;
141 static struct passwd *getpwbyname __P((char *, struct passwd *));
142 static void SigChldHandler __P((void));
143 static sigret_t SigChld __P(SIGPROTOARG);
144 static sigret_t SigInt __P(SIGPROTOARG);
145 static sigret_t CoreDump __P(SIGPROTOARG);
146 static sigret_t FinitHandler __P(SIGPROTOARG);
147 static void DoWait __P((void));
148 static void serv_read_fn __P((struct event *, char *));
149 static void serv_select_fn __P((struct event *, char *));
150 static void logflush_fn __P((struct event *, char *));
151 static void backtick_filter __P((struct backtick *));
152 static void backtick_fn __P((struct event *, char *));
153 static char *runbacktick __P((struct backtick *, int *, time_t));
154 static int IsSymbol __P((char *, char *));
155 static char *ParseChar __P((char *, char *));
156 static int ParseEscape __P((char *));
157 static char *pad_expand __P((char *, char *, int, int));
158 #ifdef DEBUG
159 static void fds __P((void));
160 #endif
162 int nversion; /* numerical version, used for secondary DA */
164 /* the attacher */
165 struct passwd *ppp;
166 char *attach_tty;
167 int attach_fd = -1;
168 char *attach_term;
169 char *LoginName;
170 struct mode attach_Mode;
172 char SockPath[MAXPATHLEN + 2 * MAXSTR];
173 char *SockName; /* SockName is pointer in SockPath */
174 char *SockMatch = NULL; /* session id command line argument */
175 int ServerSocket = -1;
176 struct event serv_read;
177 struct event serv_select;
178 struct event logflushev;
180 char **NewEnv = NULL;
182 char *RcFileName = NULL;
183 char *home;
185 char *screenlogfile; /* filename layout */
186 int log_flush = 10; /* flush interval in seconds */
187 int logtstamp_on = 0; /* tstamp disabled */
188 char *logtstamp_string; /* stamp layout */
189 int logtstamp_after = 120; /* first tstamp after 120s */
190 char *hardcopydir = NULL;
191 char *BellString;
192 char *VisualBellString;
193 char *ActivityString;
194 #ifdef COPY_PASTE
195 char *BufferFile;
196 #endif
197 #ifdef POW_DETACH
198 char *PowDetachString;
199 #endif
200 char *hstatusstring;
201 char *captionstring;
202 char *timestring;
203 char *wliststr;
204 char *wlisttit;
205 int auto_detach = 1;
206 int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
207 int cmdflag;
208 int adaptflag;
210 #ifdef MULTIUSER
211 char *multi;
212 char *multi_home;
213 int multi_uid;
214 int own_uid;
215 int multiattach;
216 int tty_mode;
217 int tty_oldmode = -1;
218 #endif
220 char HostName[MAXSTR];
221 int MasterPid, PanicPid;
222 int real_uid, real_gid, eff_uid, eff_gid;
223 int default_startup;
224 int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
225 char *preselect = NULL; /* only used in Attach() */
227 #ifdef UTF8
228 char *screenencodings;
229 #endif
231 #ifdef DW_CHARS
232 int cjkwidth;
233 #endif
235 #ifdef NETHACK
236 int nethackflag = 0;
237 #endif
238 int maxwin = MAXWIN;
241 struct layer *flayer;
242 struct win *fore;
243 struct win *windows;
244 struct win *console_window;
249 * Do this last
251 #include "extern.h"
253 char strnomem[] = "Out of memory.";
256 static int InterruptPlease;
257 static int GotSigChld;
259 static int
260 lf_secreopen(name, wantfd, l)
261 char *name;
262 int wantfd;
263 struct logfile *l;
265 int got_fd;
267 close(wantfd);
268 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
269 lf_move_fd(got_fd, wantfd) < 0)
271 logfclose(l);
272 debug1("lf_secreopen: failed for %s\n", name);
273 return -1;
275 l->st->st_ino = l->st->st_dev = 0;
276 debug2("lf_secreopen: %d = %s\n", wantfd, name);
277 return 0;
280 /********************************************************************/
281 /********************************************************************/
282 /********************************************************************/
285 static struct passwd *
286 getpwbyname(name, ppp)
287 char *name;
288 struct passwd *ppp;
290 int n;
291 #ifdef SHADOWPW
292 struct spwd *sss = NULL;
293 static char *spw = NULL;
294 #endif
296 if (!ppp && !(ppp = getpwnam(name)))
297 return NULL;
299 /* Do password sanity check..., allow ##user for SUN_C2 security */
300 #ifdef SHADOWPW
301 pw_try_again:
302 #endif
303 n = 0;
304 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
305 strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
306 n = 13;
307 for (; n < 13; n++)
309 char c = ppp->pw_passwd[n];
310 if (!(c == '.' || c == '/' || c == '$' ||
311 (c >= '0' && c <= '9') ||
312 (c >= 'a' && c <= 'z') ||
313 (c >= 'A' && c <= 'Z')))
314 break;
317 #ifdef SHADOWPW
318 /* try to determine real password */
319 if (n < 13 && sss == 0)
321 sss = getspnam(ppp->pw_name);
322 if (sss)
324 if (spw)
325 free(spw);
326 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
327 endspent(); /* this should delete all buffers ... */
328 goto pw_try_again;
330 endspent(); /* this should delete all buffers ... */
332 #endif
333 if (n < 13)
334 ppp->pw_passwd = 0;
335 #ifdef linux
336 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
337 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
338 #endif
340 return ppp;
343 static char *
344 locale_name(void)
346 static char *s;
348 if (!s)
350 s = getenv("LC_ALL");
351 if (s == NULL)
352 s = getenv("LC_CTYPE");
353 if (s == NULL)
354 s = getenv("LANG");
356 return s;
360 main(ac, av)
361 int ac;
362 char **av;
364 register int n;
365 char *ap;
366 char *av0;
367 char socknamebuf[2 * MAXSTR];
368 int mflag = 0;
369 char *myname = (ac == 0) ? "screen" : av[0];
370 char *SockDir;
371 struct stat st;
372 #ifdef _MODE_T /* (jw) */
373 mode_t oumask;
374 #else
375 int oumask;
376 #endif
377 #if defined(SYSV) && !defined(ISC)
378 struct utsname utsnam;
379 #endif
380 struct NewWindow nwin;
381 int detached = 0; /* start up detached */
382 #ifdef MULTIUSER
383 char *sockp;
384 #endif
386 #ifdef SCRIPT
387 char *script_file = 0;
388 #endif
390 char *sty = 0;
392 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
393 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
394 #endif
395 #if defined(sun) && defined(SVR4)
397 /* Solaris' login blocks SIGHUP! This is _very bad_ */
398 sigset_t sset;
399 sigemptyset(&sset);
400 sigprocmask(SIG_SETMASK, &sset, 0);
402 #endif
405 * First, close all unused descriptors
406 * (otherwise, we might have problems with the select() call)
408 closeallfiles(0);
409 #ifdef DEBUG
410 opendebug(1, 0);
411 #endif
412 snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
413 PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
414 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
415 debug2("-- screen debug started %s (%s)\n", *av, version);
416 #ifdef POSIX
417 debug("POSIX\n");
418 #endif
419 #ifdef TERMIO
420 debug("TERMIO\n");
421 #endif
422 #ifdef SYSV
423 debug("SYSV\n");
424 #endif
425 #ifdef SYSVSIGS
426 debug("SYSVSIGS\n");
427 #endif
428 #ifdef NAMEDPIPE
429 debug("NAMEDPIPE\n");
430 #endif
431 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
432 debug("Window size changing enabled\n");
433 #endif
434 #ifdef HAVE_SETREUID
435 debug("SETREUID\n");
436 #endif
437 #ifdef HAVE_SETEUID
438 debug("SETEUID\n");
439 #endif
440 #ifdef hpux
441 debug("hpux\n");
442 #endif
443 #ifdef USEBCOPY
444 debug("USEBCOPY\n");
445 #endif
446 #ifdef UTMPOK
447 debug("UTMPOK\n");
448 #endif
449 #ifdef LOADAV
450 debug("LOADAV\n");
451 #endif
452 #ifdef NETHACK
453 debug("NETHACK\n");
454 #endif
455 #ifdef TERMINFO
456 debug("TERMINFO\n");
457 #endif
458 #ifdef SHADOWPW
459 debug("SHADOWPW\n");
460 #endif
461 #ifdef NAME_MAX
462 debug1("NAME_MAX = %d\n", NAME_MAX);
463 #endif
465 BellString = SaveStr("Bell in window %n");
466 VisualBellString = SaveStr(" Wuff, Wuff!! ");
467 ActivityString = SaveStr("Activity in window %n");
468 screenlogfile = SaveStr("screenlog.%n");
469 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
470 hstatusstring = SaveStr("%h");
471 captionstring = SaveStr("%3n %t");
472 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
473 wlisttit = SaveStr("Num Name%=Flags");
474 wliststr = SaveStr("%3n %t%=%f");
475 #ifdef COPY_PASTE
476 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
477 #endif
478 ShellProg = NULL;
479 #ifdef POW_DETACH
480 PowDetachString = 0;
481 #endif
482 default_startup = (ac > 1) ? 0 : 1;
483 adaptflag = 0;
484 VBellWait = VBELLWAIT * 1000;
485 MsgWait = MSGWAIT * 1000;
486 MsgMinWait = MSGMINWAIT * 1000;
487 SilenceWait = SILENCEWAIT;
488 #ifdef HAVE_BRAILLE
489 InitBraille();
490 #endif
491 #ifdef ZMODEM
492 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
493 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
494 #endif
496 #ifdef COPY_PASTE
497 CompileKeys((char *)0, 0, mark_key_tab);
498 #endif
499 #ifdef UTF8
500 InitBuiltinTabs();
501 screenencodings = SaveStr(SCREENENCODINGS);
502 #endif
503 #ifdef DW_CHARS
504 cjkwidth = 0;
505 #endif
506 nwin = nwin_undef;
507 nwin_options = nwin_undef;
508 strcpy(screenterm, "screen");
510 logreopen_register(lf_secreopen);
512 av0 = *av;
513 /* if this is a login screen, assume -RR */
514 if (*av0 == '-')
516 rflag = 4;
517 #ifdef MULTI
518 xflag = 1;
519 #else
520 dflag = 1;
521 #endif
522 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
524 while (ac > 0)
526 ap = *++av;
527 if (--ac > 0 && *ap == '-')
529 if (ap[1] == '-' && ap[2] == 0)
531 av++;
532 ac--;
533 break;
535 if (ap[1] == '-' && !strcmp(ap, "--version"))
536 Panic(0, "Screen version %s", version);
537 if (ap[1] == '-' && !strcmp(ap, "--help"))
538 exit_with_usage(myname, NULL, NULL);
539 while (ap && *ap && *++ap)
541 switch (*ap)
543 case 'a':
544 nwin_options.aflag = 1;
545 break;
546 case 'A':
547 adaptflag = 1;
548 break;
549 case 'p': /* preselect */
550 if (*++ap)
551 preselect = ap;
552 else
554 if (!--ac)
555 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
556 preselect = *++av;
558 ap = NULL;
559 break;
560 #ifdef HAVE_BRAILLE
561 case 'B':
562 bd.bd_start_braille = 1;
563 break;
564 #endif
565 case 'c':
566 if (*++ap)
567 RcFileName = ap;
568 else
570 if (--ac == 0)
571 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
572 RcFileName = *++av;
574 ap = NULL;
575 break;
576 case 'e':
577 if (!*++ap)
579 if (--ac == 0)
580 exit_with_usage(myname, "Specify command characters with -e", NULL);
581 ap = *++av;
583 if (ParseEscape(ap))
584 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
585 ap = NULL;
586 break;
587 case 'f':
588 ap++;
589 switch (*ap++)
591 case 'n':
592 case '0':
593 nwin_options.flowflag = FLOW_NOW * 0;
594 break;
595 case '\0':
596 ap--;
597 /* FALLTHROUGH */
598 case 'y':
599 case '1':
600 nwin_options.flowflag = FLOW_NOW * 1;
601 break;
602 case 'a':
603 nwin_options.flowflag = FLOW_AUTOFLAG;
604 break;
605 default:
606 exit_with_usage(myname, "Unknown flow option -%s", --ap);
608 break;
609 case 'h':
610 if (--ac == 0)
611 exit_with_usage(myname, NULL, NULL);
612 nwin_options.histheight = atoi(*++av);
613 if (nwin_options.histheight < 0)
614 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
615 break;
616 case 'i':
617 iflag = 1;
618 break;
619 case 't': /* title, the former AkA == -k */
620 if (--ac == 0)
621 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
622 nwin_options.aka = *++av;
623 break;
624 case 'l':
625 ap++;
626 switch (*ap++)
628 case 'n':
629 case '0':
630 nwin_options.lflag = 0;
631 break;
632 case '\0':
633 ap--;
634 /* FALLTHROUGH */
635 case 'y':
636 case '1':
637 nwin_options.lflag = 1;
638 break;
639 case 'a':
640 nwin_options.lflag = 3;
641 break;
642 case 's': /* -ls */
643 case 'i': /* -list */
644 lsflag = 1;
645 if (ac > 1 && !SockMatch)
647 SockMatch = *++av;
648 ac--;
650 ap = NULL;
651 break;
652 default:
653 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
655 break;
656 case 'w':
657 lsflag = 1;
658 wipeflag = 1;
659 if (ac > 1 && !SockMatch)
661 SockMatch = *++av;
662 ac--;
664 break;
665 case 'L':
666 nwin_options.Lflag = 1;
667 break;
668 case 'm':
669 mflag = 1;
670 break;
671 case 'O': /* to be (or not to be?) deleted. jw. */
672 force_vt = 0;
673 break;
674 case 'T':
675 if (--ac == 0)
676 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
677 if (strlen(*++av) < 20)
678 strcpy(screenterm, *av);
679 else
680 Panic(0, "-T: terminal name too long. (max. 20 char)");
681 nwin_options.term = screenterm;
682 break;
683 case 'q':
684 quietflag = 1;
685 break;
686 case 'r':
687 case 'R':
688 #ifdef MULTI
689 case 'x':
690 #endif
691 if (ac > 1 && *av[1] != '-' && !SockMatch)
693 SockMatch = *++av;
694 ac--;
695 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
697 #ifdef MULTI
698 if (*ap == 'x')
699 xflag = 1;
700 #endif
701 if (rflag)
702 rflag = 2;
703 rflag += (*ap == 'R') ? 2 : 1;
704 break;
705 #ifdef REMOTE_DETACH
706 case 'd':
707 dflag = 1;
708 /* FALLTHROUGH */
709 case 'D':
710 if (!dflag)
711 dflag = 2;
712 if (ac == 2)
714 if (*av[1] != '-' && !SockMatch)
716 SockMatch = *++av;
717 ac--;
718 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
721 break;
722 #endif
723 case 's':
724 if (--ac == 0)
725 exit_with_usage(myname, "Specify shell with -s", NULL);
726 if (ShellProg)
727 free(ShellProg);
728 ShellProg = SaveStr(*++av);
729 debug1("ShellProg: '%s'\n", ShellProg);
730 break;
731 case 'S':
732 if (!SockMatch)
734 if (--ac == 0)
735 exit_with_usage(myname, "Specify session-name with -S", NULL);
736 SockMatch = *++av;
738 if (!*SockMatch)
739 exit_with_usage(myname, "Empty session-name?", NULL);
740 break;
741 case 'X':
742 cmdflag = 1;
743 break;
744 case 'v':
745 Panic(0, "Screen version %s", version);
746 /* NOTREACHED */
747 #ifdef UTF8
748 case 'U':
749 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
750 break;
751 #endif
752 case 'u':
753 if (--ac == 0)
754 exit_with_usage(myname, "Specify lua script file with -u", NULL);
755 if (script_file)
756 free(script_file);
757 script_file = SaveStr(*++av);
758 break;
759 default:
760 exit_with_usage(myname, "Unknown option %s", --ap);
764 else
765 break;
768 real_uid = getuid();
769 real_gid = getgid();
770 eff_uid = geteuid();
771 eff_gid = getegid();
772 if (eff_uid != real_uid)
774 /* if running with s-bit, we must install a special signal
775 * handler routine that resets the s-bit, so that we get a
776 * core file anyway.
778 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
779 signal(SIGBUS, CoreDump);
780 #endif /* SIGBUS */
781 signal(SIGSEGV, CoreDump);
784 #ifdef USE_LOCALE
785 setlocale(LC_ALL, "");
786 #endif
787 #ifdef ENCODINGS
788 if (nwin_options.encoding == -1)
790 /* ask locale if we should start in UTF-8 mode */
791 # ifdef HAVE_NL_LANGINFO
792 # ifndef USE_LOCALE
793 setlocale(LC_CTYPE, "");
794 # endif
795 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
796 debug1("locale says encoding = %d\n", nwin_options.encoding);
797 # else
798 # ifdef UTF8
799 char *s;
800 if ((s = locale_name()) && InStr(s, "UTF-8"))
801 nwin_options.encoding = UTF8;
802 # endif
803 debug1("environment says encoding=%d\n", nwin_options.encoding);
804 #endif
806 # ifdef DW_CHARS
808 char *s;
809 if ((s = locale_name()))
811 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
813 cjkwidth = 1;
817 #endif
818 #endif
819 if (nwin_options.aka)
821 #ifdef ENCODINGS
822 if (nwin_options.encoding > 0)
824 size_t len = strlen(nwin_options.aka);
825 size_t newsz;
826 char *newbuf = malloc(3 * len);
827 if (!newbuf)
828 Panic(0, strnomem);
829 newsz = RecodeBuf(nwin_options.aka, len,
830 nwin_options.encoding, 0, newbuf);
831 newbuf[newsz] = '\0';
832 nwin_options.aka = newbuf;
834 else
835 #endif
837 /* If we just use the original value from av,
838 subsequent shelltitle invocations will attempt to free
839 space we don't own... */
840 nwin_options.aka = SaveStr(nwin_options.aka);
844 if (SockMatch && strlen(SockMatch) >= MAXSTR)
845 Panic(0, "Ridiculously long socketname - try again.");
846 if (cmdflag && !rflag && !dflag && !xflag)
847 xflag = 1;
848 if (!cmdflag && dflag && mflag && !(rflag || xflag))
849 detached = 1;
850 nwin = nwin_options;
851 #ifdef ENCODINGS
852 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
853 #endif
854 if (ac)
855 nwin.args = av;
857 /* make the write() calls return -1 on all errors */
858 #ifdef SIGXFSZ
860 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
861 * It appears that in System V Release 4, UNIX, if you are writing
862 * an output file and you exceed the currently set file size limit,
863 * you _don't_ just get the call to `write' returning with a
864 * failure code. Rather, you get a signal called `SIGXFSZ' which,
865 * if neither handled nor ignored, will cause your program to crash
866 * with a core dump.
868 signal(SIGXFSZ, SIG_IGN);
869 #endif /* SIGXFSZ */
871 #ifdef SIGPIPE
872 signal(SIGPIPE, SIG_IGN);
873 #endif
875 if (!ShellProg)
877 register char *sh;
879 sh = getenv("SHELL");
880 ShellProg = SaveStr(sh ? sh : DefaultShell);
882 ShellArgs[0] = ShellProg;
883 home = getenv("HOME");
884 if (!mflag && !SockMatch)
886 sty = getenv("STY");
887 if (sty && *sty == 0)
888 sty = 0;
891 #ifdef NETHACK
892 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
894 char nethackrc[MAXPATHLEN];
896 if (home && (strlen(home) < (MAXPATHLEN - 20)))
898 sprintf(nethackrc,"%s/.nethackrc", home);
899 nethackflag = !access(nethackrc, F_OK);
902 #endif
904 #ifdef MULTIUSER
905 own_uid = multi_uid = real_uid;
906 if (SockMatch && (sockp = index(SockMatch, '/')))
908 *sockp = 0;
909 multi = SockMatch;
910 SockMatch = sockp + 1;
911 if (*multi)
913 struct passwd *mppp;
914 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
915 Panic(0, "Cannot identify account '%s'.", multi);
916 multi_uid = mppp->pw_uid;
917 multi_home = SaveStr(mppp->pw_dir);
918 if (strlen(multi_home) > MAXPATHLEN - 10)
919 Panic(0, "home directory path too long");
920 # ifdef MULTI
921 /* always fake multi attach mode */
922 if (rflag || lsflag)
923 xflag = 1;
924 # endif /* MULTI */
925 detached = 0;
926 multiattach = 1;
928 /* Special case: effective user is multiuser. */
929 if (eff_uid && (multi_uid != eff_uid))
930 Panic(0, "Must run suid root for multiuser support.");
932 if (SockMatch && *SockMatch == 0)
933 SockMatch = 0;
934 #endif /* MULTIUSER */
936 if ((LoginName = getlogin()) && LoginName[0] != '\0')
938 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
939 if ((int)ppp->pw_uid != real_uid)
940 ppp = (struct passwd *) 0;
942 if (ppp == 0)
944 if ((ppp = getpwuid(real_uid)) == 0)
946 Panic(0, "getpwuid() can't identify your account!");
947 exit(1);
949 LoginName = ppp->pw_name;
951 LoginName = SaveStr(LoginName);
953 ppp = getpwbyname(LoginName, ppp);
955 #if !defined(SOCKDIR) && defined(MULTIUSER)
956 if (multi && !multiattach)
958 if (home && strcmp(home, ppp->pw_dir))
959 Panic(0, "$HOME must match passwd entry for multiuser screens.");
961 #endif
963 #define SET_GUID() do \
965 setgid(real_gid); \
966 setuid(real_uid); \
967 eff_uid = real_uid; \
968 eff_gid = real_gid; \
969 } while (0)
971 #define SET_TTYNAME(fatal) do \
973 if (!(attach_tty = ttyname(0))) \
975 if (fatal) \
976 Panic(0, "Must be connected to a terminal."); \
977 else \
978 attach_tty = ""; \
980 else if (stat(attach_tty, &st)) \
981 Panic(errno, "Cannot access '%s'", attach_tty); \
982 if (strlen(attach_tty) >= MAXPATHLEN) \
983 Panic(0, "TtyName too long - sorry."); \
984 } while (0)
986 if (home == 0 || *home == '\0')
987 home = ppp->pw_dir;
988 if (strlen(LoginName) > 20)
989 Panic(0, "LoginName too long - sorry.");
990 #ifdef MULTIUSER
991 if (multi && strlen(multi) > 20)
992 Panic(0, "Screen owner name too long - sorry.");
993 #endif
994 if (strlen(home) > MAXPATHLEN - 25)
995 Panic(0, "$HOME too long - sorry.");
997 attach_tty = "";
998 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty && !xflag))
1000 #ifndef NAMEDPIPE
1001 int fl;
1002 #endif
1004 /* ttyname implies isatty */
1005 SET_TTYNAME(1);
1006 #ifdef MULTIUSER
1007 tty_mode = (int)st.st_mode & 0777;
1008 #endif
1010 #ifndef NAMEDPIPE
1011 fl = fcntl(0, F_GETFL, 0);
1012 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1013 attach_fd = 0;
1014 #endif
1015 if (attach_fd == -1)
1017 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1018 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1019 close(n);
1021 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1023 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1024 Panic(0, "Please set a terminal type.");
1025 if (strlen(attach_term) > sizeof(D_termname) - 1)
1026 Panic(0, "$TERM too long - sorry.");
1027 GetTTY(0, &attach_Mode);
1028 #ifdef DEBUGGGGGGGGGGGGGGG
1029 DebugTTY(&attach_Mode);
1030 #endif /* DEBUG */
1033 #ifdef _MODE_T
1034 oumask = umask(0); /* well, unsigned never fails? jw. */
1035 #else
1036 if ((oumask = (int)umask(0)) == -1)
1037 Panic(errno, "Cannot change umask to zero");
1038 #endif
1039 SockDir = getenv("SCREENDIR");
1040 if (SockDir)
1042 if (strlen(SockDir) >= MAXPATHLEN - 1)
1043 Panic(0, "Ridiculously long $SCREENDIR - try again.");
1044 #ifdef MULTIUSER
1045 if (multi)
1046 Panic(0, "No $SCREENDIR with multi screens, please.");
1047 #endif
1049 #ifdef MULTIUSER
1050 if (multiattach)
1052 # ifndef SOCKDIR
1053 sprintf(SockPath, "%s/.screen", multi_home);
1054 SockDir = SockPath;
1055 # else
1056 SockDir = SOCKDIR;
1057 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1058 # endif
1060 else
1061 #endif
1063 #ifndef SOCKDIR
1064 if (SockDir == 0)
1066 sprintf(SockPath, "%s/.screen", home);
1067 SockDir = SockPath;
1069 #endif
1070 if (SockDir)
1072 if (access(SockDir, F_OK))
1074 debug1("SockDir '%s' missing ...\n", SockDir);
1075 if (UserContext() > 0)
1077 if (mkdir(SockDir, 0700))
1078 UserReturn(0);
1079 UserReturn(1);
1081 if (UserStatus() <= 0)
1082 Panic(0, "Cannot make directory '%s'.", SockDir);
1084 if (SockDir != SockPath)
1085 strcpy(SockPath, SockDir);
1087 #ifdef SOCKDIR
1088 else
1090 SockDir = SOCKDIR;
1091 if (lstat(SockDir, &st))
1093 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1094 (eff_gid != real_gid) ? 0775 :
1095 #ifdef S_ISVTX
1096 0777|S_ISVTX;
1097 #else
1098 0777;
1099 #endif
1100 if (mkdir(SockDir, n) == -1)
1101 Panic(errno, "Cannot make directory '%s'", SockDir);
1103 else
1105 if (!S_ISDIR(st.st_mode))
1106 Panic(0, "'%s' must be a directory.", SockDir);
1107 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1108 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1109 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1110 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1111 0777;
1112 if (((int)st.st_mode & 0777) != n)
1113 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1115 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1116 if (access(SockPath, F_OK))
1118 if (mkdir(SockPath, 0700) == -1)
1119 Panic(errno, "Cannot make directory '%s'", SockPath);
1120 (void) chown(SockPath, real_uid, real_gid);
1123 #endif
1126 if (stat(SockPath, &st) == -1)
1127 Panic(errno, "Cannot access %s", SockPath);
1128 else
1129 if (!S_ISDIR(st.st_mode))
1130 Panic(0, "%s is not a directory.", SockPath);
1131 #ifdef MULTIUSER
1132 if (multi)
1134 if ((int)st.st_uid != multi_uid)
1135 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1137 else
1138 #endif
1140 if ((int)st.st_uid != real_uid)
1141 Panic(0, "You are not the owner of %s.", SockPath);
1143 if ((st.st_mode & 0777) != 0700)
1144 Panic(0, "Directory %s must have mode 700.", SockPath);
1145 if (SockMatch && index(SockMatch, '/'))
1146 Panic(0, "Bad session name '%s'", SockMatch);
1147 SockName = SockPath + strlen(SockPath) + 1;
1148 *SockName = 0;
1149 (void) umask(oumask);
1150 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1152 #if defined(SYSV) && !defined(ISC)
1153 if (uname(&utsnam) == -1)
1154 Panic(errno, "uname");
1155 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1156 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1157 #else
1158 (void) gethostname(HostName, MAXSTR);
1159 HostName[MAXSTR - 1] = '\0';
1160 #endif
1161 if ((ap = index(HostName, '.')) != NULL)
1162 *ap = '\0';
1164 if (lsflag)
1166 int i, fo, oth;
1168 #ifdef MULTIUSER
1169 if (multi)
1170 real_uid = multi_uid;
1171 #endif
1172 SET_GUID();
1173 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1174 if (quietflag)
1175 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1176 if (fo == 0)
1177 Panic(0, "No Sockets found in %s.\n", SockPath);
1178 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1179 /* NOTREACHED */
1181 signal(SIG_BYE, AttacherFinit); /* prevent races */
1182 if (cmdflag)
1184 /* attach_tty is not mandatory */
1185 SET_TTYNAME(0);
1186 if (!*av)
1187 Panic(0, "Please specify a command.");
1188 SET_GUID();
1189 SendCmdMessage(sty, SockMatch, av);
1190 exit(0);
1192 else if (rflag || xflag)
1194 debug("screen -r: - is there anybody out there?\n");
1195 if (Attach(MSG_ATTACH))
1197 Attacher();
1198 /* NOTREACHED */
1200 #ifdef MULTIUSER
1201 if (multiattach)
1202 Panic(0, "Can't create sessions of other users.");
1203 #endif
1204 debug("screen -r: backend not responding -- still crying\n");
1206 else if (dflag && !mflag)
1208 SET_TTYNAME(0);
1209 Attach(MSG_DETACH);
1210 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1211 eexit(0);
1212 /* NOTREACHED */
1214 if (!SockMatch && !mflag && sty)
1216 /* attach_tty is not mandatory */
1217 SET_TTYNAME(0);
1218 SET_GUID();
1219 nwin_options.args = av;
1220 SendCreateMsg(sty, &nwin);
1221 exit(0);
1222 /* NOTREACHED */
1224 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1226 if (!detached || dflag != 2)
1227 MasterPid = fork();
1228 else
1229 MasterPid = 0;
1231 switch (MasterPid)
1233 case -1:
1234 Panic(errno, "fork");
1235 /* NOTREACHED */
1236 case 0:
1237 break;
1238 default:
1239 if (detached)
1240 exit(0);
1241 if (SockMatch)
1242 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1243 else
1244 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1245 for (ap = socknamebuf; *ap; ap++)
1246 if (*ap == '/')
1247 *ap = '-';
1248 #ifdef NAME_MAX
1249 if (strlen(socknamebuf) > NAME_MAX)
1250 socknamebuf[NAME_MAX] = 0;
1251 #endif
1252 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1253 SET_GUID();
1254 Attacher();
1255 /* NOTREACHED */
1258 if (!detached)
1259 PanicPid = getppid();
1261 if (DefaultEsc == -1)
1262 DefaultEsc = Ctrl('a');
1263 if (DefaultMetaEsc == -1)
1264 DefaultMetaEsc = 'a';
1266 ap = av0 + strlen(av0) - 1;
1267 while (ap >= av0)
1269 if (!strncmp("screen", ap, 6))
1271 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1272 break;
1274 ap--;
1276 if (ap < av0)
1277 *av0 = 'S';
1279 #ifdef DEBUG
1281 char buf[256];
1283 if (dfp && dfp != stderr)
1284 fclose(dfp);
1285 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1286 if ((dfp = fopen(buf, "w")) == NULL)
1287 dfp = stderr;
1288 else
1289 (void) chmod(buf, 0666);
1291 #endif
1292 if (!detached)
1294 if (attach_fd == -1)
1296 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1297 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1299 else
1300 n = dup(attach_fd);
1302 else
1303 n = -1;
1304 freopen("/dev/null", "r", stdin);
1305 freopen("/dev/null", "w", stdout);
1307 #ifdef DEBUG
1308 if (dfp != stderr)
1309 #endif
1310 freopen("/dev/null", "w", stderr);
1311 debug("-- screen.back debug started\n");
1314 * This guarantees that the session owner is listed, even when we
1315 * start detached. From now on we should not refer to 'LoginName'
1316 * any more, use users->u_name instead.
1318 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1319 Panic(0, "Could not create user info");
1320 if (!detached)
1322 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1323 Panic(0, "Could not alloc display");
1324 PanicPid = 0;
1325 #ifdef ENCODINGS
1326 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1327 debug1("D_encoding = %d\n", D_encoding);
1328 #endif
1331 if (SockMatch)
1333 /* user started us with -S option */
1334 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1336 else
1338 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1339 HostName);
1341 for (ap = socknamebuf; *ap; ap++)
1342 if (*ap == '/')
1343 *ap = '-';
1344 #ifdef NAME_MAX
1345 if (strlen(socknamebuf) > NAME_MAX)
1347 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1348 socknamebuf[NAME_MAX] = 0;
1350 #endif
1351 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1353 ServerSocket = MakeServerSocket();
1354 InitKeytab();
1356 #ifdef SCRIPT
1357 LoadBindings();
1358 if (script_file)
1360 ScriptSource(script_file);
1361 free(script_file);
1362 script_file = 0;
1364 #endif
1366 #ifdef ETCSCREENRC
1367 # ifdef ALLOW_SYSSCREENRC
1368 if ((ap = getenv("SYSSCREENRC")))
1369 (void)StartRc(ap, 0);
1370 else
1371 # endif
1372 (void)StartRc(ETCSCREENRC, 0);
1373 #endif
1374 (void)StartRc(RcFileName, 0);
1375 # ifdef UTMPOK
1376 # ifndef UTNOKEEP
1377 InitUtmp();
1378 # endif /* UTNOKEEP */
1379 # endif /* UTMPOK */
1380 if (display)
1382 if (InitTermcap(0, 0))
1384 debug("Could not init termcap - exiting\n");
1385 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1386 freetty();
1387 if (D_userpid)
1388 Kill(D_userpid, SIG_BYE);
1389 eexit(1);
1391 MakeDefaultCanvas();
1392 InitTerm(0);
1393 #ifdef UTMPOK
1394 RemoveLoginSlot();
1395 #endif
1397 else
1398 MakeTermcap(1);
1399 #ifdef LOADAV
1400 InitLoadav();
1401 #endif /* LOADAV */
1402 MakeNewEnv();
1403 signal(SIGHUP, SigHup);
1404 signal(SIGINT, FinitHandler);
1405 signal(SIGQUIT, FinitHandler);
1406 signal(SIGTERM, FinitHandler);
1407 #ifdef BSDJOBS
1408 signal(SIGTTIN, SIG_IGN);
1409 signal(SIGTTOU, SIG_IGN);
1410 #endif
1412 if (display)
1414 brktty(D_userfd);
1415 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1416 /* Note: SetMode must be called _before_ FinishRc. */
1417 SetTTY(D_userfd, &D_NewMode);
1418 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1419 Msg(errno, "Warning: NBLOCK fcntl failed");
1421 else
1422 brktty(-1); /* just try */
1423 signal(SIGCHLD, SigChld);
1424 #ifdef ETCSCREENRC
1425 # ifdef ALLOW_SYSSCREENRC
1426 if ((ap = getenv("SYSSCREENRC")))
1427 FinishRc(ap);
1428 else
1429 # endif
1430 FinishRc(ETCSCREENRC);
1431 #endif
1432 FinishRc(RcFileName);
1434 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1435 if (windows == NULL)
1437 debug("We open one default window, as screenrc did not specify one.\n");
1438 if (MakeWindow(&nwin) == -1)
1440 fd_set rfd;
1441 struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
1442 FD_SET(0, &rfd);
1444 Msg(0, "Sorry, could not find a PTY or TTY.");
1445 // allow user to exit early by pressing any key.
1446 select(1, &rfd, NULL, NULL, &tv);
1447 Finit(0);
1448 /* NOTREACHED */
1451 else if (ac) /* Screen was invoked with a command */
1453 MakeWindow(&nwin);
1456 #ifdef HAVE_BRAILLE
1457 StartBraille();
1458 #endif
1460 if (display && default_startup)
1461 display_copyright();
1462 signal(SIGINT, SigInt);
1463 if (rflag && (rflag & 1) == 0 && !quietflag)
1465 Msg(0, "New screen...");
1466 rflag = 0;
1469 serv_read.type = EV_READ;
1470 serv_read.fd = ServerSocket;
1471 serv_read.handler = serv_read_fn;
1472 evenq(&serv_read);
1474 serv_select.pri = -10;
1475 serv_select.type = EV_ALWAYS;
1476 serv_select.handler = serv_select_fn;
1477 evenq(&serv_select);
1479 logflushev.type = EV_TIMEOUT;
1480 logflushev.handler = logflush_fn;
1482 sched();
1483 /* NOTREACHED */
1484 return 0;
1487 void
1488 WindowDied(p, wstat, wstat_valid)
1489 struct win *p;
1490 #ifdef BSDWAIT
1491 union wait wstat;
1492 #else
1493 int wstat;
1494 #endif
1495 int wstat_valid;
1497 int killit = 0;
1499 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1500 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1501 killit = 1;
1503 if (ZombieKey_destroy && !killit)
1505 char buf[100], *s, reason[100];
1506 time_t now;
1508 if (wstat_valid) {
1509 if (WIFEXITED(wstat))
1510 if (WEXITSTATUS(wstat))
1511 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1512 else
1513 sprintf(reason, "terminated normally");
1514 else if (WIFSIGNALED(wstat))
1515 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1516 #ifdef WCOREDUMP
1517 WCOREDUMP(wstat) ? " (core file generated)" : "");
1518 #else
1519 "");
1520 #endif
1521 } else
1522 sprintf(reason, "detached from window");
1524 (void) time(&now);
1525 s = ctime(&now);
1526 if (s && *s)
1527 s[strlen(s) - 1] = '\0';
1528 debug3("window %d (%s) going into zombie state fd %d",
1529 p->w_number, p->w_title, p->w_ptyfd);
1530 #ifdef UTMPOK
1531 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1533 RemoveUtmp(p);
1534 p->w_slot = 0; /* "detached" */
1536 #endif
1537 CloseDevice(p);
1539 p->w_deadpid = p->w_pid;
1540 p->w_pid = 0;
1541 ResetWindow(p);
1542 /* p->w_y = p->w_bot; */
1543 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1544 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1545 WriteString(p, buf, strlen(buf));
1546 WindowChanged(p, 'f');
1548 else
1549 KillWindow(p);
1550 #ifdef UTMPOK
1551 CarefulUtmp();
1552 #endif
1555 static void
1556 SigChldHandler()
1558 struct stat st;
1559 #ifdef DEBUG
1560 fds();
1561 #endif
1562 while (GotSigChld)
1564 GotSigChld = 0;
1565 DoWait();
1566 #ifdef SYSVSIGS
1567 signal(SIGCHLD, SigChld);
1568 #endif
1570 if (stat(SockPath, &st) == -1)
1572 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1573 if (!RecoverSocket())
1575 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1576 Finit(1);
1578 else
1579 debug1("'%s' reconstructed\n", SockPath);
1581 else
1582 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1585 static sigret_t
1586 SigChld SIGDEFARG
1588 debug("SigChld()\n");
1589 GotSigChld = 1;
1590 SIGRETURN;
1593 sigret_t
1594 SigHup SIGDEFARG
1596 /* Hangup all displays */
1597 while ((display = displays) != 0)
1598 Hangup();
1599 SIGRETURN;
1603 * the backend's Interrupt handler
1604 * we cannot insert the intrc directly, as we never know
1605 * if fore is valid.
1607 static sigret_t
1608 SigInt SIGDEFARG
1610 #if HAZARDOUS
1611 char ibuf;
1613 debug("SigInt()\n");
1614 if (fore && displays)
1616 # if defined(TERMIO) || defined(POSIX)
1617 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1618 # else
1619 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1620 # endif
1621 fore->w_inlen = 0;
1622 write(fore->w_ptyfd, &ibuf, 1);
1624 #else
1625 signal(SIGINT, SigInt);
1626 debug("SigInt() careful\n");
1627 InterruptPlease = 1;
1628 #endif
1629 SIGRETURN;
1632 static sigret_t
1633 CoreDump SIGDEFARG
1635 struct display *disp;
1636 char buf[80];
1638 #if defined(SYSVSIGS) && defined(SIGHASARG)
1639 signal(sigsig, SIG_IGN);
1640 #endif
1641 setgid(getgid());
1642 setuid(getuid());
1643 unlink("core");
1644 #ifdef SIGHASARG
1645 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1646 #else
1647 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1648 #endif
1649 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1651 #else /* SHADOWPW && !DEBUG */
1652 " (core dumped)"
1653 #endif /* SHADOWPW && !DEBUG */
1655 for (disp = displays; disp; disp = disp->d_next)
1657 fcntl(disp->d_userfd, F_SETFL, 0);
1658 SetTTY(disp->d_userfd, &D_OldMode);
1659 write(disp->d_userfd, buf, strlen(buf));
1660 Kill(disp->d_userpid, SIG_BYE);
1662 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1663 Kill(getpid(), SIGKILL);
1664 eexit(11);
1665 #else /* SHADOWPW && !DEBUG */
1666 abort();
1667 #endif /* SHADOWPW && !DEBUG */
1668 SIGRETURN;
1671 static void
1672 DoWait()
1674 register int pid;
1675 struct win *p, *next;
1676 #ifdef BSDWAIT
1677 union wait wstat;
1678 #else
1679 int wstat;
1680 #endif
1682 #ifdef BSDJOBS
1683 # ifndef BSDWAIT
1684 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1685 # else
1686 # ifdef USE_WAIT2
1688 * From: rouilj@sni-usa.com (John Rouillard)
1689 * note that WUNTRACED is not documented to work, but it is defined in
1690 * /usr/include/sys/wait.h, so it may work
1692 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1693 # else /* USE_WAIT2 */
1694 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1695 # endif /* USE_WAIT2 */
1696 # endif
1697 #else /* BSDJOBS */
1698 while ((pid = wait(&wstat)) < 0)
1699 if (errno != EINTR)
1700 break;
1701 if (pid > 0)
1702 #endif /* BSDJOBS */
1704 for (p = windows; p; p = next)
1706 next = p->w_next;
1707 if ( (p->w_pid && pid == p->w_pid) ||
1708 (p->w_deadpid && pid == p->w_deadpid) )
1710 /* child has ceased to exist */
1711 p->w_pid = 0;
1713 #ifdef BSDJOBS
1714 if (WIFSTOPPED(wstat))
1716 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1717 #ifdef SIGTTIN
1718 if (WSTOPSIG(wstat) == SIGTTIN)
1720 Msg(0, "Suspended (tty input)");
1721 continue;
1723 #endif
1724 #ifdef SIGTTOU
1725 if (WSTOPSIG(wstat) == SIGTTOU)
1727 Msg(0, "Suspended (tty output)");
1728 continue;
1730 #endif
1731 /* Try to restart process */
1732 Msg(0, "Child has been stopped, restarting.");
1733 if (killpg(pid, SIGCONT))
1734 kill(pid, SIGCONT);
1736 else
1737 #endif
1739 WindowDied(p, wstat, 1);
1741 break;
1743 #ifdef PSEUDOS
1744 if (p->w_pwin && pid == p->w_pwin->p_pid)
1746 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1747 FreePseudowin(p);
1748 break;
1750 #endif
1752 if (p == 0)
1754 debug1("pid %d not found - hope that's ok\n", pid);
1760 static sigret_t
1761 FinitHandler SIGDEFARG
1763 #ifdef SIGHASARG
1764 debug1("FinitHandler called, sig %d.\n", sigsig);
1765 #else
1766 debug("FinitHandler called.\n");
1767 #endif
1768 Finit(1);
1769 SIGRETURN;
1772 void
1773 Finit(i)
1774 int i;
1776 signal(SIGCHLD, SIG_DFL);
1777 signal(SIGHUP, SIG_IGN);
1778 debug1("Finit(%d);\n", i);
1780 #ifdef SCRIPT
1781 FinalizeBindings();
1782 #endif
1784 while (windows)
1786 struct win *p = windows;
1787 windows = windows->w_next;
1788 FreeWindow(p);
1790 if (ServerSocket != -1)
1792 debug1("we unlink(%s)\n", SockPath);
1793 #ifdef USE_SETEUID
1794 xseteuid(real_uid);
1795 xsetegid(real_gid);
1796 #endif
1797 (void) unlink(SockPath);
1798 #ifdef USE_SETEUID
1799 xseteuid(eff_uid);
1800 xsetegid(eff_gid);
1801 #endif
1803 for (display = displays; display; display = display->d_next)
1805 if (D_status)
1806 RemoveStatus();
1807 FinitTerm();
1808 #ifdef UTMPOK
1809 RestoreLoginSlot();
1810 #endif
1811 AddStr("[screen is terminating]\r\n");
1812 Flush();
1813 SetTTY(D_userfd, &D_OldMode);
1814 fcntl(D_userfd, F_SETFL, 0);
1815 freetty();
1816 Kill(D_userpid, SIG_BYE);
1819 * we _cannot_ call eexit(i) here,
1820 * instead of playing with the Socket above. Sigh.
1822 exit(i);
1825 void
1826 eexit(e)
1827 int e;
1829 debug("eexit\n");
1830 if (ServerSocket != -1)
1832 debug1("we unlink(%s)\n", SockPath);
1833 setgid(real_gid);
1834 setuid(real_uid);
1835 (void) unlink(SockPath);
1837 exit(e);
1840 void
1841 Hangup()
1843 if (display == 0)
1844 return;
1845 debug1("Hangup %x\n", display);
1846 if (D_userfd >= 0)
1848 close(D_userfd);
1849 D_userfd = -1;
1851 if (auto_detach || displays->d_next)
1852 Detach(D_HANGUP);
1853 else
1854 Finit(0);
1858 * Detach now has the following modes:
1859 *D_DETACH SIG_BYE detach backend and exit attacher
1860 *D_HANGUP SIG_BYE detach backend and exit attacher
1861 *D_STOP SIG_STOP stop attacher (and detach backend)
1862 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1863 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1864 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1865 *D_LOCK SIG_LOCK lock the attacher
1866 * (jw)
1867 * we always remove our utmp slots. (even when "lock" or "stop")
1868 * Note: Take extra care here, we may be called by interrupt!
1870 void
1871 Detach(mode)
1872 int mode;
1874 int sign = 0, pid;
1875 struct canvas *cv;
1876 struct win *p;
1878 if (display == 0)
1879 return;
1881 #define AddStrSock(msg) do { \
1882 if (SockName) \
1884 AddStr("[" msg " from "); \
1885 AddStr(SockName); \
1886 AddStr("]\r\n"); \
1888 else \
1889 AddStr("[" msg "]\r\n"); \
1890 } while (0)
1892 signal(SIGHUP, SIG_IGN);
1893 debug1("Detach(%d)\n", mode);
1894 if (D_status)
1895 RemoveStatus();
1896 FinitTerm();
1897 if (!display)
1898 return;
1899 switch (mode)
1901 case D_HANGUP:
1902 sign = SIG_BYE;
1903 break;
1904 case D_DETACH:
1905 AddStrSock("detached");
1906 sign = SIG_BYE;
1907 #ifdef SCRIPT
1908 trigger_sevent(&globalevents.detached, display, 0x0);
1909 #endif
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 #ifdef SCRIPT
1921 trigger_sevent(&globalevents.detached, display, 0x1);
1922 #endif
1923 break;
1924 #endif
1925 #ifdef POW_DETACH
1926 case D_POWER:
1927 AddStrSock("power detached");
1928 if (PowDetachString)
1930 AddStr(PowDetachString);
1931 AddStr("\r\n");
1933 sign = SIG_POWER_BYE;
1934 #ifdef SCRIPT
1935 trigger_sevent(&globalevents.detached, display, 0x2);
1936 #endif
1937 break;
1938 #ifdef REMOTE_DETACH
1939 case D_REMOTE_POWER:
1940 AddStrSock("remote power detached");
1941 if (PowDetachString)
1943 AddStr(PowDetachString);
1944 AddStr("\r\n");
1946 sign = SIG_POWER_BYE;
1947 #ifdef SCRIPT
1948 trigger_sevent(&globalevents.detached, display, 0x1 | 0x2); /* Remote and power */
1949 #endif
1950 break;
1951 #endif
1952 #endif
1953 case D_LOCK:
1954 ClearAll();
1955 sign = SIG_LOCK;
1956 /* tell attacher to lock terminal with a lockprg. */
1957 break;
1959 #ifdef UTMPOK
1960 if (displays->d_next == 0)
1962 for (p = windows; p; p = p->w_next)
1964 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1966 RemoveUtmp(p);
1968 * Set the slot to 0 to get the window
1969 * logged in again.
1971 p->w_slot = (slot_t) 0;
1975 if (mode != D_HANGUP)
1976 RestoreLoginSlot();
1977 #endif
1978 if (displays->d_next == 0 && console_window)
1980 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1982 debug("could not release console - killing window\n");
1983 KillWindow(console_window);
1984 display = displays; /* restore display */
1987 if (D_fore)
1989 #ifdef MULTIUSER
1990 ReleaseAutoWritelock(display, D_fore);
1991 #endif
1992 D_user->u_detachwin = D_fore->w_number;
1993 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1995 AutosaveLayout(D_layout);
1996 layout_last = D_layout;
1997 for (cv = D_cvlist; cv; cv = cv->c_next)
1999 p = Layer2Window(cv->c_layer);
2000 SetCanvasWindow(cv, 0);
2001 if (p)
2002 WindowChanged(p, 'u');
2005 pid = D_userpid;
2006 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
2007 FreeDisplay();
2008 if (displays == 0)
2009 /* Flag detached-ness */
2010 (void) chsock();
2012 * tell father what to do. We do that after we
2013 * freed the tty, thus getty feels more comfortable on hpux
2014 * if it was a power detach.
2016 Kill(pid, sign);
2017 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
2018 debug("Detach returns, we are successfully detached.\n");
2019 signal(SIGHUP, SigHup);
2020 #undef AddStrSock
2023 static int
2024 IsSymbol(e, s)
2025 char *e, *s;
2027 register int l;
2029 l = strlen(s);
2030 return strncmp(e, s, l) == 0 && e[l] == '=';
2033 void
2034 MakeNewEnv()
2036 register char **op, **np;
2037 static char stybuf[MAXSTR];
2039 for (op = environ; *op; ++op)
2041 if (NewEnv)
2042 free((char *)NewEnv);
2043 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
2044 if (!NewEnv)
2045 Panic(0, strnomem);
2046 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
2047 *np++ = stybuf; /* NewEnv[0] */
2048 *np++ = Term; /* NewEnv[1] */
2049 np++; /* room for SHELL */
2050 #ifdef TIOCSWINSZ
2051 np += 2; /* room for TERMCAP and WINDOW */
2052 #else
2053 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2054 #endif
2056 for (op = environ; *op; ++op)
2058 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2059 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2060 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2061 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
2063 *np++ = *op;
2065 *np = 0;
2068 void
2069 /*VARARGS2*/
2070 #if defined(USEVARARGS) && defined(__STDC__)
2071 Msg(int err, char *fmt, VA_DOTS)
2072 #else
2073 Msg(err, fmt, VA_DOTS)
2074 int err;
2075 char *fmt;
2076 VA_DECL
2077 #endif
2079 VA_LIST(ap)
2080 char buf[MAXPATHLEN*2];
2081 char *p = buf;
2083 VA_START(ap, fmt);
2084 fmt = DoNLS(fmt);
2085 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2086 VA_END(ap);
2087 if (err)
2089 p += strlen(p);
2090 *p++ = ':';
2091 *p++ = ' ';
2092 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2093 buf[sizeof(buf) - 1] = 0;
2095 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2097 if (display && displays)
2098 MakeStatus(buf);
2099 else if (displays)
2101 for (display = displays; display; display = display->d_next)
2102 MakeStatus(buf);
2104 else if (display)
2106 /* no displays but a display - must have forked.
2107 * send message to backend!
2109 char *tty = D_usertty;
2110 struct display *olddisplay = display;
2111 display = 0; /* only send once */
2112 SendErrorMsg(tty, buf);
2113 display = olddisplay;
2115 else
2116 printf("%s\r\n", buf);
2120 * Call FinitTerm for all displays, write a message to each and call eexit();
2122 void
2123 /*VARARGS2*/
2124 #if defined(USEVARARGS) && defined(__STDC__)
2125 Panic(int err, char *fmt, VA_DOTS)
2126 #else
2127 Panic(err, fmt, VA_DOTS)
2128 int err;
2129 char *fmt;
2130 VA_DECL
2131 #endif
2133 VA_LIST(ap)
2134 char buf[MAXPATHLEN*2];
2135 char *p = buf;
2137 VA_START(ap, fmt);
2138 fmt = DoNLS(fmt);
2139 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2140 VA_END(ap);
2141 if (err)
2143 p += strlen(p);
2144 *p++ = ':';
2145 *p++ = ' ';
2146 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2147 buf[sizeof(buf) - 1] = 0;
2149 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2150 if (displays == 0 && display == 0)
2152 printf("%s\r\n", buf);
2153 if (PanicPid)
2154 Kill(PanicPid, SIG_BYE);
2156 else if (displays == 0)
2158 /* no displays but a display - must have forked.
2159 * send message to backend!
2161 char *tty = D_usertty;
2162 display = 0;
2163 SendErrorMsg(tty, buf);
2164 sleep(2);
2165 _exit(1);
2167 else
2168 for (display = displays; display; display = display->d_next)
2170 if (D_status)
2171 RemoveStatus();
2172 FinitTerm();
2173 Flush();
2174 #ifdef UTMPOK
2175 RestoreLoginSlot();
2176 #endif
2177 SetTTY(D_userfd, &D_OldMode);
2178 fcntl(D_userfd, F_SETFL, 0);
2179 write(D_userfd, buf, strlen(buf));
2180 write(D_userfd, "\n", 1);
2181 freetty();
2182 if (D_userpid)
2183 Kill(D_userpid, SIG_BYE);
2185 #ifdef MULTIUSER
2186 if (tty_oldmode >= 0)
2188 # ifdef USE_SETEUID
2189 if (setuid(own_uid))
2190 xseteuid(own_uid); /* may be a loop. sigh. */
2191 # else
2192 setuid(own_uid);
2193 # endif
2194 debug1("Panic: changing back modes from %s\n", attach_tty);
2195 chmod(attach_tty, tty_oldmode);
2197 #endif
2198 eexit(1);
2203 * '^' is allowed as an escape mechanism for control characters. jw.
2205 * Added time insertion using ideas/code from /\ndy Jones
2206 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2210 #ifndef USE_LOCALE
2211 static const char days[] = "SunMonTueWedThuFriSat";
2212 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2213 #endif
2215 static char winmsg_buf[MAXSTR];
2216 #define MAX_WINMSG_REND 16 /* rendition changes */
2217 static int winmsg_rend[MAX_WINMSG_REND];
2218 static int winmsg_rendpos[MAX_WINMSG_REND];
2219 static int winmsg_numrend;
2221 static char *
2222 pad_expand(buf, p, numpad, padlen)
2223 char *buf;
2224 char *p;
2225 int numpad;
2226 int padlen;
2228 char *pn, *pn2;
2229 int i, r;
2231 padlen = padlen - (p - buf); /* space for rent */
2232 if (padlen < 0)
2233 padlen = 0;
2234 pn2 = pn = p + padlen;
2235 r = winmsg_numrend;
2236 while (p >= buf)
2238 if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
2240 winmsg_rendpos[--r] = pn - buf;
2241 continue;
2243 *pn-- = *p;
2244 if (*p-- == 127)
2246 pn[1] = ' ';
2247 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2248 padlen -= i;
2249 while (i-- > 0)
2250 *pn-- = ' ';
2251 numpad--;
2252 if (r && p - buf == winmsg_rendpos[r - 1])
2253 winmsg_rendpos[--r] = pn - buf;
2256 return pn2;
2259 struct backtick {
2260 struct backtick *next;
2261 int num;
2262 int tick;
2263 int lifespan;
2264 time_t bestbefore;
2265 char result[MAXSTR];
2266 char **cmdv;
2267 struct event ev;
2268 char *buf;
2269 int bufi;
2272 struct backtick *backticks;
2274 static void
2275 backtick_filter(bt)
2276 struct backtick *bt;
2278 char *p, *q;
2279 int c;
2281 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2283 if (c == '\t')
2284 c = ' ';
2285 if (c >= ' ' || c == '\005')
2286 *q++ = c;
2288 *q = 0;
2291 static void
2292 backtick_fn(ev, data)
2293 struct event *ev;
2294 char *data;
2296 struct backtick *bt;
2297 int i, j, k, l;
2299 bt = (struct backtick *)data;
2300 debug1("backtick_fn for #%d\n", bt->num);
2301 i = bt->bufi;
2302 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2303 if (l <= 0)
2305 debug1("EOF on backtick #%d\n", bt->num);
2306 evdeq(ev);
2307 close(ev->fd);
2308 ev->fd = -1;
2309 return;
2311 debug1("read %d bytes\n", l);
2312 i += l;
2313 for (j = 0; j < l; j++)
2314 if (bt->buf[i - j - 1] == '\n')
2315 break;
2316 if (j < l)
2318 for (k = i - j - 2; k >= 0; k--)
2319 if (bt->buf[k] == '\n')
2320 break;
2321 k++;
2322 bcopy(bt->buf + k, bt->result, i - j - k);
2323 bt->result[i - j - k - 1] = 0;
2324 backtick_filter(bt);
2325 WindowChanged(0, '`');
2327 if (j == l && i == MAXSTR)
2329 j = MAXSTR/2;
2330 l = j + 1;
2332 if (j < l)
2334 if (j)
2335 bcopy(bt->buf + i - j, bt->buf, j);
2336 i = j;
2338 bt->bufi = i;
2341 void
2342 setbacktick(num, lifespan, tick, cmdv)
2343 int num;
2344 int lifespan;
2345 int tick;
2346 char **cmdv;
2348 struct backtick **btp, *bt;
2349 char **v;
2351 debug1("setbacktick called for backtick #%d\n", num);
2352 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2353 if (bt->num == num)
2354 break;
2355 if (!bt && !cmdv)
2356 return;
2357 if (bt)
2359 for (v = bt->cmdv; *v; v++)
2360 free(*v);
2361 free(bt->cmdv);
2362 if (bt->buf)
2363 free(bt->buf);
2364 if (bt->ev.fd >= 0)
2365 close(bt->ev.fd);
2366 evdeq(&bt->ev);
2368 if (bt && !cmdv)
2370 *btp = bt->next;
2371 free(bt);
2372 return;
2374 if (!bt)
2376 bt = (struct backtick *)malloc(sizeof *bt);
2377 if (!bt)
2379 Msg(0, strnomem);
2380 return;
2382 bzero(bt, sizeof(*bt));
2383 bt->next = 0;
2384 *btp = bt;
2386 bt->num = num;
2387 bt->tick = tick;
2388 bt->lifespan = lifespan;
2389 bt->bestbefore = 0;
2390 bt->result[0] = 0;
2391 bt->buf = 0;
2392 bt->bufi = 0;
2393 bt->cmdv = cmdv;
2394 bt->ev.fd = -1;
2395 if (bt->tick == 0 && bt->lifespan == 0)
2397 debug("setbacktick: continuous mode\n");
2398 bt->buf = (char *)malloc(MAXSTR);
2399 if (bt->buf == 0)
2401 Msg(0, strnomem);
2402 setbacktick(num, 0, 0, (char **)0);
2403 return;
2405 bt->ev.type = EV_READ;
2406 bt->ev.fd = readpipe(bt->cmdv);
2407 bt->ev.handler = backtick_fn;
2408 bt->ev.data = (char *)bt;
2409 if (bt->ev.fd >= 0)
2410 evenq(&bt->ev);
2414 static char *
2415 runbacktick(bt, tickp, now)
2416 struct backtick *bt;
2417 int *tickp;
2418 time_t now;
2420 int f, i, l, j;
2421 time_t now2;
2423 debug1("runbacktick called for backtick #%d\n", bt->num);
2424 if (bt->tick && (!*tickp || bt->tick < *tickp))
2425 *tickp = bt->tick;
2426 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2428 debug1("returning old result (%d)\n", bt->lifespan);
2429 return bt->result;
2431 f = readpipe(bt->cmdv);
2432 if (f == -1)
2433 return bt->result;
2434 i = 0;
2435 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2437 debug1("runbacktick: read %d bytes\n", l);
2438 i += l;
2439 for (j = 1; j < l; j++)
2440 if (bt->result[i - j - 1] == '\n')
2441 break;
2442 if (j == l && i == sizeof(bt->result))
2444 j = sizeof(bt->result) / 2;
2445 l = j + 1;
2447 if (j < l)
2449 bcopy(bt->result + i - j, bt->result, j);
2450 i = j;
2453 close(f);
2454 bt->result[sizeof(bt->result) - 1] = '\n';
2455 if (i && bt->result[i - 1] == '\n')
2456 i--;
2457 debug1("runbacktick: finished, %d bytes\n", i);
2458 bt->result[i] = 0;
2459 backtick_filter(bt);
2460 (void)time(&now2);
2461 bt->bestbefore = now2 + bt->lifespan;
2462 return bt->result;
2465 void
2466 AppendWinMsgRend(str, color)
2467 char *str, *color;
2469 char *p;
2470 int r = -1;
2471 if (winmsg_numrend >= MAX_WINMSG_REND)
2472 return;
2473 p = winmsg_buf + strlen(winmsg_buf);
2474 if (color)
2476 if (*color != '-')
2478 r = ParseAttrColor(color, 0, 0);
2479 if (r == -1)
2480 return;
2482 winmsg_rend[winmsg_numrend] = r;
2483 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2484 winmsg_numrend++;
2486 strncpy(p, str, winmsg_buf + sizeof(winmsg_buf) - p);
2490 AddWinMsgRend(str, r)
2491 const char *str;
2492 int r;
2494 if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf ||
2495 str >= winmsg_buf + MAXSTR)
2496 return -1;
2498 winmsg_rend[winmsg_numrend] = r;
2499 winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
2500 winmsg_numrend++;
2502 return 0;
2505 char *
2506 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2507 char *str;
2508 struct win *win;
2509 int esc;
2510 int padlen;
2511 struct event *ev;
2512 int rec;
2514 static int tick;
2515 char *s = str;
2516 register char *p = winmsg_buf;
2517 register int ctrl;
2518 struct timeval now;
2519 struct tm *tm;
2520 int l, i, r;
2521 int num;
2522 int zeroflg;
2523 int longflg;
2524 int minusflg;
2525 int plusflg;
2526 int qmflag = 0, omflag = 0, qmnumrend = 0;
2527 char *qmpos = 0;
2528 int numpad = 0;
2529 int lastpad = 0;
2530 int truncpos = -1;
2531 int truncper = 0;
2532 int trunclong = 0;
2533 struct backtick *bt;
2535 if (winmsg_numrend >= 0)
2536 winmsg_numrend = 0;
2537 else
2538 winmsg_numrend = -winmsg_numrend;
2540 *p = '\0';
2541 if (!display)
2542 return winmsg_buf;
2544 tick = 0;
2545 tm = 0;
2546 ctrl = 0;
2547 gettimeofday(&now, NULL);
2548 for (s = str; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2550 *p = *s;
2551 if (ctrl)
2553 ctrl = 0;
2554 if (*s != '^' && *s >= 64)
2555 *p &= 0x1f;
2556 continue;
2558 if (*s != esc)
2560 if (esc == '%')
2562 switch (*s)
2564 #if 0
2565 case '~':
2566 *p = BELL;
2567 break;
2568 #endif
2569 case '^':
2570 ctrl = 1;
2571 *p-- = '^';
2572 break;
2573 default:
2574 break;
2577 continue;
2579 if (*++s == esc) /* double escape ? */
2580 continue;
2581 if ((plusflg = *s == '+') != 0)
2582 s++;
2583 if ((minusflg = *s == '-') != 0)
2584 s++;
2585 if ((zeroflg = *s == '0') != 0)
2586 s++;
2587 num = 0;
2588 while(*s >= '0' && *s <= '9')
2589 num = num * 10 + (*s++ - '0');
2590 if ((longflg = *s == 'L') != 0)
2591 s++;
2592 switch (*s)
2594 case '?':
2595 p--;
2596 if (qmpos)
2598 if ((!qmflag && !omflag) || omflag == 1)
2600 p = qmpos;
2601 if (qmnumrend < winmsg_numrend)
2602 winmsg_numrend = qmnumrend;
2604 qmpos = 0;
2605 break;
2607 qmpos = p;
2608 qmnumrend = winmsg_numrend;
2609 qmflag = omflag = 0;
2610 break;
2611 case ':':
2612 p--;
2613 if (!qmpos)
2614 break;
2615 if (qmflag && omflag != 1)
2617 omflag = 1;
2618 qmpos = p;
2619 qmnumrend = winmsg_numrend;
2621 else
2623 p = qmpos;
2624 if (qmnumrend < winmsg_numrend)
2625 winmsg_numrend = qmnumrend;
2626 omflag = -1;
2628 break;
2629 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2630 case 'a': case 'A': case 's': case 'c': case 'C':
2631 if (l < 4)
2632 break;
2633 if (tm == 0)
2635 time_t nowsec = now.tv_sec;
2636 tm = localtime(&nowsec);
2638 qmflag = 1;
2639 if (!tick || tick > 3600)
2640 tick = 3600;
2641 switch (*s)
2643 case 'd':
2644 sprintf(p, "%02d", tm->tm_mday % 100);
2645 break;
2646 case 'D':
2647 #ifdef USE_LOCALE
2648 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2649 #else
2650 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2651 #endif
2652 break;
2653 case 'm':
2654 sprintf(p, "%02d", tm->tm_mon + 1);
2655 break;
2656 case 'M':
2657 #ifdef USE_LOCALE
2658 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2659 #else
2660 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2661 #endif
2662 break;
2663 case 'y':
2664 sprintf(p, "%02d", tm->tm_year % 100);
2665 break;
2666 case 'Y':
2667 sprintf(p, "%04d", tm->tm_year + 1900);
2668 break;
2669 case 'a':
2670 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2671 break;
2672 case 'A':
2673 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2674 break;
2675 case 's':
2676 sprintf(p, "%02d", tm->tm_sec);
2677 tick = 1;
2678 break;
2679 case 'c':
2680 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2681 if (!tick || tick > 60)
2682 tick = 60;
2683 break;
2684 case 'C':
2685 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2686 if (!tick || tick > 60)
2687 tick = 60;
2688 break;
2689 default:
2690 break;
2692 p += strlen(p) - 1;
2693 break;
2694 case 'l':
2695 #ifdef LOADAV
2696 *p = 0;
2697 if (l > 20)
2698 AddLoadav(p);
2699 if (*p)
2701 qmflag = 1;
2702 p += strlen(p) - 1;
2704 else
2705 *p = '?';
2706 if (!tick || tick > 60)
2707 tick = 60;
2708 #else
2709 *p = '?';
2710 #endif
2711 p += strlen(p) - 1;
2712 break;
2713 case '`':
2714 case 'h':
2715 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2717 p--;
2718 break;
2720 if (*s == '`')
2722 for (bt = backticks; bt; bt = bt->next)
2723 if (bt->num == num)
2724 break;
2725 if (bt == 0)
2727 p--;
2728 break;
2732 char savebuf[sizeof(winmsg_buf)];
2733 int oldtick = tick;
2734 int oldnumrend = winmsg_numrend;
2736 *p = 0;
2737 strcpy(savebuf, winmsg_buf);
2738 winmsg_numrend = -winmsg_numrend;
2739 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2740 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2741 if (!tick || oldtick < tick)
2742 tick = oldtick;
2743 if ((int)strlen(winmsg_buf) < l)
2744 strcat(savebuf, winmsg_buf);
2745 strcpy(winmsg_buf, savebuf);
2746 while (oldnumrend < winmsg_numrend)
2747 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2748 if (*p)
2749 qmflag = 1;
2750 p += strlen(p) - 1;
2752 break;
2753 case 'w':
2754 case 'W':
2756 struct win *oldfore = 0;
2757 char *ss;
2759 if (display)
2761 oldfore = D_fore;
2762 D_fore = win;
2764 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1);
2765 if (display)
2766 D_fore = oldfore;
2768 if (*p)
2769 qmflag = 1;
2770 p += strlen(p) - 1;
2771 break;
2772 case 'u':
2773 *p = 0;
2774 if (win)
2775 AddOtherUsers(p, l - 1, win);
2776 if (*p)
2777 qmflag = 1;
2778 p += strlen(p) - 1;
2779 break;
2780 case 'f':
2781 *p = 0;
2782 if (win)
2783 AddWindowFlags(p, l - 1, win);
2784 if (*p)
2785 qmflag = 1;
2786 p += strlen(p) - 1;
2787 break;
2788 case 't':
2789 *p = 0;
2790 if (win && (int)strlen(win->w_title) < l)
2792 strcpy(p, win->w_title);
2793 if (*p)
2794 qmflag = 1;
2796 p += strlen(p) - 1;
2797 break;
2798 case '{':
2800 char rbuf[128];
2801 s++;
2802 for (i = 0; i < 127; i++)
2803 if (s[i] && s[i] != '}')
2804 rbuf[i] = s[i];
2805 else
2806 break;
2807 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2809 r = -1;
2810 rbuf[i] = 0;
2811 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2812 if (i != 1 || rbuf[0] != '-')
2813 r = ParseAttrColor(rbuf, (char *)0, 0);
2814 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2816 winmsg_rend[winmsg_numrend] = r;
2817 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2818 winmsg_numrend++;
2821 s += i;
2822 p--;
2824 break;
2825 case 'H':
2826 *p = 0;
2827 if ((int)strlen(HostName) < l)
2829 strcpy(p, HostName);
2830 if (*p)
2831 qmflag = 1;
2833 p += strlen(p) - 1;
2834 break;
2835 case 'S':
2837 char *session_name;
2838 *p = 0;
2839 session_name = strchr(SockName, '.') + 1;
2840 if ((int)strlen(session_name) < l)
2842 strcpy(p, session_name);
2843 if (*p)
2844 qmflag = 1;
2846 p += strlen(p) - 1;
2848 break;
2849 case 'p':
2851 sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2852 p += strlen(p) - 1;
2854 break;
2855 case 'F':
2856 p--;
2857 /* small hack */
2858 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2859 minusflg = !minusflg;
2860 if (minusflg)
2861 qmflag = 1;
2862 break;
2863 case '>':
2864 truncpos = p - winmsg_buf;
2865 truncper = num > 100 ? 100 : num;
2866 trunclong = longflg;
2867 p--;
2868 break;
2869 case '=':
2870 case '<':
2871 *p = ' ';
2872 if (num || zeroflg || plusflg || longflg || (*s != '='))
2874 /* expand all pads */
2875 if (minusflg)
2877 num = (plusflg ? lastpad : padlen) - num;
2878 if (!plusflg && padlen == 0)
2879 num = p - winmsg_buf;
2880 plusflg = 0;
2882 else if (!zeroflg)
2884 if (*s != '=' && num == 0 && !plusflg)
2885 num = 100;
2886 if (num > 100)
2887 num = 100;
2888 if (padlen == 0)
2889 num = p - winmsg_buf;
2890 else
2891 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2893 if (num < 0)
2894 num = 0;
2895 if (plusflg)
2896 num += lastpad;
2897 if (num > MAXSTR - 1)
2898 num = MAXSTR - 1;
2899 if (numpad)
2900 p = pad_expand(winmsg_buf, p, numpad, num);
2901 numpad = 0;
2902 if (p - winmsg_buf > num && !longflg)
2904 int left, trunc;
2906 if (truncpos == -1)
2908 truncpos = lastpad;
2909 truncper = 0;
2911 trunc = lastpad + truncper * (num - lastpad) / 100;
2912 if (trunc > num)
2913 trunc = num;
2914 if (trunc < lastpad)
2915 trunc = lastpad;
2916 left = truncpos - trunc;
2917 if (left > p - winmsg_buf - num)
2918 left = p - winmsg_buf - num;
2919 debug1("lastpad = %d, ", lastpad);
2920 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2921 if (left > 0)
2923 if (left + lastpad > p - winmsg_buf)
2924 left = p - winmsg_buf - lastpad;
2925 if (p - winmsg_buf - lastpad - left > 0)
2926 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2927 p -= left;
2928 r = winmsg_numrend;
2929 while (r && winmsg_rendpos[r - 1] > lastpad)
2931 r--;
2932 winmsg_rendpos[r] -= left;
2933 if (winmsg_rendpos[r] < lastpad)
2934 winmsg_rendpos[r] = lastpad;
2936 if (trunclong)
2938 if (p - winmsg_buf > lastpad)
2939 winmsg_buf[lastpad] = '.';
2940 if (p - winmsg_buf > lastpad + 1)
2941 winmsg_buf[lastpad + 1] = '.';
2942 if (p - winmsg_buf > lastpad + 2)
2943 winmsg_buf[lastpad + 2] = '.';
2946 if (p - winmsg_buf > num)
2948 p = winmsg_buf + num;
2949 if (trunclong)
2951 if (num - 1 >= lastpad)
2952 p[-1] = '.';
2953 if (num - 2 >= lastpad)
2954 p[-2] = '.';
2955 if (num - 3 >= lastpad)
2956 p[-3] = '.';
2958 r = winmsg_numrend;
2959 while (r && winmsg_rendpos[r - 1] > num)
2960 winmsg_rendpos[--r] = num;
2962 truncpos = -1;
2963 trunclong = 0;
2964 if (lastpad > p - winmsg_buf)
2965 lastpad = p - winmsg_buf;
2966 debug1("lastpad now %d\n", lastpad);
2968 if (*s == '=')
2970 while (p - winmsg_buf < num)
2971 *p++ = ' ';
2972 lastpad = p - winmsg_buf;
2973 truncpos = -1;
2974 trunclong = 0;
2975 debug1("lastpad2 now %d\n", lastpad);
2977 p--;
2979 else if (padlen)
2981 *p = 127; /* internal pad representation */
2982 numpad++;
2984 break;
2985 case 'n':
2986 s++;
2987 /* FALLTHROUGH */
2988 default:
2989 s--;
2990 if (l > 10 + num)
2992 if (num == 0)
2993 num = 1;
2994 if (!win)
2995 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2996 else
2997 sprintf(p, "%*d", num, win->w_number);
2998 qmflag = 1;
2999 p += strlen(p) - 1;
3001 break;
3004 if (qmpos && !qmflag)
3005 p = qmpos + 1;
3006 *p = '\0';
3007 if (numpad)
3009 if (padlen > MAXSTR - 1)
3010 padlen = MAXSTR - 1;
3011 p = pad_expand(winmsg_buf, p, numpad, padlen);
3013 if (ev)
3015 evdeq(ev); /* just in case */
3016 ev->timeout.tv_sec = 0;
3017 ev->timeout.tv_usec = 0;
3019 if (ev && tick)
3021 now.tv_usec = 100000;
3022 if (tick == 1)
3023 now.tv_sec++;
3024 else
3025 now.tv_sec += tick - (now.tv_sec % tick);
3026 ev->timeout = now;
3027 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
3029 return winmsg_buf;
3032 char *
3033 MakeWinMsg(s, win, esc)
3034 char *s;
3035 struct win *win;
3036 int esc;
3038 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
3041 void
3042 PutWinMsg(s, start, max)
3043 char *s;
3044 int start, max;
3046 int i, p, l, r, n;
3047 struct mchar rend;
3048 struct mchar rendstack[MAX_WINMSG_REND];
3049 int rendstackn = 0;
3051 if (s != winmsg_buf)
3053 /* sorry, no fancy coloring available */
3054 debug1("PutWinMsg %s plain\n", s);
3055 l = strlen(s);
3056 if (l > max)
3057 l = max;
3058 l -= start;
3059 s += start;
3060 while (l-- > 0)
3061 PUTCHARLP(*s++);
3062 return;
3064 rend = D_rend;
3065 p = 0;
3066 l = strlen(s);
3067 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3068 for (i = 0; i < winmsg_numrend && max > 0; i++)
3070 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3071 break;
3072 if (p < winmsg_rendpos[i])
3074 n = winmsg_rendpos[i] - p;
3075 if (n > max)
3076 n = max;
3077 max -= n;
3078 p += n;
3079 while(n-- > 0)
3081 if (start-- > 0)
3082 s++;
3083 else
3084 PUTCHARLP(*s++);
3087 r = winmsg_rend[i];
3088 if (r == -1)
3090 if (rendstackn > 0)
3091 rend = rendstack[--rendstackn];
3093 else
3095 rendstack[rendstackn++] = rend;
3096 ApplyAttrColor(r, &rend);
3098 SetRendition(&rend);
3100 if (p < l)
3102 n = l - p;
3103 if (n > max)
3104 n = max;
3105 while(n-- > 0)
3107 if (start-- > 0)
3108 s++;
3109 else
3110 PUTCHARLP(*s++);
3116 #ifdef DEBUG
3117 static void
3118 fds1(i, j)
3119 int i, j;
3121 while (i < j)
3123 debug1("%d ", i);
3124 i++;
3126 if ((j = open("/dev/null", 0)) >= 0)
3128 fds1(i + 1, j);
3129 close(j);
3131 else
3133 while (dup(++i) < 0 && errno != EBADF)
3134 debug1("%d ", i);
3135 debug1(" [%d]\n", i);
3139 static void
3140 fds()
3142 debug("fds: ");
3143 fds1(-1, -1);
3145 #endif
3147 static void
3148 serv_read_fn(ev, data)
3149 struct event *ev;
3150 char *data;
3152 debug("Knock - knock!\n");
3153 ReceiveMsg();
3156 static void
3157 serv_select_fn(ev, data)
3158 struct event *ev;
3159 char *data;
3161 struct win *p;
3163 debug("serv_select_fn called\n");
3164 /* XXX: messages?? */
3165 if (GotSigChld)
3167 SigChldHandler();
3169 if (InterruptPlease)
3171 debug("Backend received interrupt\n");
3172 /* This approach is rather questionable in a multi-display
3173 * environment */
3174 if (fore && displays)
3176 #if defined(TERMIO) || defined(POSIX)
3177 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3178 #else
3179 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3180 #endif
3181 #ifdef PSEUDOS
3182 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3183 &ibuf, 1);
3184 debug1("Backend wrote interrupt to %d", fore->w_number);
3185 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3186 #else
3187 write(fore->w_ptyfd, &ibuf, 1);
3188 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3189 #endif
3191 InterruptPlease = 0;
3194 for (p = windows; p; p = p->w_next)
3196 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3198 struct canvas *cv;
3199 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3200 p->w_bell = BELL_ON;
3201 for (display = displays; display; display = display->d_next)
3203 for (cv = D_cvlist; cv; cv = cv->c_next)
3204 if (cv->c_layer->l_bottom == &p->w_layer)
3205 break;
3206 if (cv == 0)
3208 p->w_bell = BELL_DONE;
3209 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3211 else if (visual && !D_VB && (!D_status || !D_status_bell))
3213 Msg(0, "%s", VisualBellString);
3214 if (D_status)
3216 D_status_bell = 1;
3217 debug1("using vbell timeout %d\n", VBellWait);
3218 SetTimeout(&D_statusev, VBellWait );
3222 /* don't annoy the user with two messages */
3223 if (p->w_monitor == MON_FOUND)
3224 p->w_monitor = MON_DONE;
3225 WindowChanged(p, 'f');
3227 if (p->w_monitor == MON_FOUND)
3229 struct canvas *cv;
3230 p->w_monitor = MON_ON;
3231 for (display = displays; display; display = display->d_next)
3233 for (cv = D_cvlist; cv; cv = cv->c_next)
3234 if (cv->c_layer->l_bottom == &p->w_layer)
3235 break;
3236 if (cv)
3237 continue; /* user already sees window */
3238 #ifdef MULTIUSER
3239 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3240 continue; /* user doesn't care */
3241 #endif
3242 #ifdef SCRIPT
3243 if (!trigger_sevent(&p->w_sev.onactivity, display, p))
3244 #endif
3245 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3246 p->w_monitor = MON_DONE;
3248 WindowChanged(p, 'f');
3252 for (display = displays; display; display = display->d_next)
3254 struct canvas *cv;
3255 if (D_status == STATUS_ON_WIN)
3256 continue;
3257 /* XXX: should use display functions! */
3258 for (cv = D_cvlist; cv; cv = cv->c_next)
3260 int lx, ly;
3262 /* normalize window, see resize.c */
3263 lx = cv->c_layer->l_x;
3264 ly = cv->c_layer->l_y;
3265 if (lx == cv->c_layer->l_width)
3266 lx--;
3267 if (ly + cv->c_yoff < cv->c_ys)
3269 int i, n = cv->c_ys - (ly + cv->c_yoff);
3270 cv->c_yoff = cv->c_ys - ly;
3271 RethinkViewportOffsets(cv);
3272 if (n > cv->c_layer->l_height)
3273 n = cv->c_layer->l_height;
3274 CV_CALL(cv,
3275 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3276 LayRedisplayLine(-1, -1, -1, 1);
3277 for (i = 0; i < n; i++)
3278 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3279 if (cv == cv->c_display->d_forecv)
3280 LaySetCursor();
3283 else if (ly + cv->c_yoff > cv->c_ye)
3285 int i, n = ly + cv->c_yoff - cv->c_ye;
3286 cv->c_yoff = cv->c_ye - ly;
3287 RethinkViewportOffsets(cv);
3288 if (n > cv->c_layer->l_height)
3289 n = cv->c_layer->l_height;
3290 CV_CALL(cv,
3291 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3292 LayRedisplayLine(-1, -1, -1, 1);
3293 for (i = 0; i < n; i++)
3294 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3295 if (cv == cv->c_display->d_forecv)
3296 LaySetCursor();
3299 if (lx + cv->c_xoff < cv->c_xs)
3301 int i, n = cv->c_xs - (lx + cv->c_xoff);
3302 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3303 n = (cv->c_xe - cv->c_xs + 1) / 2;
3304 if (cv->c_xoff + n > cv->c_xs)
3305 n = cv->c_xs - cv->c_xoff;
3306 cv->c_xoff += n;
3307 RethinkViewportOffsets(cv);
3308 if (n > cv->c_layer->l_width)
3309 n = cv->c_layer->l_width;
3310 CV_CALL(cv,
3311 LayRedisplayLine(-1, -1, -1, 1);
3312 for (i = 0; i < flayer->l_height; i++)
3314 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3315 LayRedisplayLine(i, 0, n - 1, 1);
3317 if (cv == cv->c_display->d_forecv)
3318 LaySetCursor();
3321 else if (lx + cv->c_xoff > cv->c_xe)
3323 int i, n = lx + cv->c_xoff - cv->c_xe;
3324 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3325 n = (cv->c_xe - cv->c_xs + 1) / 2;
3326 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3327 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3328 cv->c_xoff -= n;
3329 RethinkViewportOffsets(cv);
3330 if (n > cv->c_layer->l_width)
3331 n = cv->c_layer->l_width;
3332 CV_CALL(cv,
3333 LayRedisplayLine(-1, -1, -1, 1);
3334 for (i = 0; i < flayer->l_height; i++)
3336 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3337 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3339 if (cv == cv->c_display->d_forecv)
3340 LaySetCursor();
3346 for (display = displays; display; display = display->d_next)
3348 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3349 continue;
3350 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3351 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3355 static void
3356 logflush_fn(ev, data)
3357 struct event *ev;
3358 char *data;
3360 struct win *p;
3361 char *buf;
3362 int n;
3364 if (!islogfile(NULL))
3365 return; /* no more logfiles */
3366 logfflush(NULL);
3367 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3368 if (n)
3370 SetTimeout(ev, n * 1000);
3371 evenq(ev); /* re-enqueue ourself */
3373 if (!logtstamp_on)
3374 return;
3375 /* write fancy time-stamp */
3376 for (p = windows; p; p = p->w_next)
3378 if (!p->w_log)
3379 continue;
3380 p->w_logsilence += n;
3381 if (p->w_logsilence < logtstamp_after)
3382 continue;
3383 if (p->w_logsilence - n >= logtstamp_after)
3384 continue;
3385 buf = MakeWinMsg(logtstamp_string, p, '%');
3386 logfwrite(p->w_log, buf, strlen(buf));
3391 * Interprets ^?, ^@ and other ^-control-char notation.
3392 * Interprets \ddd octal notation
3394 * The result is placed in *cp, p is advanced behind the parsed expression and
3395 * returned.
3397 static char *
3398 ParseChar(p, cp)
3399 char *p, *cp;
3401 if (*p == 0)
3402 return 0;
3403 if (*p == '^' && p[1])
3405 if (*++p == '?')
3406 *cp = '\177';
3407 else if (*p >= '@')
3408 *cp = Ctrl(*p);
3409 else
3410 return 0;
3411 ++p;
3413 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3415 *cp = 0;
3417 *cp = *cp * 8 + *p - '0';
3418 while (*++p <= '7' && *p >= '0');
3420 else
3421 *cp = *p++;
3422 return p;
3425 static int
3426 ParseEscape(p)
3427 char *p;
3429 unsigned char buf[2];
3431 if (*p == 0)
3432 SetEscape((struct acluser *)0, -1, -1);
3433 else
3435 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3436 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3437 return -1;
3438 SetEscape((struct acluser *)0, buf[0], buf[1]);
3440 return 0;