More fixes for -Werror=format-security.
[screen-lua.git] / src / screen.c
blob65c51bb28d8ce5da14cf1a485448683466dae9be
1 /* Copyright (c) 2010
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
4 * Copyright (c) 2008, 2009
5 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
6 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
7 * Micah Cowan (micah@cowan.name)
8 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
9 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
10 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
11 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
12 * Copyright (c) 1987 Oliver Laumann
13 #ifdef HAVE_BRAILLE
14 * Modified by:
15 * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu
16 * Bill Barry barryb@dots.physics.orst.edu
17 * Randy Lundquist randyl@dots.physics.orst.edu
19 * Modifications Copyright (c) 1995 by
20 * Science Access Project, Oregon State University.
21 #endif
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; either version 3, or (at your option)
26 * any later version.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License
34 * along with this program (see the file COPYING); if not, see
35 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
36 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
38 ****************************************************************
41 #include <sys/types.h>
42 #include <ctype.h>
44 #include <fcntl.h>
46 #ifdef sgi
47 # include <sys/sysmacros.h>
48 #endif
50 #include <sys/stat.h>
51 #ifndef sun
52 # include <sys/ioctl.h>
53 #endif
55 #ifndef SIGINT
56 # include <signal.h>
57 #endif
59 #include "config.h"
61 #ifdef HAVE_STROPTS_H
62 # include <sys/stropts.h>
63 #endif
65 #if defined(SYSV) && !defined(ISC)
66 # include <sys/utsname.h>
67 #endif
69 #if defined(sequent) || defined(SVR4)
70 # include <sys/resource.h>
71 #endif /* sequent || SVR4 */
73 #ifdef ISC
74 # include <sys/tty.h>
75 # include <sys/sioctl.h>
76 # include <sys/pty.h>
77 #endif /* ISC */
79 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
80 # include <compat.h>
81 #endif
82 #if defined(USE_LOCALE) || defined(ENCODINGS)
83 # include <locale.h>
84 #endif
85 #if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
86 # include <langinfo.h>
87 #endif
89 #include "screen.h"
90 #ifdef HAVE_BRAILLE
91 # include "braille.h"
92 #endif
94 #include "patchlevel.h"
97 * At the moment we only need the real password if the
98 * builtin lock is used. Therefore disable SHADOWPW if
99 * we do not really need it (kind of security thing).
101 #ifndef LOCK
102 # undef SHADOWPW
103 #endif
105 #include <pwd.h>
106 #ifdef SHADOWPW
107 # include <shadow.h>
108 #endif /* SHADOWPW */
110 #include "logfile.h" /* islogfile, logfflush */
112 #ifdef DEBUG
113 FILE *dfp;
114 #endif
117 extern char Term[], screenterm[], **environ, Termcap[];
118 int force_vt = 1;
119 int VBellWait, MsgWait, MsgMinWait, SilenceWait;
121 extern struct acluser *users;
122 extern struct display *displays, *display;
124 extern struct LayFuncs MarkLf;
127 extern int visual_bell;
128 #ifdef COPY_PASTE
129 extern unsigned char mark_key_tab[];
130 #endif
131 extern char version[];
132 extern char DefaultShell[];
133 #ifdef ZMODEM
134 extern char *zmodem_sendcmd;
135 extern char *zmodem_recvcmd;
136 #endif
137 extern struct layout *layout_last;
140 char *ShellProg;
141 char *ShellArgs[2];
143 extern struct NewWindow nwin_undef, nwin_default, nwin_options;
144 struct backtick;
146 static struct passwd *getpwbyname __P((char *, struct passwd *));
147 static void SigChldHandler __P((void));
148 static sigret_t SigChld __P(SIGPROTOARG);
149 static sigret_t SigInt __P(SIGPROTOARG);
150 static sigret_t CoreDump __P(SIGPROTOARG);
151 static sigret_t FinitHandler __P(SIGPROTOARG);
152 static void DoWait __P((void));
153 static void serv_read_fn __P((struct event *, char *));
154 static void serv_select_fn __P((struct event *, char *));
155 static void logflush_fn __P((struct event *, char *));
156 static void backtick_filter __P((struct backtick *));
157 static void backtick_fn __P((struct event *, char *));
158 static char *runbacktick __P((struct backtick *, int *, time_t));
159 static int IsSymbol __P((char *, char *));
160 static char *ParseChar __P((char *, char *));
161 static int ParseEscape __P((char *));
162 static char *pad_expand __P((char *, char *, int, int));
163 #ifdef DEBUG
164 static void fds __P((void));
165 #endif
167 int nversion; /* numerical version, used for secondary DA */
169 /* the attacher */
170 struct passwd *ppp;
171 char *attach_tty;
172 int attach_fd = -1;
173 char *attach_term;
174 char *LoginName;
175 struct mode attach_Mode;
177 char SockPath[MAXPATHLEN + 2 * MAXSTR];
178 char *SockName; /* SockName is pointer in SockPath */
179 char *SockMatch = NULL; /* session id command line argument */
180 int ServerSocket = -1;
181 struct event serv_read;
182 struct event serv_select;
183 struct event logflushev;
185 char **NewEnv = NULL;
187 char *RcFileName = NULL;
188 char *home;
190 char *screenlogfile; /* filename layout */
191 int log_flush = 10; /* flush interval in seconds */
192 int logtstamp_on = 0; /* tstamp disabled */
193 char *logtstamp_string; /* stamp layout */
194 int logtstamp_after = 120; /* first tstamp after 120s */
195 char *hardcopydir = NULL;
196 char *BellString;
197 char *VisualBellString;
198 char *ActivityString;
199 #ifdef COPY_PASTE
200 char *BufferFile;
201 #endif
202 #ifdef POW_DETACH
203 char *PowDetachString;
204 #endif
205 char *hstatusstring;
206 char *captionstring;
207 char *timestring;
208 char *wliststr;
209 char *wlisttit;
210 int auto_detach = 1;
211 int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
212 int cmdflag;
213 int queryflag = -1;
214 int adaptflag;
216 #ifdef MULTIUSER
217 char *multi;
218 char *multi_home;
219 int multi_uid;
220 int own_uid;
221 int multiattach;
222 int tty_mode;
223 int tty_oldmode = -1;
224 #endif
226 char HostName[MAXSTR];
227 int MasterPid, PanicPid;
228 int real_uid, real_gid, eff_uid, eff_gid;
229 int default_startup;
230 int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
231 char *preselect = NULL; /* only used in Attach() */
233 #ifdef UTF8
234 char *screenencodings;
235 #endif
237 #ifdef DW_CHARS
238 int cjkwidth;
239 #endif
241 #ifdef NETHACK
242 int nethackflag = 0;
243 #endif
244 int maxwin;
247 struct layer *flayer;
248 struct win *fore;
249 struct win *windows;
250 struct win *console_window;
255 * Do this last
257 #include "extern.h"
259 char strnomem[] = "Out of memory.";
262 static int InterruptPlease;
263 static int GotSigChld;
265 static int
266 lf_secreopen(name, wantfd, l)
267 char *name;
268 int wantfd;
269 struct logfile *l;
271 int got_fd;
273 close(wantfd);
274 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
275 lf_move_fd(got_fd, wantfd) < 0)
277 logfclose(l);
278 debug1("lf_secreopen: failed for %s\n", name);
279 return -1;
281 l->st->st_ino = l->st->st_dev = 0;
282 debug2("lf_secreopen: %d = %s\n", wantfd, name);
283 return 0;
286 /********************************************************************/
287 /********************************************************************/
288 /********************************************************************/
291 static struct passwd *
292 getpwbyname(name, ppp)
293 char *name;
294 struct passwd *ppp;
296 int n;
297 #ifdef SHADOWPW
298 struct spwd *sss = NULL;
299 static char *spw = NULL;
300 #endif
302 if (!ppp && !(ppp = getpwnam(name)))
303 return NULL;
305 /* Do password sanity check..., allow ##user for SUN_C2 security */
306 #ifdef SHADOWPW
307 pw_try_again:
308 #endif
309 n = 0;
310 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
311 strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
312 n = 13;
313 for (; n < 13; n++)
315 char c = ppp->pw_passwd[n];
316 if (!(c == '.' || c == '/' || c == '$' ||
317 (c >= '0' && c <= '9') ||
318 (c >= 'a' && c <= 'z') ||
319 (c >= 'A' && c <= 'Z')))
320 break;
323 #ifdef SHADOWPW
324 /* try to determine real password */
325 if (n < 13 && sss == 0)
327 sss = getspnam(ppp->pw_name);
328 if (sss)
330 if (spw)
331 free(spw);
332 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
333 endspent(); /* this should delete all buffers ... */
334 goto pw_try_again;
336 endspent(); /* this should delete all buffers ... */
338 #endif
339 if (n < 13)
340 ppp->pw_passwd = 0;
341 #ifdef linux
342 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
343 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
344 #endif
346 return ppp;
349 static char *
350 locale_name(void)
352 static char *s;
354 if (!s)
356 s = getenv("LC_ALL");
357 if (s == NULL)
358 s = getenv("LC_CTYPE");
359 if (s == NULL)
360 s = getenv("LANG");
362 return s;
366 main(ac, av)
367 int ac;
368 char **av;
370 register int n;
371 char *ap;
372 char *av0;
373 char socknamebuf[2 * MAXSTR];
374 int mflag = 0;
375 char *myname = (ac == 0) ? "screen" : av[0];
376 char *SockDir;
377 struct stat st;
378 #ifdef _MODE_T /* (jw) */
379 mode_t oumask;
380 #else
381 int oumask;
382 #endif
383 #if defined(SYSV) && !defined(ISC)
384 struct utsname utsnam;
385 #endif
386 struct NewWindow nwin;
387 int detached = 0; /* start up detached */
388 #ifdef MULTIUSER
389 char *sockp;
390 #endif
391 char *sty = 0;
393 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
394 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
395 #endif
396 #if defined(sun) && defined(SVR4)
398 /* Solaris' login blocks SIGHUP! This is _very bad_ */
399 sigset_t sset;
400 sigemptyset(&sset);
401 sigprocmask(SIG_SETMASK, &sset, 0);
403 #endif
406 * First, close all unused descriptors
407 * (otherwise, we might have problems with the select() call)
409 closeallfiles(0);
410 #ifdef DEBUG
411 opendebug(1, 0);
412 #endif
413 snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
414 PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
415 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
416 debug2("-- screen debug started %s (%s)\n", *av, version);
417 #ifdef POSIX
418 debug("POSIX\n");
419 #endif
420 #ifdef TERMIO
421 debug("TERMIO\n");
422 #endif
423 #ifdef SYSV
424 debug("SYSV\n");
425 #endif
426 #ifdef SYSVSIGS
427 debug("SYSVSIGS\n");
428 #endif
429 #ifdef NAMEDPIPE
430 debug("NAMEDPIPE\n");
431 #endif
432 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
433 debug("Window size changing enabled\n");
434 #endif
435 #ifdef HAVE_SETREUID
436 debug("SETREUID\n");
437 #endif
438 #ifdef HAVE_SETEUID
439 debug("SETEUID\n");
440 #endif
441 #ifdef hpux
442 debug("hpux\n");
443 #endif
444 #ifdef USEBCOPY
445 debug("USEBCOPY\n");
446 #endif
447 #ifdef UTMPOK
448 debug("UTMPOK\n");
449 #endif
450 #ifdef LOADAV
451 debug("LOADAV\n");
452 #endif
453 #ifdef NETHACK
454 debug("NETHACK\n");
455 #endif
456 #ifdef TERMINFO
457 debug("TERMINFO\n");
458 #endif
459 #ifdef SHADOWPW
460 debug("SHADOWPW\n");
461 #endif
462 #ifdef NAME_MAX
463 debug1("NAME_MAX = %d\n", NAME_MAX);
464 #endif
466 BellString = SaveStr("Bell in window %n");
467 VisualBellString = SaveStr(" Wuff, Wuff!! ");
468 ActivityString = SaveStr("Activity in window %n");
469 screenlogfile = SaveStr("screenlog.%n");
470 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
471 hstatusstring = SaveStr("%h");
472 captionstring = SaveStr("%4n %t");
473 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
474 wlisttit = SaveStr(" Num Name%=Flags");
475 wliststr = SaveStr("%4n %t%=%f");
476 #ifdef COPY_PASTE
477 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
478 #endif
479 ShellProg = NULL;
480 #ifdef POW_DETACH
481 PowDetachString = 0;
482 #endif
483 default_startup = (ac > 1) ? 0 : 1;
484 adaptflag = 0;
485 VBellWait = VBELLWAIT * 1000;
486 MsgWait = MSGWAIT * 1000;
487 MsgMinWait = MSGMINWAIT * 1000;
488 SilenceWait = SILENCEWAIT;
489 #ifdef HAVE_BRAILLE
490 InitBraille();
491 #endif
492 #ifdef ZMODEM
493 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
494 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
495 #endif
497 #ifdef COPY_PASTE
498 CompileKeys((char *)0, 0, mark_key_tab);
499 #endif
500 #ifdef UTF8
501 InitBuiltinTabs();
502 screenencodings = SaveStr(SCREENENCODINGS);
503 #endif
504 #ifdef DW_CHARS
505 cjkwidth = 0;
506 #endif
507 nwin = nwin_undef;
508 nwin_options = nwin_undef;
509 strcpy(screenterm, "screen");
511 logreopen_register(lf_secreopen);
513 av0 = *av;
514 /* if this is a login screen, assume -RR */
515 if (*av0 == '-')
517 rflag = 4;
518 #ifdef MULTI
519 xflag = 1;
520 #else
521 dflag = 1;
522 #endif
523 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
525 while (ac > 0)
527 ap = *++av;
528 if (--ac > 0 && *ap == '-')
530 if (ap[1] == '-' && ap[2] == 0)
532 av++;
533 ac--;
534 break;
536 if (ap[1] == '-' && !strcmp(ap, "--version"))
537 Panic(0, "Screen version %s", version);
538 if (ap[1] == '-' && !strcmp(ap, "--help"))
539 exit_with_usage(myname, NULL, NULL);
540 while (ap && *ap && *++ap)
542 switch (*ap)
544 case 'a':
545 nwin_options.aflag = 1;
546 break;
547 case 'A':
548 adaptflag = 1;
549 break;
550 case 'p': /* preselect */
551 if (*++ap)
552 preselect = ap;
553 else
555 if (!--ac)
556 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
557 preselect = *++av;
559 ap = NULL;
560 break;
561 #ifdef HAVE_BRAILLE
562 case 'B':
563 bd.bd_start_braille = 1;
564 break;
565 #endif
566 case 'c':
567 if (*++ap)
568 RcFileName = ap;
569 else
571 if (--ac == 0)
572 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
573 RcFileName = *++av;
575 ap = NULL;
576 break;
577 case 'e':
578 if (!*++ap)
580 if (--ac == 0)
581 exit_with_usage(myname, "Specify command characters with -e", NULL);
582 ap = *++av;
584 if (ParseEscape(ap))
585 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
586 ap = NULL;
587 break;
588 case 'f':
589 ap++;
590 switch (*ap++)
592 case 'n':
593 case '0':
594 nwin_options.flowflag = FLOW_NOW * 0;
595 break;
596 case '\0':
597 ap--;
598 /* FALLTHROUGH */
599 case 'y':
600 case '1':
601 nwin_options.flowflag = FLOW_NOW * 1;
602 break;
603 case 'a':
604 nwin_options.flowflag = FLOW_AUTOFLAG;
605 break;
606 default:
607 exit_with_usage(myname, "Unknown flow option -%s", --ap);
609 break;
610 case 'h':
611 if (--ac == 0)
612 exit_with_usage(myname, NULL, NULL);
613 nwin_options.histheight = atoi(*++av);
614 if (nwin_options.histheight < 0)
615 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
616 break;
617 case 'i':
618 iflag = 1;
619 break;
620 case 't': /* title, the former AkA == -k */
621 if (--ac == 0)
622 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
623 nwin_options.aka = *++av;
624 break;
625 case 'l':
626 ap++;
627 switch (*ap++)
629 case 'n':
630 case '0':
631 nwin_options.lflag = 0;
632 break;
633 case '\0':
634 ap--;
635 /* FALLTHROUGH */
636 case 'y':
637 case '1':
638 nwin_options.lflag = 1;
639 break;
640 case 'a':
641 nwin_options.lflag = 3;
642 break;
643 case 's': /* -ls */
644 case 'i': /* -list */
645 lsflag = 1;
646 if (ac > 1 && !SockMatch)
648 SockMatch = *++av;
649 ac--;
651 ap = NULL;
652 break;
653 default:
654 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
656 break;
657 case 'w':
658 lsflag = 1;
659 wipeflag = 1;
660 if (ac > 1 && !SockMatch)
662 SockMatch = *++av;
663 ac--;
665 break;
666 case 'L':
667 nwin_options.Lflag = 1;
668 break;
669 case 'm':
670 mflag = 1;
671 break;
672 case 'O': /* to be (or not to be?) deleted. jw. */
673 force_vt = 0;
674 break;
675 case 'T':
676 if (--ac == 0)
677 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
678 if (strlen(*++av) < 20)
679 strcpy(screenterm, *av);
680 else
681 Panic(0, "-T: terminal name too long. (max. 20 char)");
682 nwin_options.term = screenterm;
683 break;
684 case 'q':
685 quietflag = 1;
686 break;
687 case 'Q':
688 queryflag = 1;
689 cmdflag = 1;
690 break;
691 case 'r':
692 case 'R':
693 #ifdef MULTI
694 case 'x':
695 #endif
696 if (ac > 1 && *av[1] != '-' && !SockMatch)
698 SockMatch = *++av;
699 ac--;
700 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
702 #ifdef MULTI
703 if (*ap == 'x')
704 xflag = 1;
705 #endif
706 if (rflag)
707 rflag = 2;
708 rflag += (*ap == 'R') ? 2 : 1;
709 break;
710 #ifdef REMOTE_DETACH
711 case 'd':
712 dflag = 1;
713 /* FALLTHROUGH */
714 case 'D':
715 if (!dflag)
716 dflag = 2;
717 if (ac == 2)
719 if (*av[1] != '-' && !SockMatch)
721 SockMatch = *++av;
722 ac--;
723 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
726 break;
727 #endif
728 case 's':
729 if (--ac == 0)
730 exit_with_usage(myname, "Specify shell with -s", NULL);
731 if (ShellProg)
732 free(ShellProg);
733 ShellProg = SaveStr(*++av);
734 debug1("ShellProg: '%s'\n", ShellProg);
735 break;
736 case 'S':
737 if (!SockMatch)
739 if (--ac == 0)
740 exit_with_usage(myname, "Specify session-name with -S", NULL);
741 SockMatch = *++av;
743 if (!*SockMatch)
744 exit_with_usage(myname, "Empty session-name?", NULL);
745 break;
746 case 'X':
747 cmdflag = 1;
748 break;
749 case 'v':
750 Panic(0, "Screen version %s", version);
751 /* NOTREACHED */
752 #ifdef UTF8
753 case 'U':
754 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
755 break;
756 #endif
757 default:
758 exit_with_usage(myname, "Unknown option %s", --ap);
762 else
763 break;
766 real_uid = getuid();
767 real_gid = getgid();
768 eff_uid = geteuid();
769 eff_gid = getegid();
771 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
772 signal(SIGBUS, CoreDump);
773 #endif /* SIGBUS */
774 signal(SIGSEGV, CoreDump);
777 #ifdef USE_LOCALE
778 setlocale(LC_ALL, "");
779 #endif
780 #ifdef ENCODINGS
781 if (nwin_options.encoding == -1)
783 /* ask locale if we should start in UTF-8 mode */
784 # ifdef HAVE_NL_LANGINFO
785 # ifndef USE_LOCALE
786 setlocale(LC_CTYPE, "");
787 # endif
788 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
789 debug1("locale says encoding = %d\n", nwin_options.encoding);
790 # else
791 # ifdef UTF8
792 char *s;
793 if ((s = locale_name()) && InStr(s, "UTF-8"))
794 nwin_options.encoding = UTF8;
795 # endif
796 debug1("environment says encoding=%d\n", nwin_options.encoding);
797 #endif
799 # ifdef DW_CHARS
801 char *s;
802 if ((s = locale_name()))
804 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
806 cjkwidth = 1;
810 #endif
811 #endif
812 if (nwin_options.aka)
814 #ifdef ENCODINGS
815 if (nwin_options.encoding > 0)
817 size_t len = strlen(nwin_options.aka);
818 size_t newsz;
819 char *newbuf = malloc(3 * len);
820 if (!newbuf)
821 Panic(0, "%s", strnomem);
822 newsz = RecodeBuf(nwin_options.aka, len,
823 nwin_options.encoding, 0, newbuf);
824 newbuf[newsz] = '\0';
825 nwin_options.aka = newbuf;
827 else
828 #endif
830 /* If we just use the original value from av,
831 subsequent shelltitle invocations will attempt to free
832 space we don't own... */
833 nwin_options.aka = SaveStr(nwin_options.aka);
837 if (SockMatch && strlen(SockMatch) >= MAXSTR)
838 Panic(0, "Ridiculously long socketname - try again.");
839 if (cmdflag && !rflag && !dflag && !xflag)
840 xflag = 1;
841 if (!cmdflag && dflag && mflag && !(rflag || xflag))
842 detached = 1;
843 nwin = nwin_options;
844 #ifdef ENCODINGS
845 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
846 #endif
847 if (ac)
848 nwin.args = av;
850 /* make the write() calls return -1 on all errors */
851 #ifdef SIGXFSZ
853 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
854 * It appears that in System V Release 4, UNIX, if you are writing
855 * an output file and you exceed the currently set file size limit,
856 * you _don't_ just get the call to `write' returning with a
857 * failure code. Rather, you get a signal called `SIGXFSZ' which,
858 * if neither handled nor ignored, will cause your program to crash
859 * with a core dump.
861 signal(SIGXFSZ, SIG_IGN);
862 #endif /* SIGXFSZ */
864 #ifdef SIGPIPE
865 signal(SIGPIPE, SIG_IGN);
866 #endif
868 if (!ShellProg)
870 register char *sh;
872 sh = getenv("SHELL");
873 ShellProg = SaveStr(sh ? sh : DefaultShell);
875 ShellArgs[0] = ShellProg;
876 home = getenv("HOME");
877 if (!mflag && !SockMatch)
879 sty = getenv("STY");
880 if (sty && *sty == 0)
881 sty = 0;
884 #ifdef NETHACK
885 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
887 char nethackrc[MAXPATHLEN];
889 if (home && (strlen(home) < (MAXPATHLEN - 20)))
891 sprintf(nethackrc,"%s/.nethackrc", home);
892 nethackflag = !access(nethackrc, F_OK);
895 #endif
897 #ifdef MULTIUSER
898 own_uid = multi_uid = real_uid;
899 if (SockMatch && (sockp = index(SockMatch, '/')))
901 *sockp = 0;
902 multi = SockMatch;
903 SockMatch = sockp + 1;
904 if (*multi)
906 struct passwd *mppp;
907 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
908 Panic(0, "Cannot identify account '%s'.", multi);
909 multi_uid = mppp->pw_uid;
910 multi_home = SaveStr(mppp->pw_dir);
911 if (strlen(multi_home) > MAXPATHLEN - 10)
912 Panic(0, "home directory path too long");
913 # ifdef MULTI
914 /* always fake multi attach mode */
915 if (rflag || lsflag)
916 xflag = 1;
917 # endif /* MULTI */
918 detached = 0;
919 multiattach = 1;
921 /* Special case: effective user is multiuser. */
922 if (eff_uid && (multi_uid != eff_uid))
923 Panic(0, "Must run suid root for multiuser support.");
925 if (SockMatch && *SockMatch == 0)
926 SockMatch = 0;
927 #endif /* MULTIUSER */
929 if ((LoginName = getlogin()) && LoginName[0] != '\0')
931 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
932 if ((int)ppp->pw_uid != real_uid)
933 ppp = (struct passwd *) 0;
935 if (ppp == 0)
937 if ((ppp = getpwuid(real_uid)) == 0)
939 Panic(0, "getpwuid() can't identify your account!");
940 exit(1);
942 LoginName = ppp->pw_name;
944 LoginName = SaveStr(LoginName);
946 ppp = getpwbyname(LoginName, ppp);
948 #if !defined(SOCKDIR) && defined(MULTIUSER)
949 if (multi && !multiattach)
951 if (home && strcmp(home, ppp->pw_dir))
952 Panic(0, "$HOME must match passwd entry for multiuser screens.");
954 #endif
956 #define SET_GUID() do \
958 setgid(real_gid); \
959 setuid(real_uid); \
960 eff_uid = real_uid; \
961 eff_gid = real_gid; \
962 } while (0)
964 #define SET_TTYNAME(fatal) do \
966 if (!(attach_tty = ttyname(0))) \
968 if (fatal) \
969 Panic(0, "Must be connected to a terminal."); \
970 else \
971 attach_tty = ""; \
973 else if (stat(attach_tty, &st)) \
974 Panic(errno, "Cannot access '%s'", attach_tty); \
975 if (strlen(attach_tty) >= MAXPATHLEN) \
976 Panic(0, "TtyName too long - sorry."); \
977 } while (0)
979 if (home == 0 || *home == '\0')
980 home = ppp->pw_dir;
981 if (strlen(LoginName) > 20)
982 Panic(0, "LoginName too long - sorry.");
983 #ifdef MULTIUSER
984 if (multi && strlen(multi) > 20)
985 Panic(0, "Screen owner name too long - sorry.");
986 #endif
987 if (strlen(home) > MAXPATHLEN - 25)
988 Panic(0, "$HOME too long - sorry.");
990 attach_tty = "";
991 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty && !xflag))
993 #ifndef NAMEDPIPE
994 int fl;
995 #endif
997 /* ttyname implies isatty */
998 SET_TTYNAME(1);
999 #ifdef MULTIUSER
1000 tty_mode = (int)st.st_mode & 0777;
1001 #endif
1003 #ifndef NAMEDPIPE
1004 fl = fcntl(0, F_GETFL, 0);
1005 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1006 attach_fd = 0;
1007 #endif
1008 if (attach_fd == -1)
1010 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1011 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1012 close(n);
1014 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1016 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1017 Panic(0, "Please set a terminal type.");
1018 if (strlen(attach_term) > sizeof(D_termname) - 1)
1019 Panic(0, "$TERM too long - sorry.");
1020 GetTTY(0, &attach_Mode);
1021 #ifdef DEBUGGGGGGGGGGGGGGG
1022 DebugTTY(&attach_Mode);
1023 #endif /* DEBUG */
1026 #ifdef _MODE_T
1027 oumask = umask(0); /* well, unsigned never fails? jw. */
1028 #else
1029 if ((oumask = (int)umask(0)) == -1)
1030 Panic(errno, "Cannot change umask to zero");
1031 #endif
1032 SockDir = getenv("SCREENDIR");
1033 if (SockDir)
1035 if (strlen(SockDir) >= MAXPATHLEN - 1)
1036 Panic(0, "Ridiculously long $SCREENDIR - try again.");
1037 #ifdef MULTIUSER
1038 if (multi)
1039 Panic(0, "No $SCREENDIR with multi screens, please.");
1040 #endif
1042 #ifdef MULTIUSER
1043 if (multiattach)
1045 # ifndef SOCKDIR
1046 sprintf(SockPath, "%s/.screen", multi_home);
1047 SockDir = SockPath;
1048 # else
1049 SockDir = SOCKDIR;
1050 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1051 # endif
1053 else
1054 #endif
1056 #ifndef SOCKDIR
1057 if (SockDir == 0)
1059 sprintf(SockPath, "%s/.screen", home);
1060 SockDir = SockPath;
1062 #endif
1063 if (SockDir)
1065 if (access(SockDir, F_OK))
1067 debug1("SockDir '%s' missing ...\n", SockDir);
1068 if (UserContext() > 0)
1070 if (mkdir(SockDir, 0700))
1071 UserReturn(0);
1072 UserReturn(1);
1074 if (UserStatus() <= 0)
1075 Panic(0, "Cannot make directory '%s'.", SockDir);
1077 if (SockDir != SockPath)
1078 strcpy(SockPath, SockDir);
1080 #ifdef SOCKDIR
1081 else
1083 SockDir = SOCKDIR;
1084 if (stat(SockDir, &st))
1086 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1087 (eff_gid != real_gid) ? 0775 :
1088 #ifdef S_ISVTX
1089 0777|S_ISVTX;
1090 #else
1091 0777;
1092 #endif
1093 if (mkdir(SockDir, n) == -1)
1094 Panic(errno, "Cannot make directory '%s'", SockDir);
1096 else
1098 if (!S_ISDIR(st.st_mode))
1099 Panic(0, "'%s' must be a directory.", SockDir);
1100 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1101 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1102 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1103 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1104 0777;
1105 if (((int)st.st_mode & 0777) != n)
1106 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1108 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1109 if (access(SockPath, F_OK))
1111 if (mkdir(SockPath, 0700) == -1)
1112 Panic(errno, "Cannot make directory '%s'", SockPath);
1113 (void) chown(SockPath, real_uid, real_gid);
1116 #endif
1119 if (stat(SockPath, &st) == -1)
1120 Panic(errno, "Cannot access %s", SockPath);
1121 else
1122 if (!S_ISDIR(st.st_mode))
1123 Panic(0, "%s is not a directory.", SockPath);
1124 #ifdef MULTIUSER
1125 if (multi)
1127 if ((int)st.st_uid != multi_uid)
1128 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1130 else
1131 #endif
1133 if ((int)st.st_uid != real_uid)
1134 Panic(0, "You are not the owner of %s.", SockPath);
1136 if ((st.st_mode & 0777) != 0700)
1137 Panic(0, "Directory %s must have mode 700.", SockPath);
1138 if (SockMatch && index(SockMatch, '/'))
1139 Panic(0, "Bad session name '%s'", SockMatch);
1140 SockName = SockPath + strlen(SockPath) + 1;
1141 *SockName = 0;
1142 (void) umask(oumask);
1143 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1145 #if defined(SYSV) && !defined(ISC)
1146 if (uname(&utsnam) == -1)
1147 Panic(errno, "uname");
1148 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1149 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1150 #else
1151 (void) gethostname(HostName, MAXSTR);
1152 HostName[MAXSTR - 1] = '\0';
1153 #endif
1154 if ((ap = index(HostName, '.')) != NULL)
1155 *ap = '\0';
1157 if (lsflag)
1159 int i, fo, oth;
1161 #ifdef MULTIUSER
1162 if (multi)
1163 real_uid = multi_uid;
1164 #endif
1165 SET_GUID();
1166 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1167 if (quietflag)
1168 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1169 if (fo == 0)
1170 Panic(0, "No Sockets found in %s.\n", SockPath);
1171 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1172 /* NOTREACHED */
1174 signal(SIG_BYE, AttacherFinit); /* prevent races */
1175 if (cmdflag)
1177 /* attach_tty is not mandatory */
1178 SET_TTYNAME(0);
1179 if (!*av)
1180 Panic(0, "Please specify a command.");
1181 SET_GUID();
1182 SendCmdMessage(sty, SockMatch, av, queryflag >= 0);
1183 exit(0);
1185 else if (rflag || xflag)
1187 debug("screen -r: - is there anybody out there?\n");
1188 if (Attach(MSG_ATTACH))
1190 Attacher();
1191 /* NOTREACHED */
1193 #ifdef MULTIUSER
1194 if (multiattach)
1195 Panic(0, "Can't create sessions of other users.");
1196 #endif
1197 debug("screen -r: backend not responding -- still crying\n");
1199 else if (dflag && !mflag)
1201 SET_TTYNAME(0);
1202 Attach(MSG_DETACH);
1203 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1204 eexit(0);
1205 /* NOTREACHED */
1207 if (!SockMatch && !mflag && sty)
1209 /* attach_tty is not mandatory */
1210 SET_TTYNAME(0);
1211 SET_GUID();
1212 nwin_options.args = av;
1213 SendCreateMsg(sty, &nwin);
1214 exit(0);
1215 /* NOTREACHED */
1217 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1219 if (!detached || dflag != 2)
1220 MasterPid = fork();
1221 else
1222 MasterPid = 0;
1224 switch (MasterPid)
1226 case -1:
1227 Panic(errno, "fork");
1228 /* NOTREACHED */
1229 case 0:
1230 break;
1231 default:
1232 if (detached)
1233 exit(0);
1234 if (SockMatch)
1235 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1236 else
1237 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1238 for (ap = socknamebuf; *ap; ap++)
1239 if (*ap == '/')
1240 *ap = '-';
1241 #ifdef NAME_MAX
1242 if (strlen(socknamebuf) > NAME_MAX)
1243 socknamebuf[NAME_MAX] = 0;
1244 #endif
1245 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1246 SET_GUID();
1247 Attacher();
1248 /* NOTREACHED */
1251 if (!detached)
1252 PanicPid = getppid();
1254 if (DefaultEsc == -1)
1255 DefaultEsc = Ctrl('a');
1256 if (DefaultMetaEsc == -1)
1257 DefaultMetaEsc = 'a';
1259 ap = av0 + strlen(av0) - 1;
1260 while (ap >= av0)
1262 if (!strncmp("screen", ap, 6))
1264 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1265 break;
1267 ap--;
1269 if (ap < av0)
1270 *av0 = 'S';
1272 #ifdef DEBUG
1274 char buf[256];
1276 if (dfp && dfp != stderr)
1277 fclose(dfp);
1278 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1279 if ((dfp = fopen(buf, "w")) == NULL)
1280 dfp = stderr;
1281 else
1282 (void) chmod(buf, 0666);
1284 #endif
1285 if (!detached)
1287 if (attach_fd == -1)
1289 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1290 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1292 else
1293 n = dup(attach_fd);
1295 else
1296 n = -1;
1297 freopen("/dev/null", "r", stdin);
1298 freopen("/dev/null", "w", stdout);
1300 #ifdef DEBUG
1301 if (dfp != stderr)
1302 #endif
1303 freopen("/dev/null", "w", stderr);
1304 debug("-- screen.back debug started\n");
1307 * This guarantees that the session owner is listed, even when we
1308 * start detached. From now on we should not refer to 'LoginName'
1309 * any more, use users->u_name instead.
1311 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1312 Panic(0, "Could not create user info");
1313 if (!detached)
1315 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1316 Panic(0, "Could not alloc display");
1317 PanicPid = 0;
1318 #ifdef ENCODINGS
1319 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1320 debug1("D_encoding = %d\n", D_encoding);
1321 #endif
1324 if (SockMatch)
1326 /* user started us with -S option */
1327 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1329 else
1331 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1332 HostName);
1334 for (ap = socknamebuf; *ap; ap++)
1335 if (*ap == '/')
1336 *ap = '-';
1337 #ifdef NAME_MAX
1338 if (strlen(socknamebuf) > NAME_MAX)
1340 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1341 socknamebuf[NAME_MAX] = 0;
1343 #endif
1344 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1346 ServerSocket = MakeServerSocket();
1347 InitKeytab();
1348 #ifdef ETCSCREENRC
1349 # ifdef ALLOW_SYSSCREENRC
1350 if ((ap = getenv("SYSSCREENRC")))
1351 (void)StartRc(ap, 0);
1352 else
1353 # endif
1354 (void)StartRc(ETCSCREENRC, 0);
1355 #endif
1356 (void)StartRc(RcFileName, 0);
1357 # ifdef UTMPOK
1358 # ifndef UTNOKEEP
1359 InitUtmp();
1360 # endif /* UTNOKEEP */
1361 # endif /* UTMPOK */
1362 if (display)
1364 if (InitTermcap(0, 0))
1366 debug("Could not init termcap - exiting\n");
1367 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1368 freetty();
1369 if (D_userpid)
1370 Kill(D_userpid, SIG_BYE);
1371 eexit(1);
1373 MakeDefaultCanvas();
1374 InitTerm(0);
1375 #ifdef UTMPOK
1376 RemoveLoginSlot();
1377 #endif
1379 else
1380 MakeTermcap(1);
1381 #ifdef LOADAV
1382 InitLoadav();
1383 #endif /* LOADAV */
1384 MakeNewEnv();
1385 signal(SIGHUP, SigHup);
1386 signal(SIGINT, FinitHandler);
1387 signal(SIGQUIT, FinitHandler);
1388 signal(SIGTERM, FinitHandler);
1389 #ifdef BSDJOBS
1390 signal(SIGTTIN, SIG_IGN);
1391 signal(SIGTTOU, SIG_IGN);
1392 #endif
1394 if (display)
1396 brktty(D_userfd);
1397 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1398 /* Note: SetMode must be called _before_ FinishRc. */
1399 SetTTY(D_userfd, &D_NewMode);
1400 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1401 Msg(errno, "Warning: NBLOCK fcntl failed");
1403 else
1404 brktty(-1); /* just try */
1405 signal(SIGCHLD, SigChld);
1406 #ifdef ETCSCREENRC
1407 # ifdef ALLOW_SYSSCREENRC
1408 if ((ap = getenv("SYSSCREENRC")))
1409 FinishRc(ap);
1410 else
1411 # endif
1412 FinishRc(ETCSCREENRC);
1413 #endif
1414 FinishRc(RcFileName);
1416 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1417 if (windows == NULL)
1419 debug("We open one default window, as screenrc did not specify one.\n");
1420 if (MakeWindow(&nwin) == -1)
1422 fd_set rfd;
1423 struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
1424 FD_SET(0, &rfd);
1426 Msg(0, "Sorry, could not find a PTY or TTY.");
1427 // allow user to exit early by pressing any key.
1428 select(1, &rfd, NULL, NULL, &tv);
1429 Finit(0);
1430 /* NOTREACHED */
1433 else if (ac) /* Screen was invoked with a command */
1435 MakeWindow(&nwin);
1438 #ifdef HAVE_BRAILLE
1439 StartBraille();
1440 #endif
1442 if (display && default_startup)
1443 display_copyright();
1444 signal(SIGINT, SigInt);
1445 if (rflag && (rflag & 1) == 0 && !quietflag)
1447 Msg(0, "New screen...");
1448 rflag = 0;
1451 serv_read.type = EV_READ;
1452 serv_read.fd = ServerSocket;
1453 serv_read.handler = serv_read_fn;
1454 evenq(&serv_read);
1456 serv_select.pri = -10;
1457 serv_select.type = EV_ALWAYS;
1458 serv_select.handler = serv_select_fn;
1459 evenq(&serv_select);
1461 logflushev.type = EV_TIMEOUT;
1462 logflushev.handler = logflush_fn;
1464 sched();
1465 /* NOTREACHED */
1466 return 0;
1469 void
1470 WindowDied(p, wstat, wstat_valid)
1471 struct win *p;
1472 #ifdef BSDWAIT
1473 union wait wstat;
1474 #else
1475 int wstat;
1476 #endif
1477 int wstat_valid;
1479 int killit = 0;
1481 if (p->w_destroyev.data == (char *)p)
1483 wstat = p->w_exitstatus;
1484 wstat_valid = 1;
1485 evdeq(&p->w_destroyev);
1486 p->w_destroyev.data = 0;
1489 #if defined(BSDJOBS) && !defined(BSDWAIT)
1490 if (!wstat_valid && p->w_pid > 0)
1492 /* EOF on file descriptor. The process is probably also dead.
1493 * try a waitpid */
1494 if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid)
1496 p->w_pid = 0;
1497 wstat_valid = 1;
1500 #endif
1501 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1502 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1503 killit = 1;
1505 if (ZombieKey_destroy && !killit)
1507 char buf[100], *s, reason[100];
1508 time_t now;
1510 if (wstat_valid) {
1511 if (WIFEXITED(wstat))
1512 if (WEXITSTATUS(wstat))
1513 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1514 else
1515 sprintf(reason, "terminated normally");
1516 else if (WIFSIGNALED(wstat))
1517 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1518 #ifdef WCOREDUMP
1519 WCOREDUMP(wstat) ? " (core file generated)" : "");
1520 #else
1521 "");
1522 #endif
1523 } else
1524 sprintf(reason, "detached from window");
1526 (void) time(&now);
1527 s = ctime(&now);
1528 if (s && *s)
1529 s[strlen(s) - 1] = '\0';
1530 debug3("window %d (%s) going into zombie state fd %d",
1531 p->w_number, p->w_title, p->w_ptyfd);
1532 #ifdef UTMPOK
1533 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1535 RemoveUtmp(p);
1536 p->w_slot = 0; /* "detached" */
1538 #endif
1539 CloseDevice(p);
1541 p->w_deadpid = p->w_pid;
1542 p->w_pid = 0;
1543 ResetWindow(p);
1544 /* p->w_y = p->w_bot; */
1545 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1546 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1547 WriteString(p, buf, strlen(buf));
1548 WindowChanged(p, 'f');
1550 else
1551 KillWindow(p);
1552 #ifdef UTMPOK
1553 CarefulUtmp();
1554 #endif
1557 static void
1558 SigChldHandler()
1560 struct stat st;
1561 #ifdef DEBUG
1562 fds();
1563 #endif
1564 while (GotSigChld)
1566 GotSigChld = 0;
1567 DoWait();
1568 #ifdef SYSVSIGS
1569 signal(SIGCHLD, SigChld);
1570 #endif
1572 if (stat(SockPath, &st) == -1)
1574 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1575 if (!RecoverSocket())
1577 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1578 Finit(1);
1580 else
1581 debug1("'%s' reconstructed\n", SockPath);
1583 else
1584 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1587 static sigret_t
1588 SigChld SIGDEFARG
1590 debug("SigChld()\n");
1591 GotSigChld = 1;
1592 SIGRETURN;
1595 sigret_t
1596 SigHup SIGDEFARG
1598 /* Hangup all displays */
1599 while ((display = displays) != 0)
1600 Hangup();
1601 SIGRETURN;
1605 * the backend's Interrupt handler
1606 * we cannot insert the intrc directly, as we never know
1607 * if fore is valid.
1609 static sigret_t
1610 SigInt SIGDEFARG
1612 #if HAZARDOUS
1613 char ibuf;
1615 debug("SigInt()\n");
1616 if (fore && displays)
1618 # if defined(TERMIO) || defined(POSIX)
1619 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1620 # else
1621 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1622 # endif
1623 fore->w_inlen = 0;
1624 write(fore->w_ptyfd, &ibuf, 1);
1626 #else
1627 signal(SIGINT, SigInt);
1628 debug("SigInt() careful\n");
1629 InterruptPlease = 1;
1630 #endif
1631 SIGRETURN;
1634 static sigret_t
1635 CoreDump SIGDEFARG
1637 /* if running with s-bit, we must reset the s-bit, so that we get a
1638 * core file anyway.
1641 struct display *disp;
1642 char buf[80];
1644 char *dump_msg = " (core dumped)";
1646 int running_w_s_bit = getuid() != geteuid();
1647 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1648 if (running_w_s_bit)
1649 dump_msg = "";
1650 #endif
1652 #if defined(SYSVSIGS) && defined(SIGHASARG)
1653 signal(sigsig, SIG_IGN);
1654 #endif
1655 setgid(getgid());
1656 setuid(getuid());
1657 unlink("core");
1659 #ifdef SIGHASARG
1660 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig, dump_msg);
1661 #else
1662 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n", dump_msg);
1663 #endif
1665 for (disp = displays; disp; disp = disp->d_next)
1667 if (disp->d_nonblock < -1 || disp->d_nonblock > 1000000)
1668 continue;
1669 fcntl(disp->d_userfd, F_SETFL, 0);
1670 SetTTY(disp->d_userfd, &D_OldMode);
1671 write(disp->d_userfd, buf, strlen(buf));
1672 Kill(disp->d_userpid, SIG_BYE);
1675 if (running_w_s_bit)
1677 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1678 Kill(getpid(), SIGKILL);
1679 eexit(11);
1680 #else /* SHADOWPW && !DEBUG */
1681 abort();
1682 #endif /* SHADOWPW && !DEBUG */
1684 else
1685 abort();
1686 SIGRETURN;
1689 static void
1690 DoWait()
1692 register int pid;
1693 struct win *p, *next;
1694 #ifdef BSDWAIT
1695 union wait wstat;
1696 #else
1697 int wstat;
1698 #endif
1700 #ifdef BSDJOBS
1701 # ifndef BSDWAIT
1702 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1703 # else
1704 # ifdef USE_WAIT2
1706 * From: rouilj@sni-usa.com (John Rouillard)
1707 * note that WUNTRACED is not documented to work, but it is defined in
1708 * /usr/include/sys/wait.h, so it may work
1710 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1711 # else /* USE_WAIT2 */
1712 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1713 # endif /* USE_WAIT2 */
1714 # endif
1715 #else /* BSDJOBS */
1716 while ((pid = wait(&wstat)) < 0)
1717 if (errno != EINTR)
1718 break;
1719 if (pid > 0)
1720 #endif /* BSDJOBS */
1722 for (p = windows; p; p = next)
1724 next = p->w_next;
1725 if ( (p->w_pid && pid == p->w_pid) ||
1726 (p->w_deadpid && pid == p->w_deadpid) )
1728 /* child has ceased to exist */
1729 p->w_pid = 0;
1731 #ifdef BSDJOBS
1732 if (WIFSTOPPED(wstat))
1734 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1735 #ifdef SIGTTIN
1736 if (WSTOPSIG(wstat) == SIGTTIN)
1738 Msg(0, "Suspended (tty input)");
1739 continue;
1741 #endif
1742 #ifdef SIGTTOU
1743 if (WSTOPSIG(wstat) == SIGTTOU)
1745 Msg(0, "Suspended (tty output)");
1746 continue;
1748 #endif
1749 /* Try to restart process */
1750 Msg(0, "Child has been stopped, restarting.");
1751 if (killpg(pid, SIGCONT))
1752 kill(pid, SIGCONT);
1754 else
1755 #endif
1757 /* Screen will detect the window has died when the window's
1758 * file descriptor signals EOF (which it will do when the process in
1759 * the window terminates). So do this in a timeout of 10 seconds.
1760 * (not doing this at all might also work)
1761 * See #27061 for more details.
1763 p->w_destroyev.data = (char *)p;
1764 p->w_exitstatus = wstat;
1765 SetTimeout(&p->w_destroyev, 10 * 1000);
1766 evenq(&p->w_destroyev);
1768 break;
1770 #ifdef PSEUDOS
1771 if (p->w_pwin && pid == p->w_pwin->p_pid)
1773 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1774 FreePseudowin(p);
1775 break;
1777 #endif
1779 if (p == 0)
1781 debug1("pid %d not found - hope that's ok\n", pid);
1787 static sigret_t
1788 FinitHandler SIGDEFARG
1790 #ifdef SIGHASARG
1791 debug1("FinitHandler called, sig %d.\n", sigsig);
1792 #else
1793 debug("FinitHandler called.\n");
1794 #endif
1795 Finit(1);
1796 SIGRETURN;
1799 void
1800 Finit(i)
1801 int i;
1803 signal(SIGCHLD, SIG_DFL);
1804 signal(SIGHUP, SIG_IGN);
1805 debug1("Finit(%d);\n", i);
1806 while (windows)
1808 struct win *p = windows;
1809 windows = windows->w_next;
1810 FreeWindow(p);
1812 if (ServerSocket != -1)
1814 debug1("we unlink(%s)\n", SockPath);
1815 #ifdef USE_SETEUID
1816 xseteuid(real_uid);
1817 xsetegid(real_gid);
1818 #endif
1819 (void) unlink(SockPath);
1820 #ifdef USE_SETEUID
1821 xseteuid(eff_uid);
1822 xsetegid(eff_gid);
1823 #endif
1825 for (display = displays; display; display = display->d_next)
1827 if (D_status)
1828 RemoveStatus();
1829 FinitTerm();
1830 #ifdef UTMPOK
1831 RestoreLoginSlot();
1832 #endif
1833 AddStr("[screen is terminating]\r\n");
1834 Flush(3);
1835 SetTTY(D_userfd, &D_OldMode);
1836 fcntl(D_userfd, F_SETFL, 0);
1837 freetty();
1838 Kill(D_userpid, SIG_BYE);
1841 * we _cannot_ call eexit(i) here,
1842 * instead of playing with the Socket above. Sigh.
1844 exit(i);
1847 void
1848 eexit(e)
1849 int e;
1851 debug("eexit\n");
1852 if (ServerSocket != -1)
1854 debug1("we unlink(%s)\n", SockPath);
1855 setgid(real_gid);
1856 setuid(real_uid);
1857 (void) unlink(SockPath);
1859 exit(e);
1862 void
1863 Hangup()
1865 if (display == 0)
1866 return;
1867 debug1("Hangup %x\n", display);
1868 if (D_userfd >= 0)
1870 close(D_userfd);
1871 D_userfd = -1;
1873 if (auto_detach || displays->d_next)
1874 Detach(D_HANGUP);
1875 else
1876 Finit(0);
1880 * Detach now has the following modes:
1881 *D_DETACH SIG_BYE detach backend and exit attacher
1882 *D_HANGUP SIG_BYE detach backend and exit attacher
1883 *D_STOP SIG_STOP stop attacher (and detach backend)
1884 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1885 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1886 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1887 *D_LOCK SIG_LOCK lock the attacher
1888 * (jw)
1889 * we always remove our utmp slots. (even when "lock" or "stop")
1890 * Note: Take extra care here, we may be called by interrupt!
1892 void
1893 Detach(mode)
1894 int mode;
1896 int sign = 0, pid;
1897 struct canvas *cv;
1898 struct win *p;
1900 if (display == 0)
1901 return;
1903 #define AddStrSock(msg) do { \
1904 if (SockName) \
1906 AddStr("[" msg " from "); \
1907 AddStr(SockName); \
1908 AddStr("]\r\n"); \
1910 else \
1911 AddStr("[" msg "]\r\n"); \
1912 } while (0)
1914 signal(SIGHUP, SIG_IGN);
1915 debug1("Detach(%d)\n", mode);
1916 if (D_status)
1917 RemoveStatus();
1918 FinitTerm();
1919 if (!display)
1920 return;
1921 switch (mode)
1923 case D_HANGUP:
1924 sign = SIG_BYE;
1925 break;
1926 case D_DETACH:
1927 AddStrSock("detached");
1928 sign = SIG_BYE;
1929 break;
1930 #ifdef BSDJOBS
1931 case D_STOP:
1932 sign = SIG_STOP;
1933 break;
1934 #endif
1935 #ifdef REMOTE_DETACH
1936 case D_REMOTE:
1937 AddStrSock("remote detached");
1938 sign = SIG_BYE;
1939 break;
1940 #endif
1941 #ifdef POW_DETACH
1942 case D_POWER:
1943 AddStrSock("power detached");
1944 if (PowDetachString)
1946 AddStr(PowDetachString);
1947 AddStr("\r\n");
1949 sign = SIG_POWER_BYE;
1950 break;
1951 #ifdef REMOTE_DETACH
1952 case D_REMOTE_POWER:
1953 AddStrSock("remote power detached");
1954 if (PowDetachString)
1956 AddStr(PowDetachString);
1957 AddStr("\r\n");
1959 sign = SIG_POWER_BYE;
1960 break;
1961 #endif
1962 #endif
1963 case D_LOCK:
1964 ClearAll();
1965 sign = SIG_LOCK;
1966 /* tell attacher to lock terminal with a lockprg. */
1967 break;
1969 #ifdef UTMPOK
1970 if (displays->d_next == 0)
1972 for (p = windows; p; p = p->w_next)
1974 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1976 RemoveUtmp(p);
1978 * Set the slot to 0 to get the window
1979 * logged in again.
1981 p->w_slot = (slot_t) 0;
1985 if (mode != D_HANGUP)
1986 RestoreLoginSlot();
1987 #endif
1988 if (displays->d_next == 0 && console_window)
1990 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1992 debug("could not release console - killing window\n");
1993 KillWindow(console_window);
1994 display = displays; /* restore display */
1997 if (D_fore)
1999 #ifdef MULTIUSER
2000 ReleaseAutoWritelock(display, D_fore);
2001 #endif
2002 D_user->u_detachwin = D_fore->w_number;
2003 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
2005 AutosaveLayout(D_layout);
2006 layout_last = D_layout;
2007 for (cv = D_cvlist; cv; cv = cv->c_next)
2009 p = Layer2Window(cv->c_layer);
2010 SetCanvasWindow(cv, 0);
2011 if (p)
2012 WindowChanged(p, 'u');
2015 pid = D_userpid;
2016 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
2017 FreeDisplay();
2018 if (displays == 0)
2019 /* Flag detached-ness */
2020 (void) chsock();
2022 * tell father what to do. We do that after we
2023 * freed the tty, thus getty feels more comfortable on hpux
2024 * if it was a power detach.
2026 Kill(pid, sign);
2027 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
2028 debug("Detach returns, we are successfully detached.\n");
2029 signal(SIGHUP, SigHup);
2030 #undef AddStrSock
2033 static int
2034 IsSymbol(e, s)
2035 char *e, *s;
2037 register int l;
2039 l = strlen(s);
2040 return strncmp(e, s, l) == 0 && e[l] == '=';
2043 void
2044 MakeNewEnv()
2046 register char **op, **np;
2047 static char stybuf[MAXSTR];
2049 for (op = environ; *op; ++op)
2051 if (NewEnv)
2052 free((char *)NewEnv);
2053 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
2054 if (!NewEnv)
2055 Panic(0, "%s", strnomem);
2056 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
2057 *np++ = stybuf; /* NewEnv[0] */
2058 *np++ = Term; /* NewEnv[1] */
2059 np++; /* room for SHELL */
2060 #ifdef TIOCSWINSZ
2061 np += 2; /* room for TERMCAP and WINDOW */
2062 #else
2063 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2064 #endif
2066 for (op = environ; *op; ++op)
2068 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2069 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2070 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2071 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
2073 *np++ = *op;
2075 *np = 0;
2078 void
2079 /*VARARGS2*/
2080 #if defined(USEVARARGS) && defined(__STDC__)
2081 Msg(int err, const char *fmt, VA_DOTS)
2082 #else
2083 Msg(err, fmt, VA_DOTS)
2084 int err;
2085 const char *fmt;
2086 VA_DECL
2087 #endif
2089 VA_LIST(ap)
2090 char buf[MAXPATHLEN*2];
2091 char *p = buf;
2093 VA_START(ap, fmt);
2094 fmt = DoNLS(fmt);
2095 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2096 VA_END(ap);
2097 if (err)
2099 p += strlen(p);
2100 *p++ = ':';
2101 *p++ = ' ';
2102 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2103 buf[sizeof(buf) - 1] = 0;
2105 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2107 if (display && displays)
2108 MakeStatus(buf);
2109 else if (displays)
2111 for (display = displays; display; display = display->d_next)
2112 MakeStatus(buf);
2114 else if (display)
2116 /* no displays but a display - must have forked.
2117 * send message to backend!
2119 char *tty = D_usertty;
2120 struct display *olddisplay = display;
2121 display = 0; /* only send once */
2122 SendErrorMsg(tty, buf);
2123 display = olddisplay;
2125 else
2126 printf("%s\r\n", buf);
2128 if (queryflag >= 0)
2129 write(queryflag, buf, strlen(buf));
2133 * Call FinitTerm for all displays, write a message to each and call eexit();
2135 void
2136 /*VARARGS2*/
2137 #if defined(USEVARARGS) && defined(__STDC__)
2138 Panic(int err, const char *fmt, VA_DOTS)
2139 #else
2140 Panic(err, fmt, VA_DOTS)
2141 int err;
2142 const char *fmt;
2143 VA_DECL
2144 #endif
2146 VA_LIST(ap)
2147 char buf[MAXPATHLEN*2];
2148 char *p = buf;
2150 VA_START(ap, fmt);
2151 fmt = DoNLS(fmt);
2152 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2153 VA_END(ap);
2154 if (err)
2156 p += strlen(p);
2157 *p++ = ':';
2158 *p++ = ' ';
2159 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2160 buf[sizeof(buf) - 1] = 0;
2162 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2163 if (displays == 0 && display == 0)
2165 printf("%s\r\n", buf);
2166 if (PanicPid)
2167 Kill(PanicPid, SIG_BYE);
2169 else if (displays == 0)
2171 /* no displays but a display - must have forked.
2172 * send message to backend!
2174 char *tty = D_usertty;
2175 display = 0;
2176 SendErrorMsg(tty, buf);
2177 sleep(2);
2178 _exit(1);
2180 else
2181 for (display = displays; display; display = display->d_next)
2183 if (D_status)
2184 RemoveStatus();
2185 FinitTerm();
2186 Flush(3);
2187 #ifdef UTMPOK
2188 RestoreLoginSlot();
2189 #endif
2190 SetTTY(D_userfd, &D_OldMode);
2191 fcntl(D_userfd, F_SETFL, 0);
2192 write(D_userfd, buf, strlen(buf));
2193 write(D_userfd, "\n", 1);
2194 freetty();
2195 if (D_userpid)
2196 Kill(D_userpid, SIG_BYE);
2198 #ifdef MULTIUSER
2199 if (tty_oldmode >= 0)
2201 # ifdef USE_SETEUID
2202 if (setuid(own_uid))
2203 xseteuid(own_uid); /* may be a loop. sigh. */
2204 # else
2205 setuid(own_uid);
2206 # endif
2207 debug1("Panic: changing back modes from %s\n", attach_tty);
2208 chmod(attach_tty, tty_oldmode);
2210 #endif
2211 eexit(1);
2216 * '^' is allowed as an escape mechanism for control characters. jw.
2218 * Added time insertion using ideas/code from /\ndy Jones
2219 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2223 #ifndef USE_LOCALE
2224 static const char days[] = "SunMonTueWedThuFriSat";
2225 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2226 #endif
2228 static char winmsg_buf[MAXSTR];
2229 #define MAX_WINMSG_REND 256 /* rendition changes */
2230 static int winmsg_rend[MAX_WINMSG_REND];
2231 static int winmsg_rendpos[MAX_WINMSG_REND];
2232 static int winmsg_numrend;
2234 static char *
2235 pad_expand(buf, p, numpad, padlen)
2236 char *buf;
2237 char *p;
2238 int numpad;
2239 int padlen;
2241 char *pn, *pn2;
2242 int i, r;
2244 padlen = padlen - (p - buf); /* space for rent */
2245 if (padlen < 0)
2246 padlen = 0;
2247 pn2 = pn = p + padlen;
2248 r = winmsg_numrend;
2249 while (p >= buf)
2251 if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
2253 winmsg_rendpos[--r] = pn - buf;
2254 continue;
2256 *pn-- = *p;
2257 if (*p-- == 127)
2259 pn[1] = ' ';
2260 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2261 padlen -= i;
2262 while (i-- > 0)
2263 *pn-- = ' ';
2264 numpad--;
2265 if (r && p - buf == winmsg_rendpos[r - 1])
2266 winmsg_rendpos[--r] = pn - buf;
2269 return pn2;
2272 struct backtick {
2273 struct backtick *next;
2274 int num;
2275 int tick;
2276 int lifespan;
2277 time_t bestbefore;
2278 char result[MAXSTR];
2279 char **cmdv;
2280 struct event ev;
2281 char *buf;
2282 int bufi;
2285 struct backtick *backticks;
2287 static void
2288 backtick_filter(bt)
2289 struct backtick *bt;
2291 char *p, *q;
2292 int c;
2294 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2296 if (c == '\t')
2297 c = ' ';
2298 if (c >= ' ' || c == '\005')
2299 *q++ = c;
2301 *q = 0;
2304 static void
2305 backtick_fn(ev, data)
2306 struct event *ev;
2307 char *data;
2309 struct backtick *bt;
2310 int i, j, k, l;
2312 bt = (struct backtick *)data;
2313 debug1("backtick_fn for #%d\n", bt->num);
2314 i = bt->bufi;
2315 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2316 if (l <= 0)
2318 debug1("EOF on backtick #%d\n", bt->num);
2319 evdeq(ev);
2320 close(ev->fd);
2321 ev->fd = -1;
2322 return;
2324 debug1("read %d bytes\n", l);
2325 i += l;
2326 for (j = 0; j < l; j++)
2327 if (bt->buf[i - j - 1] == '\n')
2328 break;
2329 if (j < l)
2331 for (k = i - j - 2; k >= 0; k--)
2332 if (bt->buf[k] == '\n')
2333 break;
2334 k++;
2335 bcopy(bt->buf + k, bt->result, i - j - k);
2336 bt->result[i - j - k - 1] = 0;
2337 backtick_filter(bt);
2338 WindowChanged(0, '`');
2340 if (j == l && i == MAXSTR)
2342 j = MAXSTR/2;
2343 l = j + 1;
2345 if (j < l)
2347 if (j)
2348 bcopy(bt->buf + i - j, bt->buf, j);
2349 i = j;
2351 bt->bufi = i;
2354 void
2355 setbacktick(num, lifespan, tick, cmdv)
2356 int num;
2357 int lifespan;
2358 int tick;
2359 char **cmdv;
2361 struct backtick **btp, *bt;
2362 char **v;
2364 debug1("setbacktick called for backtick #%d\n", num);
2365 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2366 if (bt->num == num)
2367 break;
2368 if (!bt && !cmdv)
2369 return;
2370 if (bt)
2372 for (v = bt->cmdv; *v; v++)
2373 free(*v);
2374 free(bt->cmdv);
2375 if (bt->buf)
2376 free(bt->buf);
2377 if (bt->ev.fd >= 0)
2378 close(bt->ev.fd);
2379 evdeq(&bt->ev);
2381 if (bt && !cmdv)
2383 *btp = bt->next;
2384 free(bt);
2385 return;
2387 if (!bt)
2389 bt = (struct backtick *)malloc(sizeof *bt);
2390 if (!bt)
2392 Msg(0, "%s", strnomem);
2393 return;
2395 bzero(bt, sizeof(*bt));
2396 bt->next = 0;
2397 *btp = bt;
2399 bt->num = num;
2400 bt->tick = tick;
2401 bt->lifespan = lifespan;
2402 bt->bestbefore = 0;
2403 bt->result[0] = 0;
2404 bt->buf = 0;
2405 bt->bufi = 0;
2406 bt->cmdv = cmdv;
2407 bt->ev.fd = -1;
2408 if (bt->tick == 0 && bt->lifespan == 0)
2410 debug("setbacktick: continuous mode\n");
2411 bt->buf = (char *)malloc(MAXSTR);
2412 if (bt->buf == 0)
2414 Msg(0, "%s", strnomem);
2415 setbacktick(num, 0, 0, (char **)0);
2416 return;
2418 bt->ev.type = EV_READ;
2419 bt->ev.fd = readpipe(bt->cmdv);
2420 bt->ev.handler = backtick_fn;
2421 bt->ev.data = (char *)bt;
2422 if (bt->ev.fd >= 0)
2423 evenq(&bt->ev);
2427 static char *
2428 runbacktick(bt, tickp, now)
2429 struct backtick *bt;
2430 int *tickp;
2431 time_t now;
2433 int f, i, l, j;
2434 time_t now2;
2436 debug1("runbacktick called for backtick #%d\n", bt->num);
2437 if (bt->tick && (!*tickp || bt->tick < *tickp))
2438 *tickp = bt->tick;
2439 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2441 debug1("returning old result (%d)\n", bt->lifespan);
2442 return bt->result;
2444 f = readpipe(bt->cmdv);
2445 if (f == -1)
2446 return bt->result;
2447 i = 0;
2448 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2450 debug1("runbacktick: read %d bytes\n", l);
2451 i += l;
2452 for (j = 1; j < l; j++)
2453 if (bt->result[i - j - 1] == '\n')
2454 break;
2455 if (j == l && i == sizeof(bt->result))
2457 j = sizeof(bt->result) / 2;
2458 l = j + 1;
2460 if (j < l)
2462 bcopy(bt->result + i - j, bt->result, j);
2463 i = j;
2466 close(f);
2467 bt->result[sizeof(bt->result) - 1] = '\n';
2468 if (i && bt->result[i - 1] == '\n')
2469 i--;
2470 debug1("runbacktick: finished, %d bytes\n", i);
2471 bt->result[i] = 0;
2472 backtick_filter(bt);
2473 (void)time(&now2);
2474 bt->bestbefore = now2 + bt->lifespan;
2475 return bt->result;
2479 AddWinMsgRend(str, r)
2480 const char *str;
2481 int r;
2483 if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf ||
2484 str >= winmsg_buf + MAXSTR)
2485 return -1;
2487 winmsg_rend[winmsg_numrend] = r;
2488 winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
2489 winmsg_numrend++;
2491 return 0;
2494 char *
2495 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2496 char *str;
2497 struct win *win;
2498 int esc;
2499 int padlen;
2500 struct event *ev;
2501 int rec;
2503 static int tick;
2504 char *s = str;
2505 register char *p = winmsg_buf;
2506 register int ctrl;
2507 struct timeval now;
2508 struct tm *tm;
2509 int l, i, r;
2510 int num;
2511 int zeroflg;
2512 int longflg;
2513 int minusflg;
2514 int plusflg;
2515 int qmflag = 0, omflag = 0, qmnumrend = 0;
2516 char *qmpos = 0;
2517 int numpad = 0;
2518 int lastpad = 0;
2519 int truncpos = -1;
2520 int truncper = 0;
2521 int trunclong = 0;
2522 struct backtick *bt;
2524 if (winmsg_numrend >= 0)
2525 winmsg_numrend = 0;
2526 else
2527 winmsg_numrend = -winmsg_numrend;
2529 tick = 0;
2530 tm = 0;
2531 ctrl = 0;
2532 gettimeofday(&now, NULL);
2533 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2535 *p = *s;
2536 if (ctrl)
2538 ctrl = 0;
2539 if (*s != '^' && *s >= 64)
2540 *p &= 0x1f;
2541 continue;
2543 if (*s != esc)
2545 if (esc == '%')
2547 switch (*s)
2549 #if 0
2550 case '~':
2551 *p = BELL;
2552 break;
2553 #endif
2554 case '^':
2555 ctrl = 1;
2556 *p-- = '^';
2557 break;
2558 default:
2559 break;
2562 continue;
2564 if (*++s == esc) /* double escape ? */
2565 continue;
2566 if ((plusflg = *s == '+') != 0)
2567 s++;
2568 if ((minusflg = *s == '-') != 0)
2569 s++;
2570 if ((zeroflg = *s == '0') != 0)
2571 s++;
2572 num = 0;
2573 while(*s >= '0' && *s <= '9')
2574 num = num * 10 + (*s++ - '0');
2575 if ((longflg = *s == 'L') != 0)
2576 s++;
2577 switch (*s)
2579 case '?':
2580 p--;
2581 if (qmpos)
2583 if ((!qmflag && !omflag) || omflag == 1)
2585 p = qmpos;
2586 if (qmnumrend < winmsg_numrend)
2587 winmsg_numrend = qmnumrend;
2589 qmpos = 0;
2590 break;
2592 qmpos = p;
2593 qmnumrend = winmsg_numrend;
2594 qmflag = omflag = 0;
2595 break;
2596 case ':':
2597 p--;
2598 if (!qmpos)
2599 break;
2600 if (qmflag && omflag != 1)
2602 omflag = 1;
2603 qmpos = p;
2604 qmnumrend = winmsg_numrend;
2606 else
2608 p = qmpos;
2609 if (qmnumrend < winmsg_numrend)
2610 winmsg_numrend = qmnumrend;
2611 omflag = -1;
2613 break;
2614 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2615 case 'a': case 'A': case 's': case 'c': case 'C':
2616 if (l < 4)
2617 break;
2618 if (tm == 0)
2620 time_t nowsec = now.tv_sec;
2621 tm = localtime(&nowsec);
2623 qmflag = 1;
2624 if (!tick || tick > 3600)
2625 tick = 3600;
2626 switch (*s)
2628 case 'd':
2629 sprintf(p, "%02d", tm->tm_mday % 100);
2630 break;
2631 case 'D':
2632 #ifdef USE_LOCALE
2633 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2634 #else
2635 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2636 #endif
2637 break;
2638 case 'm':
2639 sprintf(p, "%02d", tm->tm_mon + 1);
2640 break;
2641 case 'M':
2642 #ifdef USE_LOCALE
2643 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2644 #else
2645 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2646 #endif
2647 break;
2648 case 'y':
2649 sprintf(p, "%02d", tm->tm_year % 100);
2650 break;
2651 case 'Y':
2652 sprintf(p, "%04d", tm->tm_year + 1900);
2653 break;
2654 case 'a':
2655 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2656 break;
2657 case 'A':
2658 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2659 break;
2660 case 's':
2661 sprintf(p, "%02d", tm->tm_sec);
2662 tick = 1;
2663 break;
2664 case 'c':
2665 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2666 if (!tick || tick > 60)
2667 tick = 60;
2668 break;
2669 case 'C':
2670 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2671 if (!tick || tick > 60)
2672 tick = 60;
2673 break;
2674 default:
2675 break;
2677 p += strlen(p) - 1;
2678 break;
2679 case 'l':
2680 #ifdef LOADAV
2681 *p = 0;
2682 if (l > 20)
2683 AddLoadav(p);
2684 if (*p)
2686 qmflag = 1;
2687 p += strlen(p) - 1;
2689 else
2690 *p = '?';
2691 if (!tick || tick > 60)
2692 tick = 60;
2693 #else
2694 *p = '?';
2695 #endif
2696 p += strlen(p) - 1;
2697 break;
2698 case '`':
2699 case 'h':
2700 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2702 p--;
2703 break;
2705 if (*s == '`')
2707 for (bt = backticks; bt; bt = bt->next)
2708 if (bt->num == num)
2709 break;
2710 if (bt == 0)
2712 p--;
2713 break;
2717 char savebuf[sizeof(winmsg_buf)];
2718 int oldtick = tick;
2719 int oldnumrend = winmsg_numrend;
2721 *p = 0;
2722 strcpy(savebuf, winmsg_buf);
2723 winmsg_numrend = -winmsg_numrend;
2724 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2725 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2726 if (!tick || oldtick < tick)
2727 tick = oldtick;
2728 if ((int)strlen(winmsg_buf) < l)
2729 strcat(savebuf, winmsg_buf);
2730 strcpy(winmsg_buf, savebuf);
2731 while (oldnumrend < winmsg_numrend)
2732 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2733 if (*p)
2734 qmflag = 1;
2735 p += strlen(p) - 1;
2737 break;
2738 case 'w':
2739 case 'W':
2741 struct win *oldfore = 0;
2742 char *ss;
2744 if (display)
2746 oldfore = D_fore;
2747 D_fore = win;
2749 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1);
2750 if (display)
2751 D_fore = oldfore;
2753 if (*p)
2754 qmflag = 1;
2755 p += strlen(p) - 1;
2756 break;
2757 case 'u':
2758 *p = 0;
2759 if (win)
2760 AddOtherUsers(p, l - 1, win);
2761 if (*p)
2762 qmflag = 1;
2763 p += strlen(p) - 1;
2764 break;
2765 case 'f':
2766 *p = 0;
2767 if (win)
2768 AddWindowFlags(p, l - 1, win);
2769 if (*p)
2770 qmflag = 1;
2771 p += strlen(p) - 1;
2772 break;
2773 case 't':
2774 *p = 0;
2775 if (win && (int)strlen(win->w_title) < l)
2777 strcpy(p, win->w_title);
2778 if (*p)
2779 qmflag = 1;
2781 p += strlen(p) - 1;
2782 break;
2783 case '{':
2785 char rbuf[128];
2786 s++;
2787 for (i = 0; i < 127; i++)
2788 if (s[i] && s[i] != '}')
2789 rbuf[i] = s[i];
2790 else
2791 break;
2792 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2794 r = -1;
2795 rbuf[i] = 0;
2796 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2797 if (i != 1 || rbuf[0] != '-')
2798 r = ParseAttrColor(rbuf, (char *)0, 0);
2799 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2801 winmsg_rend[winmsg_numrend] = r;
2802 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2803 winmsg_numrend++;
2806 s += i;
2807 p--;
2809 break;
2810 case 'H':
2811 *p = 0;
2812 if ((int)strlen(HostName) < l)
2814 strcpy(p, HostName);
2815 if (*p)
2816 qmflag = 1;
2818 p += strlen(p) - 1;
2819 break;
2820 case 'S':
2822 char *session_name;
2823 *p = 0;
2824 session_name = strchr(SockName, '.') + 1;
2825 if ((int)strlen(session_name) < l)
2827 strcpy(p, session_name);
2828 if (*p)
2829 qmflag = 1;
2831 p += strlen(p) - 1;
2833 break;
2834 case 'p':
2836 sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2837 p += strlen(p) - 1;
2839 break;
2840 case 'F':
2841 p--;
2842 /* small hack */
2843 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2844 minusflg = !minusflg;
2845 if (minusflg)
2846 qmflag = 1;
2847 break;
2848 case 'P':
2849 p--;
2850 if (display && ev && ev != &D_hstatusev) /* Hack */
2852 /* Is the layer in the current canvas in copy mode? */
2853 struct canvas *cv = (struct canvas *)ev->data;
2854 if (ev == &cv->c_captev && cv->c_layer->l_layfn == &MarkLf)
2855 qmflag = 1;
2857 break;
2858 case '>':
2859 truncpos = p - winmsg_buf;
2860 truncper = num > 100 ? 100 : num;
2861 trunclong = longflg;
2862 p--;
2863 break;
2864 case '=':
2865 case '<':
2866 *p = ' ';
2867 if (num || zeroflg || plusflg || longflg || (*s != '='))
2869 /* expand all pads */
2870 if (minusflg)
2872 num = (plusflg ? lastpad : padlen) - num;
2873 if (!plusflg && padlen == 0)
2874 num = p - winmsg_buf;
2875 plusflg = 0;
2877 else if (!zeroflg)
2879 if (*s != '=' && num == 0 && !plusflg)
2880 num = 100;
2881 if (num > 100)
2882 num = 100;
2883 if (padlen == 0)
2884 num = p - winmsg_buf;
2885 else
2886 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2888 if (num < 0)
2889 num = 0;
2890 if (plusflg)
2891 num += lastpad;
2892 if (num > MAXSTR - 1)
2893 num = MAXSTR - 1;
2894 if (numpad)
2895 p = pad_expand(winmsg_buf, p, numpad, num);
2896 numpad = 0;
2897 if (p - winmsg_buf > num && !longflg)
2899 int left, trunc;
2901 if (truncpos == -1)
2903 truncpos = lastpad;
2904 truncper = 0;
2906 trunc = lastpad + truncper * (num - lastpad) / 100;
2907 if (trunc > num)
2908 trunc = num;
2909 if (trunc < lastpad)
2910 trunc = lastpad;
2911 left = truncpos - trunc;
2912 if (left > p - winmsg_buf - num)
2913 left = p - winmsg_buf - num;
2914 debug1("lastpad = %d, ", lastpad);
2915 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2916 if (left > 0)
2918 if (left + lastpad > p - winmsg_buf)
2919 left = p - winmsg_buf - lastpad;
2920 if (p - winmsg_buf - lastpad - left > 0)
2921 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2922 p -= left;
2923 r = winmsg_numrend;
2924 while (r && winmsg_rendpos[r - 1] > lastpad)
2926 r--;
2927 winmsg_rendpos[r] -= left;
2928 if (winmsg_rendpos[r] < lastpad)
2929 winmsg_rendpos[r] = lastpad;
2931 if (trunclong)
2933 if (p - winmsg_buf > lastpad)
2934 winmsg_buf[lastpad] = '.';
2935 if (p - winmsg_buf > lastpad + 1)
2936 winmsg_buf[lastpad + 1] = '.';
2937 if (p - winmsg_buf > lastpad + 2)
2938 winmsg_buf[lastpad + 2] = '.';
2941 if (p - winmsg_buf > num)
2943 p = winmsg_buf + num;
2944 if (trunclong)
2946 if (num - 1 >= lastpad)
2947 p[-1] = '.';
2948 if (num - 2 >= lastpad)
2949 p[-2] = '.';
2950 if (num - 3 >= lastpad)
2951 p[-3] = '.';
2953 r = winmsg_numrend;
2954 while (r && winmsg_rendpos[r - 1] > num)
2955 winmsg_rendpos[--r] = num;
2957 truncpos = -1;
2958 trunclong = 0;
2959 if (lastpad > p - winmsg_buf)
2960 lastpad = p - winmsg_buf;
2961 debug1("lastpad now %d\n", lastpad);
2963 if (*s == '=')
2965 while (p - winmsg_buf < num)
2966 *p++ = ' ';
2967 lastpad = p - winmsg_buf;
2968 truncpos = -1;
2969 trunclong = 0;
2970 debug1("lastpad2 now %d\n", lastpad);
2972 p--;
2974 else if (padlen)
2976 *p = 127; /* internal pad representation */
2977 numpad++;
2979 break;
2980 case 'n':
2981 s++;
2982 /* FALLTHROUGH */
2983 default:
2984 s--;
2985 if (l > 10 + num)
2987 if (num == 0)
2988 num = 1;
2989 if (!win)
2990 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2991 else
2992 sprintf(p, "%*d", num, win->w_number);
2993 qmflag = 1;
2994 p += strlen(p) - 1;
2996 break;
2999 if (qmpos && !qmflag)
3000 p = qmpos + 1;
3001 *p = '\0';
3002 if (numpad)
3004 if (padlen > MAXSTR - 1)
3005 padlen = MAXSTR - 1;
3006 p = pad_expand(winmsg_buf, p, numpad, padlen);
3008 if (ev)
3010 evdeq(ev); /* just in case */
3011 ev->timeout.tv_sec = 0;
3012 ev->timeout.tv_usec = 0;
3014 if (ev && tick)
3016 now.tv_usec = 100000;
3017 if (tick == 1)
3018 now.tv_sec++;
3019 else
3020 now.tv_sec += tick - (now.tv_sec % tick);
3021 ev->timeout = now;
3022 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
3024 return winmsg_buf;
3027 char *
3028 MakeWinMsg(s, win, esc)
3029 char *s;
3030 struct win *win;
3031 int esc;
3033 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
3036 void
3037 PutWinMsg(s, start, max)
3038 char *s;
3039 int start, max;
3041 int i, p, l, r, n;
3042 struct mchar rend;
3043 struct mchar rendstack[MAX_WINMSG_REND];
3044 int rendstackn = 0;
3046 if (s != winmsg_buf)
3048 /* sorry, no fancy coloring available */
3049 debug1("PutWinMsg %s plain\n", s);
3050 l = strlen(s);
3051 if (l > max)
3052 l = max;
3053 l -= start;
3054 s += start;
3055 while (l-- > 0)
3056 PUTCHARLP(*s++);
3057 return;
3059 rend = D_rend;
3060 p = 0;
3061 l = strlen(s);
3062 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3063 for (i = 0; i < winmsg_numrend && max > 0; i++)
3065 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3066 break;
3067 if (p < winmsg_rendpos[i])
3069 n = winmsg_rendpos[i] - p;
3070 if (n > max)
3071 n = max;
3072 max -= n;
3073 p += n;
3074 while(n-- > 0)
3076 if (start-- > 0)
3077 s++;
3078 else
3079 PUTCHARLP(*s++);
3082 r = winmsg_rend[i];
3083 if (r == -1)
3085 if (rendstackn > 0)
3086 rend = rendstack[--rendstackn];
3088 else
3090 rendstack[rendstackn++] = rend;
3091 ApplyAttrColor(r, &rend);
3093 SetRendition(&rend);
3095 if (p < l)
3097 n = l - p;
3098 if (n > max)
3099 n = max;
3100 while(n-- > 0)
3102 if (start-- > 0)
3103 s++;
3104 else
3105 PUTCHARLP(*s++);
3111 #ifdef DEBUG
3112 static void
3113 fds1(i, j)
3114 int i, j;
3116 while (i < j)
3118 debug1("%d ", i);
3119 i++;
3121 if ((j = open("/dev/null", 0)) >= 0)
3123 fds1(i + 1, j);
3124 close(j);
3126 else
3128 while (dup(++i) < 0 && errno != EBADF)
3129 debug1("%d ", i);
3130 debug1(" [%d]\n", i);
3134 static void
3135 fds()
3137 debug("fds: ");
3138 fds1(-1, -1);
3140 #endif
3142 static void
3143 serv_read_fn(ev, data)
3144 struct event *ev;
3145 char *data;
3147 debug("Knock - knock!\n");
3148 ReceiveMsg();
3151 static void
3152 serv_select_fn(ev, data)
3153 struct event *ev;
3154 char *data;
3156 struct win *p;
3158 debug("serv_select_fn called\n");
3159 /* XXX: messages?? */
3160 if (GotSigChld)
3162 SigChldHandler();
3164 if (InterruptPlease)
3166 debug("Backend received interrupt\n");
3167 /* This approach is rather questionable in a multi-display
3168 * environment */
3169 if (fore && displays)
3171 #if defined(TERMIO) || defined(POSIX)
3172 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3173 #else
3174 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3175 #endif
3176 #ifdef PSEUDOS
3177 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3178 &ibuf, 1);
3179 debug1("Backend wrote interrupt to %d", fore->w_number);
3180 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3181 #else
3182 write(fore->w_ptyfd, &ibuf, 1);
3183 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3184 #endif
3186 InterruptPlease = 0;
3189 for (p = windows; p; p = p->w_next)
3191 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3193 struct canvas *cv;
3194 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3195 p->w_bell = BELL_ON;
3196 for (display = displays; display; display = display->d_next)
3198 for (cv = D_cvlist; cv; cv = cv->c_next)
3199 if (cv->c_layer->l_bottom == &p->w_layer)
3200 break;
3201 if (cv == 0)
3203 p->w_bell = BELL_DONE;
3204 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3206 else if (visual && !D_VB && (!D_status || !D_status_bell))
3208 Msg(0, "%s", VisualBellString);
3209 if (D_status)
3211 D_status_bell = 1;
3212 debug1("using vbell timeout %d\n", VBellWait);
3213 SetTimeout(&D_statusev, VBellWait );
3217 /* don't annoy the user with two messages */
3218 if (p->w_monitor == MON_FOUND)
3219 p->w_monitor = MON_DONE;
3220 WindowChanged(p, 'f');
3222 if (p->w_monitor == MON_FOUND)
3224 struct canvas *cv;
3225 p->w_monitor = MON_ON;
3226 for (display = displays; display; display = display->d_next)
3228 for (cv = D_cvlist; cv; cv = cv->c_next)
3229 if (cv->c_layer->l_bottom == &p->w_layer)
3230 break;
3231 if (cv)
3232 continue; /* user already sees window */
3233 #ifdef MULTIUSER
3234 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3235 continue; /* user doesn't care */
3236 #endif
3237 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3238 p->w_monitor = MON_DONE;
3240 WindowChanged(p, 'f');
3244 for (display = displays; display; display = display->d_next)
3246 struct canvas *cv;
3247 if (D_status == STATUS_ON_WIN)
3248 continue;
3249 /* XXX: should use display functions! */
3250 for (cv = D_cvlist; cv; cv = cv->c_next)
3252 int lx, ly;
3254 /* normalize window, see resize.c */
3255 lx = cv->c_layer->l_x;
3256 ly = cv->c_layer->l_y;
3257 if (lx == cv->c_layer->l_width)
3258 lx--;
3259 if (ly + cv->c_yoff < cv->c_ys)
3261 int i, n = cv->c_ys - (ly + cv->c_yoff);
3262 cv->c_yoff = cv->c_ys - ly;
3263 RethinkViewportOffsets(cv);
3264 if (n > cv->c_layer->l_height)
3265 n = cv->c_layer->l_height;
3266 CV_CALL(cv,
3267 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3268 LayRedisplayLine(-1, -1, -1, 1);
3269 for (i = 0; i < n; i++)
3270 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3271 if (cv == cv->c_display->d_forecv)
3272 LaySetCursor();
3275 else if (ly + cv->c_yoff > cv->c_ye)
3277 int i, n = ly + cv->c_yoff - cv->c_ye;
3278 cv->c_yoff = cv->c_ye - ly;
3279 RethinkViewportOffsets(cv);
3280 if (n > cv->c_layer->l_height)
3281 n = cv->c_layer->l_height;
3282 CV_CALL(cv,
3283 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3284 LayRedisplayLine(-1, -1, -1, 1);
3285 for (i = 0; i < n; i++)
3286 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3287 if (cv == cv->c_display->d_forecv)
3288 LaySetCursor();
3291 if (lx + cv->c_xoff < cv->c_xs)
3293 int i, n = cv->c_xs - (lx + cv->c_xoff);
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_xs)
3297 n = cv->c_xs - cv->c_xoff;
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, 0, n - 1, 1);
3309 if (cv == cv->c_display->d_forecv)
3310 LaySetCursor();
3313 else if (lx + cv->c_xoff > cv->c_xe)
3315 int i, n = lx + cv->c_xoff - cv->c_xe;
3316 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3317 n = (cv->c_xe - cv->c_xs + 1) / 2;
3318 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3319 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3320 cv->c_xoff -= n;
3321 RethinkViewportOffsets(cv);
3322 if (n > cv->c_layer->l_width)
3323 n = cv->c_layer->l_width;
3324 CV_CALL(cv,
3325 LayRedisplayLine(-1, -1, -1, 1);
3326 for (i = 0; i < flayer->l_height; i++)
3328 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3329 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3331 if (cv == cv->c_display->d_forecv)
3332 LaySetCursor();
3338 for (display = displays; display; display = display->d_next)
3340 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3341 continue;
3342 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3343 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3347 static void
3348 logflush_fn(ev, data)
3349 struct event *ev;
3350 char *data;
3352 struct win *p;
3353 char *buf;
3354 int n;
3356 if (!islogfile(NULL))
3357 return; /* no more logfiles */
3358 logfflush(NULL);
3359 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3360 if (n)
3362 SetTimeout(ev, n * 1000);
3363 evenq(ev); /* re-enqueue ourself */
3365 if (!logtstamp_on)
3366 return;
3367 /* write fancy time-stamp */
3368 for (p = windows; p; p = p->w_next)
3370 if (!p->w_log)
3371 continue;
3372 p->w_logsilence += n;
3373 if (p->w_logsilence < logtstamp_after)
3374 continue;
3375 if (p->w_logsilence - n >= logtstamp_after)
3376 continue;
3377 buf = MakeWinMsg(logtstamp_string, p, '%');
3378 logfwrite(p->w_log, buf, strlen(buf));
3383 * Interprets ^?, ^@ and other ^-control-char notation.
3384 * Interprets \ddd octal notation
3386 * The result is placed in *cp, p is advanced behind the parsed expression and
3387 * returned.
3389 static char *
3390 ParseChar(p, cp)
3391 char *p, *cp;
3393 if (*p == 0)
3394 return 0;
3395 if (*p == '^' && p[1])
3397 if (*++p == '?')
3398 *cp = '\177';
3399 else if (*p >= '@')
3400 *cp = Ctrl(*p);
3401 else
3402 return 0;
3403 ++p;
3405 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3407 *cp = 0;
3409 *cp = *cp * 8 + *p - '0';
3410 while (*++p <= '7' && *p >= '0');
3412 else
3413 *cp = *p++;
3414 return p;
3417 static int
3418 ParseEscape(p)
3419 char *p;
3421 unsigned char buf[2];
3423 if (*p == 0)
3424 SetEscape((struct acluser *)0, -1, -1);
3425 else
3427 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3428 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3429 return -1;
3430 SetEscape((struct acluser *)0, buf[0], buf[1]);
3432 return 0;