Allow user to bypass sleep of 5 seconds.
[screen-lua.git] / src / screen.c
blobea8f83f4d96aed54a7bc6fb3e60b3e2bc4fadf56
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
385 char *sty = 0;
387 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
388 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
389 #endif
390 #if defined(sun) && defined(SVR4)
392 /* Solaris' login blocks SIGHUP! This is _very bad_ */
393 sigset_t sset;
394 sigemptyset(&sset);
395 sigprocmask(SIG_SETMASK, &sset, 0);
397 #endif
400 * First, close all unused descriptors
401 * (otherwise, we might have problems with the select() call)
403 closeallfiles(0);
404 #ifdef DEBUG
405 opendebug(1, 0);
406 #endif
407 snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
408 PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
409 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
410 debug2("-- screen debug started %s (%s)\n", *av, version);
411 #ifdef POSIX
412 debug("POSIX\n");
413 #endif
414 #ifdef TERMIO
415 debug("TERMIO\n");
416 #endif
417 #ifdef SYSV
418 debug("SYSV\n");
419 #endif
420 #ifdef SYSVSIGS
421 debug("SYSVSIGS\n");
422 #endif
423 #ifdef NAMEDPIPE
424 debug("NAMEDPIPE\n");
425 #endif
426 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
427 debug("Window size changing enabled\n");
428 #endif
429 #ifdef HAVE_SETREUID
430 debug("SETREUID\n");
431 #endif
432 #ifdef HAVE_SETEUID
433 debug("SETEUID\n");
434 #endif
435 #ifdef hpux
436 debug("hpux\n");
437 #endif
438 #ifdef USEBCOPY
439 debug("USEBCOPY\n");
440 #endif
441 #ifdef UTMPOK
442 debug("UTMPOK\n");
443 #endif
444 #ifdef LOADAV
445 debug("LOADAV\n");
446 #endif
447 #ifdef NETHACK
448 debug("NETHACK\n");
449 #endif
450 #ifdef TERMINFO
451 debug("TERMINFO\n");
452 #endif
453 #ifdef SHADOWPW
454 debug("SHADOWPW\n");
455 #endif
456 #ifdef NAME_MAX
457 debug1("NAME_MAX = %d\n", NAME_MAX);
458 #endif
460 BellString = SaveStr("Bell in window %n");
461 VisualBellString = SaveStr(" Wuff, Wuff!! ");
462 ActivityString = SaveStr("Activity in window %n");
463 screenlogfile = SaveStr("screenlog.%n");
464 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
465 hstatusstring = SaveStr("%h");
466 captionstring = SaveStr("%3n %t");
467 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
468 wlisttit = SaveStr("Num Name%=Flags");
469 wliststr = SaveStr("%3n %t%=%f");
470 #ifdef COPY_PASTE
471 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
472 #endif
473 ShellProg = NULL;
474 #ifdef POW_DETACH
475 PowDetachString = 0;
476 #endif
477 default_startup = (ac > 1) ? 0 : 1;
478 adaptflag = 0;
479 VBellWait = VBELLWAIT * 1000;
480 MsgWait = MSGWAIT * 1000;
481 MsgMinWait = MSGMINWAIT * 1000;
482 SilenceWait = SILENCEWAIT;
483 #ifdef HAVE_BRAILLE
484 InitBraille();
485 #endif
486 #ifdef ZMODEM
487 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
488 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
489 #endif
491 #ifdef COPY_PASTE
492 CompileKeys((char *)0, 0, mark_key_tab);
493 #endif
494 #ifdef UTF8
495 InitBuiltinTabs();
496 screenencodings = SaveStr(SCREENENCODINGS);
497 #endif
498 #ifdef DW_CHARS
499 cjkwidth = 0;
500 #endif
501 nwin = nwin_undef;
502 nwin_options = nwin_undef;
503 strcpy(screenterm, "screen");
505 logreopen_register(lf_secreopen);
507 av0 = *av;
508 /* if this is a login screen, assume -RR */
509 if (*av0 == '-')
511 rflag = 4;
512 #ifdef MULTI
513 xflag = 1;
514 #else
515 dflag = 1;
516 #endif
517 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
519 while (ac > 0)
521 ap = *++av;
522 if (--ac > 0 && *ap == '-')
524 if (ap[1] == '-' && ap[2] == 0)
526 av++;
527 ac--;
528 break;
530 if (ap[1] == '-' && !strcmp(ap, "--version"))
531 Panic(0, "Screen version %s", version);
532 if (ap[1] == '-' && !strcmp(ap, "--help"))
533 exit_with_usage(myname, NULL, NULL);
534 while (ap && *ap && *++ap)
536 switch (*ap)
538 case 'a':
539 nwin_options.aflag = 1;
540 break;
541 case 'A':
542 adaptflag = 1;
543 break;
544 case 'p': /* preselect */
545 if (*++ap)
546 preselect = ap;
547 else
549 if (!--ac)
550 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
551 preselect = *++av;
553 ap = NULL;
554 break;
555 #ifdef HAVE_BRAILLE
556 case 'B':
557 bd.bd_start_braille = 1;
558 break;
559 #endif
560 case 'c':
561 if (*++ap)
562 RcFileName = ap;
563 else
565 if (--ac == 0)
566 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
567 RcFileName = *++av;
569 ap = NULL;
570 break;
571 case 'e':
572 if (!*++ap)
574 if (--ac == 0)
575 exit_with_usage(myname, "Specify command characters with -e", NULL);
576 ap = *++av;
578 if (ParseEscape(ap))
579 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
580 ap = NULL;
581 break;
582 case 'f':
583 ap++;
584 switch (*ap++)
586 case 'n':
587 case '0':
588 nwin_options.flowflag = FLOW_NOW * 0;
589 break;
590 case '\0':
591 ap--;
592 /* FALLTHROUGH */
593 case 'y':
594 case '1':
595 nwin_options.flowflag = FLOW_NOW * 1;
596 break;
597 case 'a':
598 nwin_options.flowflag = FLOW_AUTOFLAG;
599 break;
600 default:
601 exit_with_usage(myname, "Unknown flow option -%s", --ap);
603 break;
604 case 'h':
605 if (--ac == 0)
606 exit_with_usage(myname, NULL, NULL);
607 nwin_options.histheight = atoi(*++av);
608 if (nwin_options.histheight < 0)
609 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
610 break;
611 case 'i':
612 iflag = 1;
613 break;
614 case 't': /* title, the former AkA == -k */
615 if (--ac == 0)
616 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
617 nwin_options.aka = *++av;
618 break;
619 case 'l':
620 ap++;
621 switch (*ap++)
623 case 'n':
624 case '0':
625 nwin_options.lflag = 0;
626 break;
627 case '\0':
628 ap--;
629 /* FALLTHROUGH */
630 case 'y':
631 case '1':
632 nwin_options.lflag = 1;
633 break;
634 case 'a':
635 nwin_options.lflag = 3;
636 break;
637 case 's': /* -ls */
638 case 'i': /* -list */
639 lsflag = 1;
640 if (ac > 1 && !SockMatch)
642 SockMatch = *++av;
643 ac--;
645 ap = NULL;
646 break;
647 default:
648 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
650 break;
651 case 'w':
652 lsflag = 1;
653 wipeflag = 1;
654 if (ac > 1 && !SockMatch)
656 SockMatch = *++av;
657 ac--;
659 break;
660 case 'L':
661 nwin_options.Lflag = 1;
662 break;
663 case 'm':
664 mflag = 1;
665 break;
666 case 'O': /* to be (or not to be?) deleted. jw. */
667 force_vt = 0;
668 break;
669 case 'T':
670 if (--ac == 0)
671 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
672 if (strlen(*++av) < 20)
673 strcpy(screenterm, *av);
674 else
675 Panic(0, "-T: terminal name too long. (max. 20 char)");
676 nwin_options.term = screenterm;
677 break;
678 case 'q':
679 quietflag = 1;
680 break;
681 case 'r':
682 case 'R':
683 #ifdef MULTI
684 case 'x':
685 #endif
686 if (ac > 1 && *av[1] != '-' && !SockMatch)
688 SockMatch = *++av;
689 ac--;
690 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
692 #ifdef MULTI
693 if (*ap == 'x')
694 xflag = 1;
695 #endif
696 if (rflag)
697 rflag = 2;
698 rflag += (*ap == 'R') ? 2 : 1;
699 break;
700 #ifdef REMOTE_DETACH
701 case 'd':
702 dflag = 1;
703 /* FALLTHROUGH */
704 case 'D':
705 if (!dflag)
706 dflag = 2;
707 if (ac == 2)
709 if (*av[1] != '-' && !SockMatch)
711 SockMatch = *++av;
712 ac--;
713 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
716 break;
717 #endif
718 case 's':
719 if (--ac == 0)
720 exit_with_usage(myname, "Specify shell with -s", NULL);
721 if (ShellProg)
722 free(ShellProg);
723 ShellProg = SaveStr(*++av);
724 debug1("ShellProg: '%s'\n", ShellProg);
725 break;
726 case 'S':
727 if (!SockMatch)
729 if (--ac == 0)
730 exit_with_usage(myname, "Specify session-name with -S", NULL);
731 SockMatch = *++av;
733 if (!*SockMatch)
734 exit_with_usage(myname, "Empty session-name?", NULL);
735 break;
736 case 'X':
737 cmdflag = 1;
738 break;
739 case 'v':
740 Panic(0, "Screen version %s", version);
741 /* NOTREACHED */
742 #ifdef UTF8
743 case 'U':
744 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
745 break;
746 #endif
747 default:
748 exit_with_usage(myname, "Unknown option %s", --ap);
752 else
753 break;
756 real_uid = getuid();
757 real_gid = getgid();
758 eff_uid = geteuid();
759 eff_gid = getegid();
760 if (eff_uid != real_uid)
762 /* if running with s-bit, we must install a special signal
763 * handler routine that resets the s-bit, so that we get a
764 * core file anyway.
766 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
767 signal(SIGBUS, CoreDump);
768 #endif /* SIGBUS */
769 signal(SIGSEGV, CoreDump);
772 #ifdef USE_LOCALE
773 setlocale(LC_ALL, "");
774 #endif
775 #ifdef ENCODINGS
776 if (nwin_options.encoding == -1)
778 /* ask locale if we should start in UTF-8 mode */
779 # ifdef HAVE_NL_LANGINFO
780 # ifndef USE_LOCALE
781 setlocale(LC_CTYPE, "");
782 # endif
783 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
784 debug1("locale says encoding = %d\n", nwin_options.encoding);
785 # else
786 # ifdef UTF8
787 char *s;
788 if ((s = locale_name()) && InStr(s, "UTF-8"))
789 nwin_options.encoding = UTF8;
790 # endif
791 debug1("environment says encoding=%d\n", nwin_options.encoding);
792 #endif
794 # ifdef DW_CHARS
796 char *s;
797 if ((s = locale_name()))
799 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
801 cjkwidth = 1;
805 #endif
806 #endif
807 if (nwin_options.aka)
809 #ifdef ENCODINGS
810 if (nwin_options.encoding > 0)
812 size_t len = strlen(nwin_options.aka);
813 size_t newsz;
814 char *newbuf = malloc(3 * len);
815 if (!newbuf)
816 Panic(0, strnomem);
817 newsz = RecodeBuf(nwin_options.aka, len,
818 nwin_options.encoding, 0, newbuf);
819 newbuf[newsz] = '\0';
820 nwin_options.aka = newbuf;
822 else
823 #endif
825 /* If we just use the original value from av,
826 subsequent shelltitle invocations will attempt to free
827 space we don't own... */
828 nwin_options.aka = SaveStr(nwin_options.aka);
832 if (SockMatch && strlen(SockMatch) >= MAXSTR)
833 Panic(0, "Ridiculously long socketname - try again.");
834 if (cmdflag && !rflag && !dflag && !xflag)
835 xflag = 1;
836 if (!cmdflag && dflag && mflag && !(rflag || xflag))
837 detached = 1;
838 nwin = nwin_options;
839 #ifdef ENCODINGS
840 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
841 #endif
842 if (ac)
843 nwin.args = av;
845 /* make the write() calls return -1 on all errors */
846 #ifdef SIGXFSZ
848 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
849 * It appears that in System V Release 4, UNIX, if you are writing
850 * an output file and you exceed the currently set file size limit,
851 * you _don't_ just get the call to `write' returning with a
852 * failure code. Rather, you get a signal called `SIGXFSZ' which,
853 * if neither handled nor ignored, will cause your program to crash
854 * with a core dump.
856 signal(SIGXFSZ, SIG_IGN);
857 #endif /* SIGXFSZ */
859 #ifdef SIGPIPE
860 signal(SIGPIPE, SIG_IGN);
861 #endif
863 if (!ShellProg)
865 register char *sh;
867 sh = getenv("SHELL");
868 ShellProg = SaveStr(sh ? sh : DefaultShell);
870 ShellArgs[0] = ShellProg;
871 home = getenv("HOME");
872 if (!mflag && !SockMatch)
874 sty = getenv("STY");
875 if (sty && *sty == 0)
876 sty = 0;
879 #ifdef NETHACK
880 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
882 char nethackrc[MAXPATHLEN];
884 if (home && (strlen(home) < (MAXPATHLEN - 20)))
886 sprintf(nethackrc,"%s/.nethackrc", home);
887 nethackflag = !access(nethackrc, F_OK);
890 #endif
892 #ifdef MULTIUSER
893 own_uid = multi_uid = real_uid;
894 if (SockMatch && (sockp = index(SockMatch, '/')))
896 *sockp = 0;
897 multi = SockMatch;
898 SockMatch = sockp + 1;
899 if (*multi)
901 struct passwd *mppp;
902 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
903 Panic(0, "Cannot identify account '%s'.", multi);
904 multi_uid = mppp->pw_uid;
905 multi_home = SaveStr(mppp->pw_dir);
906 if (strlen(multi_home) > MAXPATHLEN - 10)
907 Panic(0, "home directory path too long");
908 # ifdef MULTI
909 /* always fake multi attach mode */
910 if (rflag || lsflag)
911 xflag = 1;
912 # endif /* MULTI */
913 detached = 0;
914 multiattach = 1;
916 /* Special case: effective user is multiuser. */
917 if (eff_uid && (multi_uid != eff_uid))
918 Panic(0, "Must run suid root for multiuser support.");
920 if (SockMatch && *SockMatch == 0)
921 SockMatch = 0;
922 #endif /* MULTIUSER */
924 if ((LoginName = getlogin()) && LoginName[0] != '\0')
926 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
927 if ((int)ppp->pw_uid != real_uid)
928 ppp = (struct passwd *) 0;
930 if (ppp == 0)
932 if ((ppp = getpwuid(real_uid)) == 0)
934 Panic(0, "getpwuid() can't identify your account!");
935 exit(1);
937 LoginName = ppp->pw_name;
939 LoginName = SaveStr(LoginName);
941 ppp = getpwbyname(LoginName, ppp);
943 #if !defined(SOCKDIR) && defined(MULTIUSER)
944 if (multi && !multiattach)
946 if (home && strcmp(home, ppp->pw_dir))
947 Panic(0, "$HOME must match passwd entry for multiuser screens.");
949 #endif
951 #define SET_GUID() do \
953 setgid(real_gid); \
954 setuid(real_uid); \
955 eff_uid = real_uid; \
956 eff_gid = real_gid; \
957 } while (0)
959 #define SET_TTYNAME(fatal) do \
961 if (!(attach_tty = ttyname(0))) \
963 if (fatal) \
964 Panic(0, "Must be connected to a terminal."); \
965 else \
966 attach_tty = ""; \
968 else if (stat(attach_tty, &st)) \
969 Panic(errno, "Cannot access '%s'", attach_tty); \
970 if (strlen(attach_tty) >= MAXPATHLEN) \
971 Panic(0, "TtyName too long - sorry."); \
972 } while (0)
974 if (home == 0 || *home == '\0')
975 home = ppp->pw_dir;
976 if (strlen(LoginName) > 20)
977 Panic(0, "LoginName too long - sorry.");
978 #ifdef MULTIUSER
979 if (multi && strlen(multi) > 20)
980 Panic(0, "Screen owner name too long - sorry.");
981 #endif
982 if (strlen(home) > MAXPATHLEN - 25)
983 Panic(0, "$HOME too long - sorry.");
985 attach_tty = "";
986 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty && !xflag))
988 #ifndef NAMEDPIPE
989 int fl;
990 #endif
992 /* ttyname implies isatty */
993 SET_TTYNAME(1);
994 #ifdef MULTIUSER
995 tty_mode = (int)st.st_mode & 0777;
996 #endif
998 #ifndef NAMEDPIPE
999 fl = fcntl(0, F_GETFL, 0);
1000 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
1001 attach_fd = 0;
1002 #endif
1003 if (attach_fd == -1)
1005 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
1006 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
1007 close(n);
1009 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
1011 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
1012 Panic(0, "Please set a terminal type.");
1013 if (strlen(attach_term) > sizeof(D_termname) - 1)
1014 Panic(0, "$TERM too long - sorry.");
1015 GetTTY(0, &attach_Mode);
1016 #ifdef DEBUGGGGGGGGGGGGGGG
1017 DebugTTY(&attach_Mode);
1018 #endif /* DEBUG */
1021 #ifdef _MODE_T
1022 oumask = umask(0); /* well, unsigned never fails? jw. */
1023 #else
1024 if ((oumask = (int)umask(0)) == -1)
1025 Panic(errno, "Cannot change umask to zero");
1026 #endif
1027 SockDir = getenv("SCREENDIR");
1028 if (SockDir)
1030 if (strlen(SockDir) >= MAXPATHLEN - 1)
1031 Panic(0, "Ridiculously long $SCREENDIR - try again.");
1032 #ifdef MULTIUSER
1033 if (multi)
1034 Panic(0, "No $SCREENDIR with multi screens, please.");
1035 #endif
1037 #ifdef MULTIUSER
1038 if (multiattach)
1040 # ifndef SOCKDIR
1041 sprintf(SockPath, "%s/.screen", multi_home);
1042 SockDir = SockPath;
1043 # else
1044 SockDir = SOCKDIR;
1045 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1046 # endif
1048 else
1049 #endif
1051 #ifndef SOCKDIR
1052 if (SockDir == 0)
1054 sprintf(SockPath, "%s/.screen", home);
1055 SockDir = SockPath;
1057 #endif
1058 if (SockDir)
1060 if (access(SockDir, F_OK))
1062 debug1("SockDir '%s' missing ...\n", SockDir);
1063 if (UserContext() > 0)
1065 if (mkdir(SockDir, 0700))
1066 UserReturn(0);
1067 UserReturn(1);
1069 if (UserStatus() <= 0)
1070 Panic(0, "Cannot make directory '%s'.", SockDir);
1072 if (SockDir != SockPath)
1073 strcpy(SockPath, SockDir);
1075 #ifdef SOCKDIR
1076 else
1078 SockDir = SOCKDIR;
1079 if (lstat(SockDir, &st))
1081 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1082 (eff_gid != real_gid) ? 0775 :
1083 #ifdef S_ISVTX
1084 0777|S_ISVTX;
1085 #else
1086 0777;
1087 #endif
1088 if (mkdir(SockDir, n) == -1)
1089 Panic(errno, "Cannot make directory '%s'", SockDir);
1091 else
1093 if (!S_ISDIR(st.st_mode))
1094 Panic(0, "'%s' must be a directory.", SockDir);
1095 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1096 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1097 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1098 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1099 0777;
1100 if (((int)st.st_mode & 0777) != n)
1101 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1103 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1104 if (access(SockPath, F_OK))
1106 if (mkdir(SockPath, 0700) == -1)
1107 Panic(errno, "Cannot make directory '%s'", SockPath);
1108 (void) chown(SockPath, real_uid, real_gid);
1111 #endif
1114 if (stat(SockPath, &st) == -1)
1115 Panic(errno, "Cannot access %s", SockPath);
1116 else
1117 if (!S_ISDIR(st.st_mode))
1118 Panic(0, "%s is not a directory.", SockPath);
1119 #ifdef MULTIUSER
1120 if (multi)
1122 if ((int)st.st_uid != multi_uid)
1123 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1125 else
1126 #endif
1128 if ((int)st.st_uid != real_uid)
1129 Panic(0, "You are not the owner of %s.", SockPath);
1131 if ((st.st_mode & 0777) != 0700)
1132 Panic(0, "Directory %s must have mode 700.", SockPath);
1133 if (SockMatch && index(SockMatch, '/'))
1134 Panic(0, "Bad session name '%s'", SockMatch);
1135 SockName = SockPath + strlen(SockPath) + 1;
1136 *SockName = 0;
1137 (void) umask(oumask);
1138 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1140 #if defined(SYSV) && !defined(ISC)
1141 if (uname(&utsnam) == -1)
1142 Panic(errno, "uname");
1143 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1144 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1145 #else
1146 (void) gethostname(HostName, MAXSTR);
1147 HostName[MAXSTR - 1] = '\0';
1148 #endif
1149 if ((ap = index(HostName, '.')) != NULL)
1150 *ap = '\0';
1152 if (lsflag)
1154 int i, fo, oth;
1156 #ifdef MULTIUSER
1157 if (multi)
1158 real_uid = multi_uid;
1159 #endif
1160 SET_GUID();
1161 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1162 if (quietflag)
1163 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1164 if (fo == 0)
1165 Panic(0, "No Sockets found in %s.\n", SockPath);
1166 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1167 /* NOTREACHED */
1169 signal(SIG_BYE, AttacherFinit); /* prevent races */
1170 if (cmdflag)
1172 /* attach_tty is not mandatory */
1173 SET_TTYNAME(0);
1174 if (!*av)
1175 Panic(0, "Please specify a command.");
1176 SET_GUID();
1177 SendCmdMessage(sty, SockMatch, av);
1178 exit(0);
1180 else if (rflag || xflag)
1182 debug("screen -r: - is there anybody out there?\n");
1183 if (Attach(MSG_ATTACH))
1185 Attacher();
1186 /* NOTREACHED */
1188 #ifdef MULTIUSER
1189 if (multiattach)
1190 Panic(0, "Can't create sessions of other users.");
1191 #endif
1192 debug("screen -r: backend not responding -- still crying\n");
1194 else if (dflag && !mflag)
1196 SET_TTYNAME(0);
1197 Attach(MSG_DETACH);
1198 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1199 eexit(0);
1200 /* NOTREACHED */
1202 if (!SockMatch && !mflag && sty)
1204 /* attach_tty is not mandatory */
1205 SET_TTYNAME(0);
1206 SET_GUID();
1207 nwin_options.args = av;
1208 SendCreateMsg(sty, &nwin);
1209 exit(0);
1210 /* NOTREACHED */
1212 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1214 if (!detached || dflag != 2)
1215 MasterPid = fork();
1216 else
1217 MasterPid = 0;
1219 switch (MasterPid)
1221 case -1:
1222 Panic(errno, "fork");
1223 /* NOTREACHED */
1224 case 0:
1225 break;
1226 default:
1227 if (detached)
1228 exit(0);
1229 if (SockMatch)
1230 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1231 else
1232 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1233 for (ap = socknamebuf; *ap; ap++)
1234 if (*ap == '/')
1235 *ap = '-';
1236 #ifdef NAME_MAX
1237 if (strlen(socknamebuf) > NAME_MAX)
1238 socknamebuf[NAME_MAX] = 0;
1239 #endif
1240 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1241 SET_GUID();
1242 Attacher();
1243 /* NOTREACHED */
1246 if (!detached)
1247 PanicPid = getppid();
1249 if (DefaultEsc == -1)
1250 DefaultEsc = Ctrl('a');
1251 if (DefaultMetaEsc == -1)
1252 DefaultMetaEsc = 'a';
1254 ap = av0 + strlen(av0) - 1;
1255 while (ap >= av0)
1257 if (!strncmp("screen", ap, 6))
1259 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1260 break;
1262 ap--;
1264 if (ap < av0)
1265 *av0 = 'S';
1267 #ifdef DEBUG
1269 char buf[256];
1271 if (dfp && dfp != stderr)
1272 fclose(dfp);
1273 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1274 if ((dfp = fopen(buf, "w")) == NULL)
1275 dfp = stderr;
1276 else
1277 (void) chmod(buf, 0666);
1279 #endif
1280 if (!detached)
1282 if (attach_fd == -1)
1284 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1285 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1287 else
1288 n = dup(attach_fd);
1290 else
1291 n = -1;
1292 freopen("/dev/null", "r", stdin);
1293 freopen("/dev/null", "w", stdout);
1295 #ifdef DEBUG
1296 if (dfp != stderr)
1297 #endif
1298 freopen("/dev/null", "w", stderr);
1299 debug("-- screen.back debug started\n");
1302 * This guarantees that the session owner is listed, even when we
1303 * start detached. From now on we should not refer to 'LoginName'
1304 * any more, use users->u_name instead.
1306 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1307 Panic(0, "Could not create user info");
1308 if (!detached)
1310 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1311 Panic(0, "Could not alloc display");
1312 PanicPid = 0;
1313 #ifdef ENCODINGS
1314 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1315 debug1("D_encoding = %d\n", D_encoding);
1316 #endif
1319 if (SockMatch)
1321 /* user started us with -S option */
1322 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1324 else
1326 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1327 HostName);
1329 for (ap = socknamebuf; *ap; ap++)
1330 if (*ap == '/')
1331 *ap = '-';
1332 #ifdef NAME_MAX
1333 if (strlen(socknamebuf) > NAME_MAX)
1335 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1336 socknamebuf[NAME_MAX] = 0;
1338 #endif
1339 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1341 ServerSocket = MakeServerSocket();
1342 InitKeytab();
1343 #ifdef ETCSCREENRC
1344 # ifdef ALLOW_SYSSCREENRC
1345 if ((ap = getenv("SYSSCREENRC")))
1346 (void)StartRc(ap, 0);
1347 else
1348 # endif
1349 (void)StartRc(ETCSCREENRC, 0);
1350 #endif
1351 (void)StartRc(RcFileName, 0);
1352 # ifdef UTMPOK
1353 # ifndef UTNOKEEP
1354 InitUtmp();
1355 # endif /* UTNOKEEP */
1356 # endif /* UTMPOK */
1357 if (display)
1359 if (InitTermcap(0, 0))
1361 debug("Could not init termcap - exiting\n");
1362 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1363 freetty();
1364 if (D_userpid)
1365 Kill(D_userpid, SIG_BYE);
1366 eexit(1);
1368 MakeDefaultCanvas();
1369 InitTerm(0);
1370 #ifdef UTMPOK
1371 RemoveLoginSlot();
1372 #endif
1374 else
1375 MakeTermcap(1);
1376 #ifdef LOADAV
1377 InitLoadav();
1378 #endif /* LOADAV */
1379 MakeNewEnv();
1380 signal(SIGHUP, SigHup);
1381 signal(SIGINT, FinitHandler);
1382 signal(SIGQUIT, FinitHandler);
1383 signal(SIGTERM, FinitHandler);
1384 #ifdef BSDJOBS
1385 signal(SIGTTIN, SIG_IGN);
1386 signal(SIGTTOU, SIG_IGN);
1387 #endif
1389 if (display)
1391 brktty(D_userfd);
1392 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1393 /* Note: SetMode must be called _before_ FinishRc. */
1394 SetTTY(D_userfd, &D_NewMode);
1395 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1396 Msg(errno, "Warning: NBLOCK fcntl failed");
1398 else
1399 brktty(-1); /* just try */
1400 signal(SIGCHLD, SigChld);
1401 #ifdef ETCSCREENRC
1402 # ifdef ALLOW_SYSSCREENRC
1403 if ((ap = getenv("SYSSCREENRC")))
1404 FinishRc(ap);
1405 else
1406 # endif
1407 FinishRc(ETCSCREENRC);
1408 #endif
1409 FinishRc(RcFileName);
1411 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1412 if (windows == NULL)
1414 debug("We open one default window, as screenrc did not specify one.\n");
1415 if (MakeWindow(&nwin) == -1)
1417 fd_set rfd;
1418 struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
1419 FD_SET(0, &rfd);
1421 Msg(0, "Sorry, could not find a PTY or TTY.");
1422 // allow user to exit early by pressing any key.
1423 select(1, &rfd, NULL, NULL, &tv);
1424 Finit(0);
1425 /* NOTREACHED */
1428 else if (ac) /* Screen was invoked with a command */
1430 MakeWindow(&nwin);
1433 #ifdef HAVE_BRAILLE
1434 StartBraille();
1435 #endif
1437 if (display && default_startup)
1438 display_copyright();
1439 signal(SIGINT, SigInt);
1440 if (rflag && (rflag & 1) == 0 && !quietflag)
1442 Msg(0, "New screen...");
1443 rflag = 0;
1446 serv_read.type = EV_READ;
1447 serv_read.fd = ServerSocket;
1448 serv_read.handler = serv_read_fn;
1449 evenq(&serv_read);
1451 serv_select.pri = -10;
1452 serv_select.type = EV_ALWAYS;
1453 serv_select.handler = serv_select_fn;
1454 evenq(&serv_select);
1456 logflushev.type = EV_TIMEOUT;
1457 logflushev.handler = logflush_fn;
1459 sched();
1460 /* NOTREACHED */
1461 return 0;
1464 void
1465 WindowDied(p, wstat, wstat_valid)
1466 struct win *p;
1467 #ifdef BSDWAIT
1468 union wait wstat;
1469 #else
1470 int wstat;
1471 #endif
1472 int wstat_valid;
1474 int killit = 0;
1476 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1477 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1478 killit = 1;
1480 if (ZombieKey_destroy && !killit)
1482 char buf[100], *s, reason[100];
1483 time_t now;
1485 if (wstat_valid) {
1486 if (WIFEXITED(wstat))
1487 if (WEXITSTATUS(wstat))
1488 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1489 else
1490 sprintf(reason, "terminated normally");
1491 else if (WIFSIGNALED(wstat))
1492 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1493 #ifdef WCOREDUMP
1494 WCOREDUMP(wstat) ? " (core file generated)" : "");
1495 #else
1496 "");
1497 #endif
1498 } else
1499 sprintf(reason, "detached from window");
1501 (void) time(&now);
1502 s = ctime(&now);
1503 if (s && *s)
1504 s[strlen(s) - 1] = '\0';
1505 debug3("window %d (%s) going into zombie state fd %d",
1506 p->w_number, p->w_title, p->w_ptyfd);
1507 #ifdef UTMPOK
1508 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1510 RemoveUtmp(p);
1511 p->w_slot = 0; /* "detached" */
1513 #endif
1514 CloseDevice(p);
1516 p->w_deadpid = p->w_pid;
1517 p->w_pid = 0;
1518 ResetWindow(p);
1519 /* p->w_y = p->w_bot; */
1520 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1521 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1522 WriteString(p, buf, strlen(buf));
1523 WindowChanged(p, 'f');
1525 else
1526 KillWindow(p);
1527 #ifdef UTMPOK
1528 CarefulUtmp();
1529 #endif
1532 static void
1533 SigChldHandler()
1535 struct stat st;
1536 #ifdef DEBUG
1537 fds();
1538 #endif
1539 while (GotSigChld)
1541 GotSigChld = 0;
1542 DoWait();
1543 #ifdef SYSVSIGS
1544 signal(SIGCHLD, SigChld);
1545 #endif
1547 if (stat(SockPath, &st) == -1)
1549 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1550 if (!RecoverSocket())
1552 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1553 Finit(1);
1555 else
1556 debug1("'%s' reconstructed\n", SockPath);
1558 else
1559 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1562 static sigret_t
1563 SigChld SIGDEFARG
1565 debug("SigChld()\n");
1566 GotSigChld = 1;
1567 SIGRETURN;
1570 sigret_t
1571 SigHup SIGDEFARG
1573 /* Hangup all displays */
1574 while ((display = displays) != 0)
1575 Hangup();
1576 SIGRETURN;
1580 * the backend's Interrupt handler
1581 * we cannot insert the intrc directly, as we never know
1582 * if fore is valid.
1584 static sigret_t
1585 SigInt SIGDEFARG
1587 #if HAZARDOUS
1588 char ibuf;
1590 debug("SigInt()\n");
1591 if (fore && displays)
1593 # if defined(TERMIO) || defined(POSIX)
1594 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1595 # else
1596 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1597 # endif
1598 fore->w_inlen = 0;
1599 write(fore->w_ptyfd, &ibuf, 1);
1601 #else
1602 signal(SIGINT, SigInt);
1603 debug("SigInt() careful\n");
1604 InterruptPlease = 1;
1605 #endif
1606 SIGRETURN;
1609 static sigret_t
1610 CoreDump SIGDEFARG
1612 struct display *disp;
1613 char buf[80];
1615 #if defined(SYSVSIGS) && defined(SIGHASARG)
1616 signal(sigsig, SIG_IGN);
1617 #endif
1618 setgid(getgid());
1619 setuid(getuid());
1620 unlink("core");
1621 #ifdef SIGHASARG
1622 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1623 #else
1624 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1625 #endif
1626 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1628 #else /* SHADOWPW && !DEBUG */
1629 " (core dumped)"
1630 #endif /* SHADOWPW && !DEBUG */
1632 for (disp = displays; disp; disp = disp->d_next)
1634 fcntl(disp->d_userfd, F_SETFL, 0);
1635 SetTTY(disp->d_userfd, &D_OldMode);
1636 write(disp->d_userfd, buf, strlen(buf));
1637 Kill(disp->d_userpid, SIG_BYE);
1639 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1640 Kill(getpid(), SIGKILL);
1641 eexit(11);
1642 #else /* SHADOWPW && !DEBUG */
1643 abort();
1644 #endif /* SHADOWPW && !DEBUG */
1645 SIGRETURN;
1648 static void
1649 DoWait()
1651 register int pid;
1652 struct win *p, *next;
1653 #ifdef BSDWAIT
1654 union wait wstat;
1655 #else
1656 int wstat;
1657 #endif
1659 #ifdef BSDJOBS
1660 # ifndef BSDWAIT
1661 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1662 # else
1663 # ifdef USE_WAIT2
1665 * From: rouilj@sni-usa.com (John Rouillard)
1666 * note that WUNTRACED is not documented to work, but it is defined in
1667 * /usr/include/sys/wait.h, so it may work
1669 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1670 # else /* USE_WAIT2 */
1671 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1672 # endif /* USE_WAIT2 */
1673 # endif
1674 #else /* BSDJOBS */
1675 while ((pid = wait(&wstat)) < 0)
1676 if (errno != EINTR)
1677 break;
1678 if (pid > 0)
1679 #endif /* BSDJOBS */
1681 for (p = windows; p; p = next)
1683 next = p->w_next;
1684 if ( (p->w_pid && pid == p->w_pid) ||
1685 (p->w_deadpid && pid == p->w_deadpid) )
1687 /* child has ceased to exist */
1688 p->w_pid = 0;
1690 #ifdef BSDJOBS
1691 if (WIFSTOPPED(wstat))
1693 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1694 #ifdef SIGTTIN
1695 if (WSTOPSIG(wstat) == SIGTTIN)
1697 Msg(0, "Suspended (tty input)");
1698 continue;
1700 #endif
1701 #ifdef SIGTTOU
1702 if (WSTOPSIG(wstat) == SIGTTOU)
1704 Msg(0, "Suspended (tty output)");
1705 continue;
1707 #endif
1708 /* Try to restart process */
1709 Msg(0, "Child has been stopped, restarting.");
1710 if (killpg(pid, SIGCONT))
1711 kill(pid, SIGCONT);
1713 else
1714 #endif
1716 WindowDied(p, wstat, 1);
1718 break;
1720 #ifdef PSEUDOS
1721 if (p->w_pwin && pid == p->w_pwin->p_pid)
1723 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1724 FreePseudowin(p);
1725 break;
1727 #endif
1729 if (p == 0)
1731 debug1("pid %d not found - hope that's ok\n", pid);
1737 static sigret_t
1738 FinitHandler SIGDEFARG
1740 #ifdef SIGHASARG
1741 debug1("FinitHandler called, sig %d.\n", sigsig);
1742 #else
1743 debug("FinitHandler called.\n");
1744 #endif
1745 Finit(1);
1746 SIGRETURN;
1749 void
1750 Finit(i)
1751 int i;
1753 signal(SIGCHLD, SIG_DFL);
1754 signal(SIGHUP, SIG_IGN);
1755 debug1("Finit(%d);\n", i);
1756 while (windows)
1758 struct win *p = windows;
1759 windows = windows->w_next;
1760 FreeWindow(p);
1762 if (ServerSocket != -1)
1764 debug1("we unlink(%s)\n", SockPath);
1765 #ifdef USE_SETEUID
1766 xseteuid(real_uid);
1767 xsetegid(real_gid);
1768 #endif
1769 (void) unlink(SockPath);
1770 #ifdef USE_SETEUID
1771 xseteuid(eff_uid);
1772 xsetegid(eff_gid);
1773 #endif
1775 for (display = displays; display; display = display->d_next)
1777 if (D_status)
1778 RemoveStatus();
1779 FinitTerm();
1780 #ifdef UTMPOK
1781 RestoreLoginSlot();
1782 #endif
1783 AddStr("[screen is terminating]\r\n");
1784 Flush();
1785 SetTTY(D_userfd, &D_OldMode);
1786 fcntl(D_userfd, F_SETFL, 0);
1787 freetty();
1788 Kill(D_userpid, SIG_BYE);
1791 * we _cannot_ call eexit(i) here,
1792 * instead of playing with the Socket above. Sigh.
1794 exit(i);
1797 void
1798 eexit(e)
1799 int e;
1801 debug("eexit\n");
1802 if (ServerSocket != -1)
1804 debug1("we unlink(%s)\n", SockPath);
1805 setgid(real_gid);
1806 setuid(real_uid);
1807 (void) unlink(SockPath);
1809 exit(e);
1812 void
1813 Hangup()
1815 if (display == 0)
1816 return;
1817 debug1("Hangup %x\n", display);
1818 if (D_userfd >= 0)
1820 close(D_userfd);
1821 D_userfd = -1;
1823 if (auto_detach || displays->d_next)
1824 Detach(D_HANGUP);
1825 else
1826 Finit(0);
1830 * Detach now has the following modes:
1831 *D_DETACH SIG_BYE detach backend and exit attacher
1832 *D_HANGUP SIG_BYE detach backend and exit attacher
1833 *D_STOP SIG_STOP stop attacher (and detach backend)
1834 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1835 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1836 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1837 *D_LOCK SIG_LOCK lock the attacher
1838 * (jw)
1839 * we always remove our utmp slots. (even when "lock" or "stop")
1840 * Note: Take extra care here, we may be called by interrupt!
1842 void
1843 Detach(mode)
1844 int mode;
1846 int sign = 0, pid;
1847 struct canvas *cv;
1848 struct win *p;
1850 if (display == 0)
1851 return;
1853 #define AddStrSock(msg) do { \
1854 if (SockName) \
1856 AddStr("[" msg " from "); \
1857 AddStr(SockName); \
1858 AddStr("]\r\n"); \
1860 else \
1861 AddStr("[" msg "]\r\n"); \
1862 } while (0)
1864 signal(SIGHUP, SIG_IGN);
1865 debug1("Detach(%d)\n", mode);
1866 if (D_status)
1867 RemoveStatus();
1868 FinitTerm();
1869 if (!display)
1870 return;
1871 switch (mode)
1873 case D_HANGUP:
1874 sign = SIG_BYE;
1875 break;
1876 case D_DETACH:
1877 AddStrSock("detached");
1878 sign = SIG_BYE;
1879 break;
1880 #ifdef BSDJOBS
1881 case D_STOP:
1882 sign = SIG_STOP;
1883 break;
1884 #endif
1885 #ifdef REMOTE_DETACH
1886 case D_REMOTE:
1887 AddStrSock("remote detached");
1888 sign = SIG_BYE;
1889 break;
1890 #endif
1891 #ifdef POW_DETACH
1892 case D_POWER:
1893 AddStrSock("power detached");
1894 if (PowDetachString)
1896 AddStr(PowDetachString);
1897 AddStr("\r\n");
1899 sign = SIG_POWER_BYE;
1900 break;
1901 #ifdef REMOTE_DETACH
1902 case D_REMOTE_POWER:
1903 AddStrSock("remote power detached");
1904 if (PowDetachString)
1906 AddStr(PowDetachString);
1907 AddStr("\r\n");
1909 sign = SIG_POWER_BYE;
1910 break;
1911 #endif
1912 #endif
1913 case D_LOCK:
1914 ClearAll();
1915 sign = SIG_LOCK;
1916 /* tell attacher to lock terminal with a lockprg. */
1917 break;
1919 #ifdef UTMPOK
1920 if (displays->d_next == 0)
1922 for (p = windows; p; p = p->w_next)
1924 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1926 RemoveUtmp(p);
1928 * Set the slot to 0 to get the window
1929 * logged in again.
1931 p->w_slot = (slot_t) 0;
1935 if (mode != D_HANGUP)
1936 RestoreLoginSlot();
1937 #endif
1938 if (displays->d_next == 0 && console_window)
1940 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1942 debug("could not release console - killing window\n");
1943 KillWindow(console_window);
1944 display = displays; /* restore display */
1947 if (D_fore)
1949 #ifdef MULTIUSER
1950 ReleaseAutoWritelock(display, D_fore);
1951 #endif
1952 D_user->u_detachwin = D_fore->w_number;
1953 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1955 AutosaveLayout(D_layout);
1956 layout_last = D_layout;
1957 for (cv = D_cvlist; cv; cv = cv->c_next)
1959 p = Layer2Window(cv->c_layer);
1960 SetCanvasWindow(cv, 0);
1961 if (p)
1962 WindowChanged(p, 'u');
1965 pid = D_userpid;
1966 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1967 FreeDisplay();
1968 if (displays == 0)
1969 /* Flag detached-ness */
1970 (void) chsock();
1972 * tell father what to do. We do that after we
1973 * freed the tty, thus getty feels more comfortable on hpux
1974 * if it was a power detach.
1976 Kill(pid, sign);
1977 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1978 debug("Detach returns, we are successfully detached.\n");
1979 signal(SIGHUP, SigHup);
1980 #undef AddStrSock
1983 static int
1984 IsSymbol(e, s)
1985 char *e, *s;
1987 register int l;
1989 l = strlen(s);
1990 return strncmp(e, s, l) == 0 && e[l] == '=';
1993 void
1994 MakeNewEnv()
1996 register char **op, **np;
1997 static char stybuf[MAXSTR];
1999 for (op = environ; *op; ++op)
2001 if (NewEnv)
2002 free((char *)NewEnv);
2003 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
2004 if (!NewEnv)
2005 Panic(0, strnomem);
2006 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
2007 *np++ = stybuf; /* NewEnv[0] */
2008 *np++ = Term; /* NewEnv[1] */
2009 np++; /* room for SHELL */
2010 #ifdef TIOCSWINSZ
2011 np += 2; /* room for TERMCAP and WINDOW */
2012 #else
2013 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
2014 #endif
2016 for (op = environ; *op; ++op)
2018 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
2019 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
2020 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
2021 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
2023 *np++ = *op;
2025 *np = 0;
2028 void
2029 /*VARARGS2*/
2030 #if defined(USEVARARGS) && defined(__STDC__)
2031 Msg(int err, char *fmt, VA_DOTS)
2032 #else
2033 Msg(err, fmt, VA_DOTS)
2034 int err;
2035 char *fmt;
2036 VA_DECL
2037 #endif
2039 VA_LIST(ap)
2040 char buf[MAXPATHLEN*2];
2041 char *p = buf;
2043 VA_START(ap, fmt);
2044 fmt = DoNLS(fmt);
2045 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2046 VA_END(ap);
2047 if (err)
2049 p += strlen(p);
2050 *p++ = ':';
2051 *p++ = ' ';
2052 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2053 buf[sizeof(buf) - 1] = 0;
2055 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
2057 if (display && displays)
2058 MakeStatus(buf);
2059 else if (displays)
2061 for (display = displays; display; display = display->d_next)
2062 MakeStatus(buf);
2064 else if (display)
2066 /* no displays but a display - must have forked.
2067 * send message to backend!
2069 char *tty = D_usertty;
2070 struct display *olddisplay = display;
2071 display = 0; /* only send once */
2072 SendErrorMsg(tty, buf);
2073 display = olddisplay;
2075 else
2076 printf("%s\r\n", buf);
2080 * Call FinitTerm for all displays, write a message to each and call eexit();
2082 void
2083 /*VARARGS2*/
2084 #if defined(USEVARARGS) && defined(__STDC__)
2085 Panic(int err, char *fmt, VA_DOTS)
2086 #else
2087 Panic(err, fmt, VA_DOTS)
2088 int err;
2089 char *fmt;
2090 VA_DECL
2091 #endif
2093 VA_LIST(ap)
2094 char buf[MAXPATHLEN*2];
2095 char *p = buf;
2097 VA_START(ap, fmt);
2098 fmt = DoNLS(fmt);
2099 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2100 VA_END(ap);
2101 if (err)
2103 p += strlen(p);
2104 *p++ = ':';
2105 *p++ = ' ';
2106 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2107 buf[sizeof(buf) - 1] = 0;
2109 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2110 if (displays == 0 && display == 0)
2112 printf("%s\r\n", buf);
2113 if (PanicPid)
2114 Kill(PanicPid, SIG_BYE);
2116 else if (displays == 0)
2118 /* no displays but a display - must have forked.
2119 * send message to backend!
2121 char *tty = D_usertty;
2122 display = 0;
2123 SendErrorMsg(tty, buf);
2124 sleep(2);
2125 _exit(1);
2127 else
2128 for (display = displays; display; display = display->d_next)
2130 if (D_status)
2131 RemoveStatus();
2132 FinitTerm();
2133 Flush();
2134 #ifdef UTMPOK
2135 RestoreLoginSlot();
2136 #endif
2137 SetTTY(D_userfd, &D_OldMode);
2138 fcntl(D_userfd, F_SETFL, 0);
2139 write(D_userfd, buf, strlen(buf));
2140 write(D_userfd, "\n", 1);
2141 freetty();
2142 if (D_userpid)
2143 Kill(D_userpid, SIG_BYE);
2145 #ifdef MULTIUSER
2146 if (tty_oldmode >= 0)
2148 # ifdef USE_SETEUID
2149 if (setuid(own_uid))
2150 xseteuid(own_uid); /* may be a loop. sigh. */
2151 # else
2152 setuid(own_uid);
2153 # endif
2154 debug1("Panic: changing back modes from %s\n", attach_tty);
2155 chmod(attach_tty, tty_oldmode);
2157 #endif
2158 eexit(1);
2163 * '^' is allowed as an escape mechanism for control characters. jw.
2165 * Added time insertion using ideas/code from /\ndy Jones
2166 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2170 #ifndef USE_LOCALE
2171 static const char days[] = "SunMonTueWedThuFriSat";
2172 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2173 #endif
2175 static char winmsg_buf[MAXSTR];
2176 #define MAX_WINMSG_REND 16 /* rendition changes */
2177 static int winmsg_rend[MAX_WINMSG_REND];
2178 static int winmsg_rendpos[MAX_WINMSG_REND];
2179 static int winmsg_numrend;
2181 static char *
2182 pad_expand(buf, p, numpad, padlen)
2183 char *buf;
2184 char *p;
2185 int numpad;
2186 int padlen;
2188 char *pn, *pn2;
2189 int i, r;
2191 padlen = padlen - (p - buf); /* space for rent */
2192 if (padlen < 0)
2193 padlen = 0;
2194 pn2 = pn = p + padlen;
2195 r = winmsg_numrend;
2196 while (p >= buf)
2198 if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
2200 winmsg_rendpos[--r] = pn - buf;
2201 continue;
2203 *pn-- = *p;
2204 if (*p-- == 127)
2206 pn[1] = ' ';
2207 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2208 padlen -= i;
2209 while (i-- > 0)
2210 *pn-- = ' ';
2211 numpad--;
2212 if (r && p - buf == winmsg_rendpos[r - 1])
2213 winmsg_rendpos[--r] = pn - buf;
2216 return pn2;
2219 struct backtick {
2220 struct backtick *next;
2221 int num;
2222 int tick;
2223 int lifespan;
2224 time_t bestbefore;
2225 char result[MAXSTR];
2226 char **cmdv;
2227 struct event ev;
2228 char *buf;
2229 int bufi;
2232 struct backtick *backticks;
2234 static void
2235 backtick_filter(bt)
2236 struct backtick *bt;
2238 char *p, *q;
2239 int c;
2241 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2243 if (c == '\t')
2244 c = ' ';
2245 if (c >= ' ' || c == '\005')
2246 *q++ = c;
2248 *q = 0;
2251 static void
2252 backtick_fn(ev, data)
2253 struct event *ev;
2254 char *data;
2256 struct backtick *bt;
2257 int i, j, k, l;
2259 bt = (struct backtick *)data;
2260 debug1("backtick_fn for #%d\n", bt->num);
2261 i = bt->bufi;
2262 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2263 if (l <= 0)
2265 debug1("EOF on backtick #%d\n", bt->num);
2266 evdeq(ev);
2267 close(ev->fd);
2268 ev->fd = -1;
2269 return;
2271 debug1("read %d bytes\n", l);
2272 i += l;
2273 for (j = 0; j < l; j++)
2274 if (bt->buf[i - j - 1] == '\n')
2275 break;
2276 if (j < l)
2278 for (k = i - j - 2; k >= 0; k--)
2279 if (bt->buf[k] == '\n')
2280 break;
2281 k++;
2282 bcopy(bt->buf + k, bt->result, i - j - k);
2283 bt->result[i - j - k - 1] = 0;
2284 backtick_filter(bt);
2285 WindowChanged(0, '`');
2287 if (j == l && i == MAXSTR)
2289 j = MAXSTR/2;
2290 l = j + 1;
2292 if (j < l)
2294 if (j)
2295 bcopy(bt->buf + i - j, bt->buf, j);
2296 i = j;
2298 bt->bufi = i;
2301 void
2302 setbacktick(num, lifespan, tick, cmdv)
2303 int num;
2304 int lifespan;
2305 int tick;
2306 char **cmdv;
2308 struct backtick **btp, *bt;
2309 char **v;
2311 debug1("setbacktick called for backtick #%d\n", num);
2312 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2313 if (bt->num == num)
2314 break;
2315 if (!bt && !cmdv)
2316 return;
2317 if (bt)
2319 for (v = bt->cmdv; *v; v++)
2320 free(*v);
2321 free(bt->cmdv);
2322 if (bt->buf)
2323 free(bt->buf);
2324 if (bt->ev.fd >= 0)
2325 close(bt->ev.fd);
2326 evdeq(&bt->ev);
2328 if (bt && !cmdv)
2330 *btp = bt->next;
2331 free(bt);
2332 return;
2334 if (!bt)
2336 bt = (struct backtick *)malloc(sizeof *bt);
2337 if (!bt)
2339 Msg(0, strnomem);
2340 return;
2342 bzero(bt, sizeof(*bt));
2343 bt->next = 0;
2344 *btp = bt;
2346 bt->num = num;
2347 bt->tick = tick;
2348 bt->lifespan = lifespan;
2349 bt->bestbefore = 0;
2350 bt->result[0] = 0;
2351 bt->buf = 0;
2352 bt->bufi = 0;
2353 bt->cmdv = cmdv;
2354 bt->ev.fd = -1;
2355 if (bt->tick == 0 && bt->lifespan == 0)
2357 debug("setbacktick: continuous mode\n");
2358 bt->buf = (char *)malloc(MAXSTR);
2359 if (bt->buf == 0)
2361 Msg(0, strnomem);
2362 setbacktick(num, 0, 0, (char **)0);
2363 return;
2365 bt->ev.type = EV_READ;
2366 bt->ev.fd = readpipe(bt->cmdv);
2367 bt->ev.handler = backtick_fn;
2368 bt->ev.data = (char *)bt;
2369 if (bt->ev.fd >= 0)
2370 evenq(&bt->ev);
2374 static char *
2375 runbacktick(bt, tickp, now)
2376 struct backtick *bt;
2377 int *tickp;
2378 time_t now;
2380 int f, i, l, j;
2381 time_t now2;
2383 debug1("runbacktick called for backtick #%d\n", bt->num);
2384 if (bt->tick && (!*tickp || bt->tick < *tickp))
2385 *tickp = bt->tick;
2386 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2388 debug1("returning old result (%d)\n", bt->lifespan);
2389 return bt->result;
2391 f = readpipe(bt->cmdv);
2392 if (f == -1)
2393 return bt->result;
2394 i = 0;
2395 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2397 debug1("runbacktick: read %d bytes\n", l);
2398 i += l;
2399 for (j = 1; j < l; j++)
2400 if (bt->result[i - j - 1] == '\n')
2401 break;
2402 if (j == l && i == sizeof(bt->result))
2404 j = sizeof(bt->result) / 2;
2405 l = j + 1;
2407 if (j < l)
2409 bcopy(bt->result + i - j, bt->result, j);
2410 i = j;
2413 close(f);
2414 bt->result[sizeof(bt->result) - 1] = '\n';
2415 if (i && bt->result[i - 1] == '\n')
2416 i--;
2417 debug1("runbacktick: finished, %d bytes\n", i);
2418 bt->result[i] = 0;
2419 backtick_filter(bt);
2420 (void)time(&now2);
2421 bt->bestbefore = now2 + bt->lifespan;
2422 return bt->result;
2426 AddWinMsgRend(str, r)
2427 const char *str;
2428 int r;
2430 if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf ||
2431 str >= winmsg_buf + MAXSTR)
2432 return -1;
2434 winmsg_rend[winmsg_numrend] = r;
2435 winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
2436 winmsg_numrend++;
2438 return 0;
2441 char *
2442 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2443 char *str;
2444 struct win *win;
2445 int esc;
2446 int padlen;
2447 struct event *ev;
2448 int rec;
2450 static int tick;
2451 char *s = str;
2452 register char *p = winmsg_buf;
2453 register int ctrl;
2454 struct timeval now;
2455 struct tm *tm;
2456 int l, i, r;
2457 int num;
2458 int zeroflg;
2459 int longflg;
2460 int minusflg;
2461 int plusflg;
2462 int qmflag = 0, omflag = 0, qmnumrend = 0;
2463 char *qmpos = 0;
2464 int numpad = 0;
2465 int lastpad = 0;
2466 int truncpos = -1;
2467 int truncper = 0;
2468 int trunclong = 0;
2469 struct backtick *bt;
2471 if (winmsg_numrend >= 0)
2472 winmsg_numrend = 0;
2473 else
2474 winmsg_numrend = -winmsg_numrend;
2476 tick = 0;
2477 tm = 0;
2478 ctrl = 0;
2479 gettimeofday(&now, NULL);
2480 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2482 *p = *s;
2483 if (ctrl)
2485 ctrl = 0;
2486 if (*s != '^' && *s >= 64)
2487 *p &= 0x1f;
2488 continue;
2490 if (*s != esc)
2492 if (esc == '%')
2494 switch (*s)
2496 #if 0
2497 case '~':
2498 *p = BELL;
2499 break;
2500 #endif
2501 case '^':
2502 ctrl = 1;
2503 *p-- = '^';
2504 break;
2505 default:
2506 break;
2509 continue;
2511 if (*++s == esc) /* double escape ? */
2512 continue;
2513 if ((plusflg = *s == '+') != 0)
2514 s++;
2515 if ((minusflg = *s == '-') != 0)
2516 s++;
2517 if ((zeroflg = *s == '0') != 0)
2518 s++;
2519 num = 0;
2520 while(*s >= '0' && *s <= '9')
2521 num = num * 10 + (*s++ - '0');
2522 if ((longflg = *s == 'L') != 0)
2523 s++;
2524 switch (*s)
2526 case '?':
2527 p--;
2528 if (qmpos)
2530 if ((!qmflag && !omflag) || omflag == 1)
2532 p = qmpos;
2533 if (qmnumrend < winmsg_numrend)
2534 winmsg_numrend = qmnumrend;
2536 qmpos = 0;
2537 break;
2539 qmpos = p;
2540 qmnumrend = winmsg_numrend;
2541 qmflag = omflag = 0;
2542 break;
2543 case ':':
2544 p--;
2545 if (!qmpos)
2546 break;
2547 if (qmflag && omflag != 1)
2549 omflag = 1;
2550 qmpos = p;
2551 qmnumrend = winmsg_numrend;
2553 else
2555 p = qmpos;
2556 if (qmnumrend < winmsg_numrend)
2557 winmsg_numrend = qmnumrend;
2558 omflag = -1;
2560 break;
2561 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2562 case 'a': case 'A': case 's': case 'c': case 'C':
2563 if (l < 4)
2564 break;
2565 if (tm == 0)
2567 time_t nowsec = now.tv_sec;
2568 tm = localtime(&nowsec);
2570 qmflag = 1;
2571 if (!tick || tick > 3600)
2572 tick = 3600;
2573 switch (*s)
2575 case 'd':
2576 sprintf(p, "%02d", tm->tm_mday % 100);
2577 break;
2578 case 'D':
2579 #ifdef USE_LOCALE
2580 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2581 #else
2582 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2583 #endif
2584 break;
2585 case 'm':
2586 sprintf(p, "%02d", tm->tm_mon + 1);
2587 break;
2588 case 'M':
2589 #ifdef USE_LOCALE
2590 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2591 #else
2592 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2593 #endif
2594 break;
2595 case 'y':
2596 sprintf(p, "%02d", tm->tm_year % 100);
2597 break;
2598 case 'Y':
2599 sprintf(p, "%04d", tm->tm_year + 1900);
2600 break;
2601 case 'a':
2602 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2603 break;
2604 case 'A':
2605 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2606 break;
2607 case 's':
2608 sprintf(p, "%02d", tm->tm_sec);
2609 tick = 1;
2610 break;
2611 case 'c':
2612 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2613 if (!tick || tick > 60)
2614 tick = 60;
2615 break;
2616 case 'C':
2617 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2618 if (!tick || tick > 60)
2619 tick = 60;
2620 break;
2621 default:
2622 break;
2624 p += strlen(p) - 1;
2625 break;
2626 case 'l':
2627 #ifdef LOADAV
2628 *p = 0;
2629 if (l > 20)
2630 AddLoadav(p);
2631 if (*p)
2633 qmflag = 1;
2634 p += strlen(p) - 1;
2636 else
2637 *p = '?';
2638 if (!tick || tick > 60)
2639 tick = 60;
2640 #else
2641 *p = '?';
2642 #endif
2643 p += strlen(p) - 1;
2644 break;
2645 case '`':
2646 case 'h':
2647 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2649 p--;
2650 break;
2652 if (*s == '`')
2654 for (bt = backticks; bt; bt = bt->next)
2655 if (bt->num == num)
2656 break;
2657 if (bt == 0)
2659 p--;
2660 break;
2664 char savebuf[sizeof(winmsg_buf)];
2665 int oldtick = tick;
2666 int oldnumrend = winmsg_numrend;
2668 *p = 0;
2669 strcpy(savebuf, winmsg_buf);
2670 winmsg_numrend = -winmsg_numrend;
2671 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2672 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2673 if (!tick || oldtick < tick)
2674 tick = oldtick;
2675 if ((int)strlen(winmsg_buf) < l)
2676 strcat(savebuf, winmsg_buf);
2677 strcpy(winmsg_buf, savebuf);
2678 while (oldnumrend < winmsg_numrend)
2679 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2680 if (*p)
2681 qmflag = 1;
2682 p += strlen(p) - 1;
2684 break;
2685 case 'w':
2686 case 'W':
2688 struct win *oldfore = 0;
2689 char *ss;
2691 if (display)
2693 oldfore = D_fore;
2694 D_fore = win;
2696 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1);
2697 if (display)
2698 D_fore = oldfore;
2700 if (*p)
2701 qmflag = 1;
2702 p += strlen(p) - 1;
2703 break;
2704 case 'u':
2705 *p = 0;
2706 if (win)
2707 AddOtherUsers(p, l - 1, win);
2708 if (*p)
2709 qmflag = 1;
2710 p += strlen(p) - 1;
2711 break;
2712 case 'f':
2713 *p = 0;
2714 if (win)
2715 AddWindowFlags(p, l - 1, win);
2716 if (*p)
2717 qmflag = 1;
2718 p += strlen(p) - 1;
2719 break;
2720 case 't':
2721 *p = 0;
2722 if (win && (int)strlen(win->w_title) < l)
2724 strcpy(p, win->w_title);
2725 if (*p)
2726 qmflag = 1;
2728 p += strlen(p) - 1;
2729 break;
2730 case '{':
2732 char rbuf[128];
2733 s++;
2734 for (i = 0; i < 127; i++)
2735 if (s[i] && s[i] != '}')
2736 rbuf[i] = s[i];
2737 else
2738 break;
2739 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2741 r = -1;
2742 rbuf[i] = 0;
2743 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2744 if (i != 1 || rbuf[0] != '-')
2745 r = ParseAttrColor(rbuf, (char *)0, 0);
2746 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2748 winmsg_rend[winmsg_numrend] = r;
2749 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2750 winmsg_numrend++;
2753 s += i;
2754 p--;
2756 break;
2757 case 'H':
2758 *p = 0;
2759 if ((int)strlen(HostName) < l)
2761 strcpy(p, HostName);
2762 if (*p)
2763 qmflag = 1;
2765 p += strlen(p) - 1;
2766 break;
2767 case 'S':
2769 char *session_name;
2770 *p = 0;
2771 session_name = strchr(SockName, '.') + 1;
2772 if ((int)strlen(session_name) < l)
2774 strcpy(p, session_name);
2775 if (*p)
2776 qmflag = 1;
2778 p += strlen(p) - 1;
2780 break;
2781 case 'p':
2783 sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
2784 p += strlen(p) - 1;
2786 break;
2787 case 'F':
2788 p--;
2789 /* small hack */
2790 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2791 minusflg = !minusflg;
2792 if (minusflg)
2793 qmflag = 1;
2794 break;
2795 case '>':
2796 truncpos = p - winmsg_buf;
2797 truncper = num > 100 ? 100 : num;
2798 trunclong = longflg;
2799 p--;
2800 break;
2801 case '=':
2802 case '<':
2803 *p = ' ';
2804 if (num || zeroflg || plusflg || longflg || (*s != '='))
2806 /* expand all pads */
2807 if (minusflg)
2809 num = (plusflg ? lastpad : padlen) - num;
2810 if (!plusflg && padlen == 0)
2811 num = p - winmsg_buf;
2812 plusflg = 0;
2814 else if (!zeroflg)
2816 if (*s != '=' && num == 0 && !plusflg)
2817 num = 100;
2818 if (num > 100)
2819 num = 100;
2820 if (padlen == 0)
2821 num = p - winmsg_buf;
2822 else
2823 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2825 if (num < 0)
2826 num = 0;
2827 if (plusflg)
2828 num += lastpad;
2829 if (num > MAXSTR - 1)
2830 num = MAXSTR - 1;
2831 if (numpad)
2832 p = pad_expand(winmsg_buf, p, numpad, num);
2833 numpad = 0;
2834 if (p - winmsg_buf > num && !longflg)
2836 int left, trunc;
2838 if (truncpos == -1)
2840 truncpos = lastpad;
2841 truncper = 0;
2843 trunc = lastpad + truncper * (num - lastpad) / 100;
2844 if (trunc > num)
2845 trunc = num;
2846 if (trunc < lastpad)
2847 trunc = lastpad;
2848 left = truncpos - trunc;
2849 if (left > p - winmsg_buf - num)
2850 left = p - winmsg_buf - num;
2851 debug1("lastpad = %d, ", lastpad);
2852 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2853 if (left > 0)
2855 if (left + lastpad > p - winmsg_buf)
2856 left = p - winmsg_buf - lastpad;
2857 if (p - winmsg_buf - lastpad - left > 0)
2858 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2859 p -= left;
2860 r = winmsg_numrend;
2861 while (r && winmsg_rendpos[r - 1] > lastpad)
2863 r--;
2864 winmsg_rendpos[r] -= left;
2865 if (winmsg_rendpos[r] < lastpad)
2866 winmsg_rendpos[r] = lastpad;
2868 if (trunclong)
2870 if (p - winmsg_buf > lastpad)
2871 winmsg_buf[lastpad] = '.';
2872 if (p - winmsg_buf > lastpad + 1)
2873 winmsg_buf[lastpad + 1] = '.';
2874 if (p - winmsg_buf > lastpad + 2)
2875 winmsg_buf[lastpad + 2] = '.';
2878 if (p - winmsg_buf > num)
2880 p = winmsg_buf + num;
2881 if (trunclong)
2883 if (num - 1 >= lastpad)
2884 p[-1] = '.';
2885 if (num - 2 >= lastpad)
2886 p[-2] = '.';
2887 if (num - 3 >= lastpad)
2888 p[-3] = '.';
2890 r = winmsg_numrend;
2891 while (r && winmsg_rendpos[r - 1] > num)
2892 winmsg_rendpos[--r] = num;
2894 truncpos = -1;
2895 trunclong = 0;
2896 if (lastpad > p - winmsg_buf)
2897 lastpad = p - winmsg_buf;
2898 debug1("lastpad now %d\n", lastpad);
2900 if (*s == '=')
2902 while (p - winmsg_buf < num)
2903 *p++ = ' ';
2904 lastpad = p - winmsg_buf;
2905 truncpos = -1;
2906 trunclong = 0;
2907 debug1("lastpad2 now %d\n", lastpad);
2909 p--;
2911 else if (padlen)
2913 *p = 127; /* internal pad representation */
2914 numpad++;
2916 break;
2917 case 'n':
2918 s++;
2919 /* FALLTHROUGH */
2920 default:
2921 s--;
2922 if (l > 10 + num)
2924 if (num == 0)
2925 num = 1;
2926 if (!win)
2927 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2928 else
2929 sprintf(p, "%*d", num, win->w_number);
2930 qmflag = 1;
2931 p += strlen(p) - 1;
2933 break;
2936 if (qmpos && !qmflag)
2937 p = qmpos + 1;
2938 *p = '\0';
2939 if (numpad)
2941 if (padlen > MAXSTR - 1)
2942 padlen = MAXSTR - 1;
2943 p = pad_expand(winmsg_buf, p, numpad, padlen);
2945 if (ev)
2947 evdeq(ev); /* just in case */
2948 ev->timeout.tv_sec = 0;
2949 ev->timeout.tv_usec = 0;
2951 if (ev && tick)
2953 now.tv_usec = 100000;
2954 if (tick == 1)
2955 now.tv_sec++;
2956 else
2957 now.tv_sec += tick - (now.tv_sec % tick);
2958 ev->timeout = now;
2959 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2961 return winmsg_buf;
2964 char *
2965 MakeWinMsg(s, win, esc)
2966 char *s;
2967 struct win *win;
2968 int esc;
2970 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2973 void
2974 PutWinMsg(s, start, max)
2975 char *s;
2976 int start, max;
2978 int i, p, l, r, n;
2979 struct mchar rend;
2980 struct mchar rendstack[MAX_WINMSG_REND];
2981 int rendstackn = 0;
2983 if (s != winmsg_buf)
2985 /* sorry, no fancy coloring available */
2986 debug1("PutWinMsg %s plain\n", s);
2987 l = strlen(s);
2988 if (l > max)
2989 l = max;
2990 l -= start;
2991 s += start;
2992 while (l-- > 0)
2993 PUTCHARLP(*s++);
2994 return;
2996 rend = D_rend;
2997 p = 0;
2998 l = strlen(s);
2999 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
3000 for (i = 0; i < winmsg_numrend && max > 0; i++)
3002 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
3003 break;
3004 if (p < winmsg_rendpos[i])
3006 n = winmsg_rendpos[i] - p;
3007 if (n > max)
3008 n = max;
3009 max -= n;
3010 p += n;
3011 while(n-- > 0)
3013 if (start-- > 0)
3014 s++;
3015 else
3016 PUTCHARLP(*s++);
3019 r = winmsg_rend[i];
3020 if (r == -1)
3022 if (rendstackn > 0)
3023 rend = rendstack[--rendstackn];
3025 else
3027 rendstack[rendstackn++] = rend;
3028 ApplyAttrColor(r, &rend);
3030 SetRendition(&rend);
3032 if (p < l)
3034 n = l - p;
3035 if (n > max)
3036 n = max;
3037 while(n-- > 0)
3039 if (start-- > 0)
3040 s++;
3041 else
3042 PUTCHARLP(*s++);
3048 #ifdef DEBUG
3049 static void
3050 fds1(i, j)
3051 int i, j;
3053 while (i < j)
3055 debug1("%d ", i);
3056 i++;
3058 if ((j = open("/dev/null", 0)) >= 0)
3060 fds1(i + 1, j);
3061 close(j);
3063 else
3065 while (dup(++i) < 0 && errno != EBADF)
3066 debug1("%d ", i);
3067 debug1(" [%d]\n", i);
3071 static void
3072 fds()
3074 debug("fds: ");
3075 fds1(-1, -1);
3077 #endif
3079 static void
3080 serv_read_fn(ev, data)
3081 struct event *ev;
3082 char *data;
3084 debug("Knock - knock!\n");
3085 ReceiveMsg();
3088 static void
3089 serv_select_fn(ev, data)
3090 struct event *ev;
3091 char *data;
3093 struct win *p;
3095 debug("serv_select_fn called\n");
3096 /* XXX: messages?? */
3097 if (GotSigChld)
3099 SigChldHandler();
3101 if (InterruptPlease)
3103 debug("Backend received interrupt\n");
3104 /* This approach is rather questionable in a multi-display
3105 * environment */
3106 if (fore && displays)
3108 #if defined(TERMIO) || defined(POSIX)
3109 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3110 #else
3111 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3112 #endif
3113 #ifdef PSEUDOS
3114 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3115 &ibuf, 1);
3116 debug1("Backend wrote interrupt to %d", fore->w_number);
3117 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3118 #else
3119 write(fore->w_ptyfd, &ibuf, 1);
3120 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3121 #endif
3123 InterruptPlease = 0;
3126 for (p = windows; p; p = p->w_next)
3128 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3130 struct canvas *cv;
3131 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3132 p->w_bell = BELL_ON;
3133 for (display = displays; display; display = display->d_next)
3135 for (cv = D_cvlist; cv; cv = cv->c_next)
3136 if (cv->c_layer->l_bottom == &p->w_layer)
3137 break;
3138 if (cv == 0)
3140 p->w_bell = BELL_DONE;
3141 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3143 else if (visual && !D_VB && (!D_status || !D_status_bell))
3145 Msg(0, "%s", VisualBellString);
3146 if (D_status)
3148 D_status_bell = 1;
3149 debug1("using vbell timeout %d\n", VBellWait);
3150 SetTimeout(&D_statusev, VBellWait );
3154 /* don't annoy the user with two messages */
3155 if (p->w_monitor == MON_FOUND)
3156 p->w_monitor = MON_DONE;
3157 WindowChanged(p, 'f');
3159 if (p->w_monitor == MON_FOUND)
3161 struct canvas *cv;
3162 p->w_monitor = MON_ON;
3163 for (display = displays; display; display = display->d_next)
3165 for (cv = D_cvlist; cv; cv = cv->c_next)
3166 if (cv->c_layer->l_bottom == &p->w_layer)
3167 break;
3168 if (cv)
3169 continue; /* user already sees window */
3170 #ifdef MULTIUSER
3171 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3172 continue; /* user doesn't care */
3173 #endif
3174 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3175 p->w_monitor = MON_DONE;
3177 WindowChanged(p, 'f');
3181 for (display = displays; display; display = display->d_next)
3183 struct canvas *cv;
3184 if (D_status == STATUS_ON_WIN)
3185 continue;
3186 /* XXX: should use display functions! */
3187 for (cv = D_cvlist; cv; cv = cv->c_next)
3189 int lx, ly;
3191 /* normalize window, see resize.c */
3192 lx = cv->c_layer->l_x;
3193 ly = cv->c_layer->l_y;
3194 if (lx == cv->c_layer->l_width)
3195 lx--;
3196 if (ly + cv->c_yoff < cv->c_ys)
3198 int i, n = cv->c_ys - (ly + cv->c_yoff);
3199 cv->c_yoff = cv->c_ys - ly;
3200 RethinkViewportOffsets(cv);
3201 if (n > cv->c_layer->l_height)
3202 n = cv->c_layer->l_height;
3203 CV_CALL(cv,
3204 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3205 LayRedisplayLine(-1, -1, -1, 1);
3206 for (i = 0; i < n; i++)
3207 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3208 if (cv == cv->c_display->d_forecv)
3209 LaySetCursor();
3212 else if (ly + cv->c_yoff > cv->c_ye)
3214 int i, n = ly + cv->c_yoff - cv->c_ye;
3215 cv->c_yoff = cv->c_ye - ly;
3216 RethinkViewportOffsets(cv);
3217 if (n > cv->c_layer->l_height)
3218 n = cv->c_layer->l_height;
3219 CV_CALL(cv,
3220 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3221 LayRedisplayLine(-1, -1, -1, 1);
3222 for (i = 0; i < n; i++)
3223 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3224 if (cv == cv->c_display->d_forecv)
3225 LaySetCursor();
3228 if (lx + cv->c_xoff < cv->c_xs)
3230 int i, n = cv->c_xs - (lx + cv->c_xoff);
3231 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3232 n = (cv->c_xe - cv->c_xs + 1) / 2;
3233 if (cv->c_xoff + n > cv->c_xs)
3234 n = cv->c_xs - cv->c_xoff;
3235 cv->c_xoff += n;
3236 RethinkViewportOffsets(cv);
3237 if (n > cv->c_layer->l_width)
3238 n = cv->c_layer->l_width;
3239 CV_CALL(cv,
3240 LayRedisplayLine(-1, -1, -1, 1);
3241 for (i = 0; i < flayer->l_height; i++)
3243 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3244 LayRedisplayLine(i, 0, n - 1, 1);
3246 if (cv == cv->c_display->d_forecv)
3247 LaySetCursor();
3250 else if (lx + cv->c_xoff > cv->c_xe)
3252 int i, n = lx + cv->c_xoff - cv->c_xe;
3253 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3254 n = (cv->c_xe - cv->c_xs + 1) / 2;
3255 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3256 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3257 cv->c_xoff -= n;
3258 RethinkViewportOffsets(cv);
3259 if (n > cv->c_layer->l_width)
3260 n = cv->c_layer->l_width;
3261 CV_CALL(cv,
3262 LayRedisplayLine(-1, -1, -1, 1);
3263 for (i = 0; i < flayer->l_height; i++)
3265 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3266 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3268 if (cv == cv->c_display->d_forecv)
3269 LaySetCursor();
3275 for (display = displays; display; display = display->d_next)
3277 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3278 continue;
3279 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3280 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3284 static void
3285 logflush_fn(ev, data)
3286 struct event *ev;
3287 char *data;
3289 struct win *p;
3290 char *buf;
3291 int n;
3293 if (!islogfile(NULL))
3294 return; /* no more logfiles */
3295 logfflush(NULL);
3296 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3297 if (n)
3299 SetTimeout(ev, n * 1000);
3300 evenq(ev); /* re-enqueue ourself */
3302 if (!logtstamp_on)
3303 return;
3304 /* write fancy time-stamp */
3305 for (p = windows; p; p = p->w_next)
3307 if (!p->w_log)
3308 continue;
3309 p->w_logsilence += n;
3310 if (p->w_logsilence < logtstamp_after)
3311 continue;
3312 if (p->w_logsilence - n >= logtstamp_after)
3313 continue;
3314 buf = MakeWinMsg(logtstamp_string, p, '%');
3315 logfwrite(p->w_log, buf, strlen(buf));
3320 * Interprets ^?, ^@ and other ^-control-char notation.
3321 * Interprets \ddd octal notation
3323 * The result is placed in *cp, p is advanced behind the parsed expression and
3324 * returned.
3326 static char *
3327 ParseChar(p, cp)
3328 char *p, *cp;
3330 if (*p == 0)
3331 return 0;
3332 if (*p == '^' && p[1])
3334 if (*++p == '?')
3335 *cp = '\177';
3336 else if (*p >= '@')
3337 *cp = Ctrl(*p);
3338 else
3339 return 0;
3340 ++p;
3342 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3344 *cp = 0;
3346 *cp = *cp * 8 + *p - '0';
3347 while (*++p <= '7' && *p >= '0');
3349 else
3350 *cp = *p++;
3351 return p;
3354 static int
3355 ParseEscape(p)
3356 char *p;
3358 unsigned char buf[2];
3360 if (*p == 0)
3361 SetEscape((struct acluser *)0, -1, -1);
3362 else
3364 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3365 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3366 return -1;
3367 SetEscape((struct acluser *)0, buf[0], buf[1]);
3369 return 0;